diff options
Diffstat (limited to 'compiler')
284 files changed, 4685 insertions, 4163 deletions
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 1442747fe1e..12dd1542d79 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1,4 +1,5 @@ #![cfg_attr(feature = "nightly", feature(step_trait, rustc_attrs, min_specialization))] +#![cfg_attr(all(not(bootstrap), feature = "nightly"), allow(internal_features))] use std::fmt; #[cfg(feature = "nightly")] diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index ba47ebd68cb..f4900ece1cc 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -23,6 +23,7 @@ #![deny(unsafe_op_in_unsafe_fn)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #![allow(clippy::mut_from_ref)] // Arena allocators are one of the places where this pattern is fine. use smallvec::SmallVec; diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index dd4321bea1b..f2e90fd8eed 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -14,7 +14,7 @@ //! - [`Generics`], [`GenericParam`], [`WhereClause`]: Metadata associated with generic parameters. //! - [`EnumDef`] and [`Variant`]: Enum declaration. //! - [`MetaItemLit`] and [`LitKind`]: Literal expressions. -//! - [`MacroDef`], [`MacStmtStyle`], [`MacCall`], [`MacDelimiter`]: Macro definition and invocation. +//! - [`MacroDef`], [`MacStmtStyle`], [`MacCall`]: Macro definition and invocation. //! - [`Attribute`]: Metadata associated with item. //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators. @@ -1462,7 +1462,8 @@ pub enum ExprKind { /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field. Field(P<Expr>, Ident), /// An indexing operation (e.g., `foo[2]`). - Index(P<Expr>, P<Expr>), + /// The span represents the span of the `[2]`, including brackets. + Index(P<Expr>, P<Expr>, Span), /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assignment). Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits), /// An underscore, used in destructuring assignment to ignore a value. @@ -1693,7 +1694,7 @@ where #[derive(Clone, Encodable, Decodable, Debug)] pub struct DelimArgs { pub dspan: DelimSpan, - pub delim: MacDelimiter, + pub delim: Delimiter, // Note: `Delimiter::Invisible` never occurs pub tokens: TokenStream, } @@ -1701,7 +1702,7 @@ impl DelimArgs { /// Whether a macro with these arguments needs a semicolon /// when used as a standalone item or statement. pub fn need_semicolon(&self) -> bool { - !matches!(self, DelimArgs { delim: MacDelimiter::Brace, .. }) + !matches!(self, DelimArgs { delim: Delimiter::Brace, .. }) } } @@ -1717,32 +1718,6 @@ where } } -#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, HashStable_Generic)] -pub enum MacDelimiter { - Parenthesis, - Bracket, - Brace, -} - -impl MacDelimiter { - pub fn to_token(self) -> Delimiter { - match self { - MacDelimiter::Parenthesis => Delimiter::Parenthesis, - MacDelimiter::Bracket => Delimiter::Bracket, - MacDelimiter::Brace => Delimiter::Brace, - } - } - - pub fn from_token(delim: Delimiter) -> Option<MacDelimiter> { - match delim { - Delimiter::Parenthesis => Some(MacDelimiter::Parenthesis), - Delimiter::Bracket => Some(MacDelimiter::Bracket), - Delimiter::Brace => Some(MacDelimiter::Brace), - Delimiter::Invisible => None, - } - } -} - /// Represents a macro definition. #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MacroDef { diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 8b9bb1df5dc..19a2b3017bc 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -2,7 +2,7 @@ use crate::ast::{AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute}; use crate::ast::{DelimArgs, Expr, ExprKind, LitKind, MetaItemLit}; -use crate::ast::{MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem, NormalAttr}; +use crate::ast::{MetaItem, MetaItemKind, NestedMetaItem, NormalAttr}; use crate::ast::{Path, PathSegment, DUMMY_NODE_ID}; use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter, Token}; @@ -196,7 +196,7 @@ impl AttrItem { fn meta_item_list(&self) -> Option<ThinVec<NestedMetaItem>> { match &self.args { - AttrArgs::Delimited(args) if args.delim == MacDelimiter::Parenthesis => { + AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => { MetaItemKind::list_from_tokens(args.tokens.clone()) } AttrArgs::Delimited(_) | AttrArgs::Eq(..) | AttrArgs::Empty => None, @@ -402,11 +402,9 @@ impl MetaItemKind { fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> { match args { AttrArgs::Empty => Some(MetaItemKind::Word), - AttrArgs::Delimited(DelimArgs { - dspan: _, - delim: MacDelimiter::Parenthesis, - tokens, - }) => MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List), + AttrArgs::Delimited(DelimArgs { dspan: _, delim: Delimiter::Parenthesis, tokens }) => { + MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List) + } AttrArgs::Delimited(..) => None, AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind { ExprKind::Lit(token_lit) => { @@ -578,7 +576,7 @@ pub fn mk_attr_nested_word( let path = Path::from_ident(outer_ident); let attr_args = AttrArgs::Delimited(DelimArgs { dspan: DelimSpan::from_single(span), - delim: MacDelimiter::Parenthesis, + delim: Delimiter::Parenthesis, tokens: inner_tokens, }); mk_attr(g, style, path, attr_args, span) diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index e87f6e820a1..f825b10f489 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -33,29 +33,41 @@ pub enum AllocatorTy { pub struct AllocatorMethod { pub name: Symbol, - pub inputs: &'static [AllocatorTy], + pub inputs: &'static [AllocatorMethodInput], pub output: AllocatorTy, } +pub struct AllocatorMethodInput { + pub name: &'static str, + pub ty: AllocatorTy, +} + pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ AllocatorMethod { name: sym::alloc, - inputs: &[AllocatorTy::Layout], + inputs: &[AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }], output: AllocatorTy::ResultPtr, }, AllocatorMethod { name: sym::dealloc, - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout], + inputs: &[ + AllocatorMethodInput { name: "ptr", ty: AllocatorTy::Ptr }, + AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }, + ], output: AllocatorTy::Unit, }, AllocatorMethod { name: sym::realloc, - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Usize], + inputs: &[ + AllocatorMethodInput { name: "ptr", ty: AllocatorTy::Ptr }, + AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }, + AllocatorMethodInput { name: "new_size", ty: AllocatorTy::Usize }, + ], output: AllocatorTy::ResultPtr, }, AllocatorMethod { name: sym::alloc_zeroed, - inputs: &[AllocatorTy::Layout], + inputs: &[AllocatorMethodInput { name: "layout", ty: AllocatorTy::Layout }], output: AllocatorTy::ResultPtr, }, ]; diff --git a/compiler/rustc_ast/src/format.rs b/compiler/rustc_ast/src/format.rs index 699946f307b..805596ff00a 100644 --- a/compiler/rustc_ast/src/format.rs +++ b/compiler/rustc_ast/src/format.rs @@ -67,12 +67,6 @@ pub struct FormatArguments { names: FxHashMap<Symbol, usize>, } -// FIXME: Rustdoc has trouble proving Send/Sync for this. See #106930. -#[cfg(parallel_compiler)] -unsafe impl Sync for FormatArguments {} -#[cfg(parallel_compiler)] -unsafe impl Send for FormatArguments {} - impl FormatArguments { pub fn new() -> Self { Self { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 84b56efd325..bae3979fbf9 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1400,7 +1400,7 @@ pub fn noop_visit_expr<T: MutVisitor>( fn_decl, body, fn_decl_span, - fn_arg_span: _, + fn_arg_span, }) => { vis.visit_closure_binder(binder); visit_constness(constness, vis); @@ -1408,6 +1408,7 @@ pub fn noop_visit_expr<T: MutVisitor>( vis.visit_fn_decl(fn_decl); vis.visit_expr(body); vis.visit_span(fn_decl_span); + vis.visit_span(fn_arg_span); } ExprKind::Block(blk, label) => { vis.visit_block(blk); @@ -1420,9 +1421,10 @@ pub fn noop_visit_expr<T: MutVisitor>( vis.visit_expr(expr); vis.visit_span(await_kw_span); } - ExprKind::Assign(el, er, _) => { + ExprKind::Assign(el, er, span) => { vis.visit_expr(el); vis.visit_expr(er); + vis.visit_span(span); } ExprKind::AssignOp(_op, el, er) => { vis.visit_expr(el); @@ -1432,9 +1434,10 @@ pub fn noop_visit_expr<T: MutVisitor>( vis.visit_expr(el); vis.visit_ident(ident); } - ExprKind::Index(el, er) => { + ExprKind::Index(el, er, brackets_span) => { vis.visit_expr(el); vis.visit_expr(er); + vis.visit_span(brackets_span); } ExprKind::Range(e1, e2, _lim) => { visit_opt(e1, |e1| vis.visit_expr(e1)); diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 4c920e84f86..f4ad0efa423 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -41,8 +41,6 @@ pub enum BinOpToken { /// Describes how a sequence of token trees is delimited. /// Cannot use `proc_macro::Delimiter` directly because this /// structure should implement some additional traits. -/// The `None` variant is also renamed to `Invisible` to be -/// less confusing and better convey the semantics. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Encodable, Decodable, Hash, HashStable_Generic)] pub enum Delimiter { diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 096077e09bf..d3e43e20235 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -390,7 +390,7 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { | ast::ExprKind::Cast(x, _) | ast::ExprKind::Type(x, _) | ast::ExprKind::Field(x, _) - | ast::ExprKind::Index(x, _) => { + | ast::ExprKind::Index(x, _, _) => { // &X { y: 1 }, X { y: 1 }.y contains_exterior_struct_lit(x) } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index aed24e11c4e..6d474de2d15 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -885,7 +885,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(subexpression); visitor.visit_ident(*ident); } - ExprKind::Index(main_expression, index_expression) => { + ExprKind::Index(main_expression, index_expression, _) => { visitor.visit_expr(main_expression); visitor.visit_expr(index_expression) } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 0954cf03da9..42d0998d162 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -240,8 +240,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Field(el, ident) => { hir::ExprKind::Field(self.lower_expr(el), self.lower_ident(*ident)) } - ExprKind::Index(el, er) => { - hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er)) + ExprKind::Index(el, er, brackets_span) => { + hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er), *brackets_span) } ExprKind::Range(Some(e1), Some(e2), RangeLimits::Closed) => { self.lower_expr_range_closed(e.span, e1, e2) @@ -657,14 +657,14 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to - /// `inner_hir_id` in case the `closure_track_caller` feature is enabled. + /// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled. pub(super) fn maybe_forward_track_caller( &mut self, span: Span, outer_hir_id: hir::HirId, inner_hir_id: hir::HirId, ) { - if self.tcx.features().closure_track_caller + if self.tcx.features().async_fn_track_caller && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id) && attrs.into_iter().any(|attr| attr.has_name(sym::track_caller)) { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 5a0474dbc01..df73c721ade 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -56,6 +56,11 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { owner: NodeId, f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>, ) { + let allow_gen_future = Some(if self.tcx.features().async_fn_track_caller { + [sym::gen_future, sym::closure_track_caller][..].into() + } else { + [sym::gen_future][..].into() + }); let mut lctx = LoweringContext { // Pseudo-globals. tcx: self.tcx, @@ -83,7 +88,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { impl_trait_defs: Vec::new(), impl_trait_bounds: Vec::new(), allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()), - allow_gen_future: Some([sym::gen_future, sym::closure_track_caller][..].into()), + allow_gen_future, generics_def_id_map: Default::default(), }; lctx.with_hir_id_owner(owner, |lctx| f(lctx)); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index ac750690046..d29e9f9b3f6 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -512,11 +512,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.resolver.node_id_to_def_id.get(&node).map(|local_def_id| *local_def_id) } - fn orig_local_def_id(&self, node: NodeId) -> LocalDefId { - self.orig_opt_local_def_id(node) - .unwrap_or_else(|| panic!("no entry for node id: `{node:?}`")) - } - /// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name /// resolver (if any), after applying any remapping from `get_remapped_def_id`. /// @@ -1521,209 +1516,86 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // frequently opened issues show. let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None); - let opaque_ty_def_id = self.create_def( - self.current_hir_id_owner.def_id, - opaque_ty_node_id, - DefPathData::ImplTrait, - opaque_ty_span, - ); - debug!(?opaque_ty_def_id); - - // If this came from a TAIT (as opposed to a function that returns an RPIT), we only want - // to capture the lifetimes that appear in the bounds. So visit the bounds to find out - // exactly which ones those are. - let lifetimes_to_remap = match origin { + let captured_lifetimes_to_duplicate = match origin { hir::OpaqueTyOrigin::TyAlias { .. } => { - // in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't keep all the lifetime parameters + // in a TAIT like `type Foo<'a> = impl Foo<'a>`, we don't duplicate any + // lifetimes, since we don't have the issue that any are late-bound. Vec::new() } - hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..) => { - // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` example, - // we only keep the lifetimes that appear in the `impl Debug` itself: + hir::OpaqueTyOrigin::FnReturn(..) => { + // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` + // example, we only need to duplicate lifetimes that appear in the + // bounds, since those are the only ones that are captured by the opaque. lifetime_collector::lifetimes_in_bounds(&self.resolver, bounds) } + hir::OpaqueTyOrigin::AsyncFn(..) => { + unreachable!("should be using `lower_async_fn_ret_ty`") + } }; - debug!(?lifetimes_to_remap); - - let mut new_remapping = FxHashMap::default(); - - // Contains the new lifetime definitions created for the TAIT (if any). - // If this opaque type is only capturing a subset of the lifetimes (those that appear in - // bounds), then create the new lifetime parameters required and create a mapping from the - // old `'a` (on the function) to the new `'a` (on the opaque type). - let collected_lifetimes = - self.create_lifetime_defs(opaque_ty_def_id, &lifetimes_to_remap, &mut new_remapping); - debug!(?collected_lifetimes); - debug!(?new_remapping); - - // This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type - // TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`. - let collected_lifetime_mapping: Vec<_> = collected_lifetimes - .iter() - .map(|(node_id, lifetime)| { - let id = self.next_node_id(); - let lifetime = self.new_named_lifetime(lifetime.id, id, lifetime.ident); - let def_id = self.local_def_id(*node_id); - (lifetime, def_id) - }) - .collect(); - debug!(?collected_lifetime_mapping); - - self.with_hir_id_owner(opaque_ty_node_id, |lctx| { - // Install the remapping from old to new (if any): - lctx.with_remapping(new_remapping, |lctx| { - // This creates HIR lifetime definitions as `hir::GenericParam`, in the given - // example `type TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection - // containing `&['x]`. - let lifetime_defs = lctx.arena.alloc_from_iter(collected_lifetimes.iter().map( - |&(new_node_id, lifetime)| { - let hir_id = lctx.lower_node_id(new_node_id); - debug_assert_ne!(lctx.opt_local_def_id(new_node_id), None); - - let (name, kind) = if lifetime.ident.name == kw::UnderscoreLifetime { - (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided) - } else { - ( - hir::ParamName::Plain(lifetime.ident), - hir::LifetimeParamKind::Explicit, - ) - }; + debug!(?captured_lifetimes_to_duplicate); - hir::GenericParam { - hir_id, - def_id: lctx.local_def_id(new_node_id), - name, - span: lifetime.ident.span, - pure_wrt_drop: false, - kind: hir::GenericParamKind::Lifetime { kind }, - colon_span: None, - source: hir::GenericParamSource::Generics, - } - }, - )); - debug!(?lifetime_defs); - - // Then when we lower the param bounds, references to 'a are remapped to 'a1, so we - // get back Debug + 'a1, which is suitable for use on the TAIT. - let hir_bounds = lctx.lower_param_bounds(bounds, itctx); - debug!(?hir_bounds); - - let lifetime_mapping = if in_trait { - Some( - &*self.arena.alloc_from_iter( - collected_lifetime_mapping - .iter() - .map(|(lifetime, def_id)| (**lifetime, *def_id)), - ), - ) - } else { - None - }; - - let opaque_ty_item = hir::OpaqueTy { - generics: self.arena.alloc(hir::Generics { - params: lifetime_defs, - predicates: &[], - has_where_clause_predicates: false, - where_clause_span: lctx.lower_span(span), - span: lctx.lower_span(span), - }), - bounds: hir_bounds, - origin, - lifetime_mapping, - in_trait, - }; - debug!(?opaque_ty_item); - - lctx.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span) - }) - }); - - // `impl Trait` now just becomes `Foo<'a, 'b, ..>`. - hir::TyKind::OpaqueDef( - hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } }, - self.arena.alloc_from_iter( - collected_lifetime_mapping - .iter() - .map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)), - ), + self.lower_opaque_inner( + opaque_ty_node_id, + origin, in_trait, + captured_lifetimes_to_duplicate, + span, + opaque_ty_span, + |this| this.lower_param_bounds(bounds, itctx), ) } - /// Registers a new opaque type with the proper `NodeId`s and - /// returns the lowered node-ID for the opaque type. - fn generate_opaque_type( + fn lower_opaque_inner( &mut self, - opaque_ty_id: LocalDefId, - opaque_ty_item: hir::OpaqueTy<'hir>, + opaque_ty_node_id: NodeId, + origin: hir::OpaqueTyOrigin, + in_trait: bool, + captured_lifetimes_to_duplicate: Vec<Lifetime>, span: Span, opaque_ty_span: Span, - ) -> hir::OwnerNode<'hir> { - let opaque_ty_item_kind = hir::ItemKind::OpaqueTy(self.arena.alloc(opaque_ty_item)); - // Generate an `type Foo = impl Trait;` declaration. - trace!("registering opaque type with id {:#?}", opaque_ty_id); - let opaque_ty_item = hir::Item { - owner_id: hir::OwnerId { def_id: opaque_ty_id }, - ident: Ident::empty(), - kind: opaque_ty_item_kind, - vis_span: self.lower_span(span.shrink_to_lo()), - span: self.lower_span(opaque_ty_span), - }; - hir::OwnerNode::Item(self.arena.alloc(opaque_ty_item)) - } - - /// Given a `parent_def_id`, a list of `lifetimes_in_bounds` and a `remapping` hash to be - /// filled, this function creates new definitions for `Param` and `Fresh` lifetimes, inserts the - /// new definition, adds it to the remapping with the definition of the given lifetime and - /// returns a list of lifetimes to be lowered afterwards. - fn create_lifetime_defs( - &mut self, - parent_def_id: LocalDefId, - lifetimes_in_bounds: &[Lifetime], - remapping: &mut FxHashMap<LocalDefId, LocalDefId>, - ) -> Vec<(NodeId, Lifetime)> { - let mut result = Vec::new(); + lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>], + ) -> hir::TyKind<'hir> { + let opaque_ty_def_id = self.create_def( + self.current_hir_id_owner.def_id, + opaque_ty_node_id, + DefPathData::ImplTrait, + opaque_ty_span, + ); + debug!(?opaque_ty_def_id); - for lifetime in lifetimes_in_bounds { + // Map from captured (old) lifetime to synthetic (new) lifetime. + // Used to resolve lifetimes in the bounds of the opaque. + let mut captured_to_synthesized_mapping = FxHashMap::default(); + // List of (early-bound) synthetic lifetimes that are owned by the opaque. + // This is used to create the `hir::Generics` owned by the opaque. + let mut synthesized_lifetime_definitions = vec![]; + // Pairs of lifetime arg (that resolves to the captured lifetime) + // and the def-id of the (early-bound) synthetic lifetime definition. + // This is used both to create generics for the `TyKind::OpaqueDef` that + // we return, and also as a captured lifetime mapping for RPITITs. + let mut synthesized_lifetime_args = vec![]; + + for lifetime in captured_lifetimes_to_duplicate { let res = self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error); - debug!(?res); - - match res { - LifetimeRes::Param { param: old_def_id, binder: _ } => { - if remapping.get(&old_def_id).is_none() { - let node_id = self.next_node_id(); - - let new_def_id = self.create_def( - parent_def_id, - node_id, - DefPathData::LifetimeNs(lifetime.ident.name), - lifetime.ident.span, - ); - remapping.insert(old_def_id, new_def_id); - - result.push((node_id, *lifetime)); - } - } + let old_def_id = match res { + LifetimeRes::Param { param: old_def_id, binder: _ } => old_def_id, LifetimeRes::Fresh { param, binder: _ } => { debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime); - if let Some(old_def_id) = self.orig_opt_local_def_id(param) && remapping.get(&old_def_id).is_none() { - let node_id = self.next_node_id(); - - let new_def_id = self.create_def( - parent_def_id, - node_id, - DefPathData::LifetimeNs(kw::UnderscoreLifetime), - lifetime.ident.span, - ); - remapping.insert(old_def_id, new_def_id); - - result.push((node_id, *lifetime)); + if let Some(old_def_id) = self.orig_opt_local_def_id(param) { + old_def_id + } else { + self.tcx + .sess + .delay_span_bug(lifetime.ident.span, "no def-id for fresh lifetime"); + continue; } } - LifetimeRes::Static | LifetimeRes::Error => {} + // Opaques do not capture `'static` + LifetimeRes::Static | LifetimeRes::Error => { + continue; + } res => { let bug_msg = format!( @@ -1732,10 +1604,113 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ); span_bug!(lifetime.ident.span, "{}", bug_msg); } + }; + + if captured_to_synthesized_mapping.get(&old_def_id).is_none() { + // Create a new lifetime parameter local to the opaque. + let duplicated_lifetime_node_id = self.next_node_id(); + let duplicated_lifetime_def_id = self.create_def( + opaque_ty_def_id, + duplicated_lifetime_node_id, + DefPathData::LifetimeNs(lifetime.ident.name), + lifetime.ident.span, + ); + captured_to_synthesized_mapping.insert(old_def_id, duplicated_lifetime_def_id); + // FIXME: Instead of doing this, we could move this whole loop + // into the `with_hir_id_owner`, then just directly construct + // the `hir::GenericParam` here. + synthesized_lifetime_definitions.push(( + duplicated_lifetime_node_id, + duplicated_lifetime_def_id, + lifetime.ident, + )); + + // Now make an arg that we can use for the substs of the opaque tykind. + let id = self.next_node_id(); + let lifetime_arg = self.new_named_lifetime_with_res(id, lifetime.ident, res); + let duplicated_lifetime_def_id = self.local_def_id(duplicated_lifetime_node_id); + synthesized_lifetime_args.push((lifetime_arg, duplicated_lifetime_def_id)) } } - result + self.with_hir_id_owner(opaque_ty_node_id, |this| { + // Install the remapping from old to new (if any). This makes sure that + // any lifetimes that would have resolved to the def-id of captured + // lifetimes are remapped to the new *synthetic* lifetimes of the opaque. + let bounds = this + .with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this)); + + let generic_params = this.arena.alloc_from_iter( + synthesized_lifetime_definitions.iter().map(|&(new_node_id, new_def_id, ident)| { + let hir_id = this.lower_node_id(new_node_id); + let (name, kind) = if ident.name == kw::UnderscoreLifetime { + (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided) + } else { + (hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit) + }; + + hir::GenericParam { + hir_id, + def_id: new_def_id, + name, + span: ident.span, + pure_wrt_drop: false, + kind: hir::GenericParamKind::Lifetime { kind }, + colon_span: None, + source: hir::GenericParamSource::Generics, + } + }), + ); + debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params); + + let lifetime_mapping = if in_trait { + Some(&*self.arena.alloc_slice(&synthesized_lifetime_args)) + } else { + None + }; + + let opaque_ty_item = hir::OpaqueTy { + generics: this.arena.alloc(hir::Generics { + params: generic_params, + predicates: &[], + has_where_clause_predicates: false, + where_clause_span: this.lower_span(span), + span: this.lower_span(span), + }), + bounds, + origin, + lifetime_mapping, + in_trait, + }; + + // Generate an `type Foo = impl Trait;` declaration. + trace!("registering opaque type with id {:#?}", opaque_ty_def_id); + let opaque_ty_item = hir::Item { + owner_id: hir::OwnerId { def_id: opaque_ty_def_id }, + ident: Ident::empty(), + kind: hir::ItemKind::OpaqueTy(this.arena.alloc(opaque_ty_item)), + vis_span: this.lower_span(span.shrink_to_lo()), + span: this.lower_span(opaque_ty_span), + }; + + hir::OwnerNode::Item(this.arena.alloc(opaque_ty_item)) + }); + + let generic_args = self.arena.alloc_from_iter( + synthesized_lifetime_args + .iter() + .map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)), + ); + + // Create the `Foo<...>` reference itself. Note that the `type + // Foo = impl Trait` is, internally, created as a child of the + // async fn, so the *type parameters* are inherited. It's + // only the lifetime parameters that we must supply. + hir::TyKind::OpaqueDef( + hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } }, + generic_args, + in_trait, + ) } fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] { @@ -1813,9 +1788,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } + let fn_def_id = self.local_def_id(fn_node_id); self.lower_async_fn_ret_ty( &decl.output, - fn_node_id, + fn_def_id, ret_id, matches!(kind, FnDeclKind::Trait), ) @@ -1892,151 +1868,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_async_fn_ret_ty( &mut self, output: &FnRetTy, - fn_node_id: NodeId, + fn_def_id: LocalDefId, opaque_ty_node_id: NodeId, in_trait: bool, ) -> hir::FnRetTy<'hir> { - let span = output.span(); - + let span = self.lower_span(output.span()); let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None); - let fn_def_id = self.local_def_id(fn_node_id); - - let opaque_ty_def_id = - self.create_def(fn_def_id, opaque_ty_node_id, DefPathData::ImplTrait, opaque_ty_span); - - // When we create the opaque type for this async fn, it is going to have - // to capture all the lifetimes involved in the signature (including in the - // return type). This is done by introducing lifetime parameters for: - // - // - all the explicitly declared lifetimes from the impl and function itself; - // - all the elided lifetimes in the fn arguments; - // - all the elided lifetimes in the return type. - // - // So for example in this snippet: - // - // ```rust - // impl<'a> Foo<'a> { - // async fn bar<'b>(&self, x: &'b Vec<f64>, y: &str) -> &u32 { - // // ^ '0 ^ '1 ^ '2 - // // elided lifetimes used below - // } - // } - // ``` - // - // we would create an opaque type like: - // - // ``` - // type Bar<'a, 'b, '0, '1, '2> = impl Future<Output = &'2 u32>; - // ``` - // - // and we would then desugar `bar` to the equivalent of: - // - // ```rust - // impl<'a> Foo<'a> { - // fn bar<'b, '0, '1>(&'0 self, x: &'b Vec<f64>, y: &'1 str) -> Bar<'a, 'b, '0, '1, '_> - // } - // ``` - // - // Note that the final parameter to `Bar` is `'_`, not `'2` -- - // this is because the elided lifetimes from the return type - // should be figured out using the ordinary elision rules, and - // this desugaring achieves that. - - // Calculate all the lifetimes that should be captured - // by the opaque type. This should include all in-scope - // lifetime parameters, including those defined in-band. - - // Contains the new lifetime definitions created for the TAIT (if any) generated for the - // return type. - let mut collected_lifetimes = Vec::new(); - let mut new_remapping = FxHashMap::default(); - - let extra_lifetime_params = self.resolver.take_extra_lifetime_params(opaque_ty_node_id); - debug!(?extra_lifetime_params); - for (ident, outer_node_id, outer_res) in extra_lifetime_params { - let outer_def_id = self.orig_local_def_id(outer_node_id); - let inner_node_id = self.next_node_id(); - - // Add a definition for the in scope lifetime def. - let inner_def_id = self.create_def( - opaque_ty_def_id, - inner_node_id, - DefPathData::LifetimeNs(ident.name), - ident.span, - ); - new_remapping.insert(outer_def_id, inner_def_id); - - let inner_res = match outer_res { - // Input lifetime like `'a`: - LifetimeRes::Param { param, .. } => { - LifetimeRes::Param { param, binder: fn_node_id } - } - // Input lifetime like `'1`: - LifetimeRes::Fresh { param, .. } => { - LifetimeRes::Fresh { param, binder: fn_node_id } - } - LifetimeRes::Static | LifetimeRes::Error => continue, - res => { - panic!( - "Unexpected lifetime resolution {:?} for {:?} at {:?}", - res, ident, ident.span - ) - } - }; - - let lifetime = Lifetime { id: outer_node_id, ident }; - collected_lifetimes.push((inner_node_id, lifetime, Some(inner_res))); - } - debug!(?collected_lifetimes); - - // We only want to capture the lifetimes that appear in the bounds. So visit the bounds to - // find out exactly which ones those are. - // in fn return position, like the `fn test<'a>() -> impl Debug + 'a` example, - // we only keep the lifetimes that appear in the `impl Debug` itself: - let lifetimes_to_remap = lifetime_collector::lifetimes_in_ret_ty(&self.resolver, output); - debug!(?lifetimes_to_remap); - - // If this opaque type is only capturing a subset of the lifetimes (those that appear in - // bounds), then create the new lifetime parameters required and create a mapping from the - // old `'a` (on the function) to the new `'a` (on the opaque type). - collected_lifetimes.extend( - self.create_lifetime_defs(opaque_ty_def_id, &lifetimes_to_remap, &mut new_remapping) - .into_iter() - .map(|(new_node_id, lifetime)| (new_node_id, lifetime, None)), - ); - debug!(?collected_lifetimes); - debug!(?new_remapping); - - // This creates pairs of HIR lifetimes and def_ids. In the given example `type - // TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing the - // new lifetime of the RPIT 'x and the def_id of the lifetime 'x corresponding to - // `TestReturn`. - let collected_lifetime_mapping: Vec<_> = collected_lifetimes - .iter() - .map(|(node_id, lifetime, res)| { - let id = self.next_node_id(); - let res = res.unwrap_or( - self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error), - ); - let lifetime = self.new_named_lifetime_with_res(id, lifetime.ident, res); - let def_id = self.local_def_id(*node_id); - (lifetime, def_id) - }) + let captured_lifetimes: Vec<_> = self + .resolver + .take_extra_lifetime_params(opaque_ty_node_id) + .into_iter() + .map(|(ident, id, _)| Lifetime { id, ident }) .collect(); - debug!(?collected_lifetime_mapping); - self.with_hir_id_owner(opaque_ty_node_id, |this| { - // Install the remapping from old to new (if any): - this.with_remapping(new_remapping, |this| { - // We have to be careful to get elision right here. The - // idea is that we create a lifetime parameter for each - // lifetime in the return type. So, given a return type - // like `async fn foo(..) -> &[&u32]`, we lower to `impl - // Future<Output = &'1 [ &'2 u32 ]>`. - // - // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and - // hence the elision takes place at the fn site. + let opaque_ty_ref = self.lower_opaque_inner( + opaque_ty_node_id, + hir::OpaqueTyOrigin::AsyncFn(fn_def_id), + in_trait, + captured_lifetimes, + span, + opaque_ty_span, + |this| { let future_bound = this.lower_async_fn_output_type_to_future_bound( output, span, @@ -2052,96 +1905,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } }, ); - - let generic_params = this.arena.alloc_from_iter(collected_lifetimes.iter().map( - |&(new_node_id, lifetime, _)| { - let hir_id = this.lower_node_id(new_node_id); - debug_assert_ne!(this.opt_local_def_id(new_node_id), None); - - let (name, kind) = if lifetime.ident.name == kw::UnderscoreLifetime { - (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided) - } else { - ( - hir::ParamName::Plain(lifetime.ident), - hir::LifetimeParamKind::Explicit, - ) - }; - - hir::GenericParam { - hir_id, - def_id: this.local_def_id(new_node_id), - name, - span: lifetime.ident.span, - pure_wrt_drop: false, - kind: hir::GenericParamKind::Lifetime { kind }, - colon_span: None, - source: hir::GenericParamSource::Generics, - } - }, - )); - debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params); - - let lifetime_mapping = if in_trait { - Some( - &*self.arena.alloc_from_iter( - collected_lifetime_mapping - .iter() - .map(|(lifetime, def_id)| (**lifetime, *def_id)), - ), - ) - } else { - None - }; - - let opaque_ty_item = hir::OpaqueTy { - generics: this.arena.alloc(hir::Generics { - params: generic_params, - predicates: &[], - has_where_clause_predicates: false, - where_clause_span: this.lower_span(span), - span: this.lower_span(span), - }), - bounds: arena_vec![this; future_bound], - origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id), - lifetime_mapping, - in_trait, - }; - - trace!("exist ty from async fn def id: {:#?}", opaque_ty_def_id); - this.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span) - }) - }); - - // As documented above, we need to create the lifetime - // arguments to our opaque type. Continuing with our example, - // we're creating the type arguments for the return type: - // - // ``` - // Bar<'a, 'b, '0, '1, '_> - // ``` - // - // For the "input" lifetime parameters, we wish to create - // references to the parameters themselves, including the - // "implicit" ones created from parameter types (`'a`, `'b`, - // '`0`, `'1`). - // - // For the "output" lifetime parameters, we just want to - // generate `'_`. - let generic_args = self.arena.alloc_from_iter( - collected_lifetime_mapping - .iter() - .map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)), + arena_vec![this; future_bound] + }, ); - // Create the `Foo<...>` reference itself. Note that the `type - // Foo = impl Trait` is, internally, created as a child of the - // async fn, so the *type parameters* are inherited. It's - // only the lifetime parameters that we must supply. - let opaque_ty_ref = hir::TyKind::OpaqueDef( - hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } }, - generic_args, - in_trait, - ); let opaque_ty = self.ty(opaque_ty_span, opaque_ty_ref); hir::FnRetTy::Return(self.arena.alloc(opaque_ty)) } diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs index 3989fc48619..0e0bdf17389 100644 --- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs +++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs @@ -1,6 +1,6 @@ use super::ResolverAstLoweringExt; use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor}; -use rustc_ast::{FnRetTy, GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind}; +use rustc_ast::{GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind}; use rustc_hir::def::LifetimeRes; use rustc_middle::span_bug; use rustc_middle::ty::ResolverAstLowering; @@ -94,12 +94,6 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> { } } -pub fn lifetimes_in_ret_ty(resolver: &ResolverAstLowering, ret_ty: &FnRetTy) -> Vec<Lifetime> { - let mut visitor = LifetimeCollectVisitor::new(resolver); - visitor.visit_fn_ret_ty(ret_ty); - visitor.collected_lifetimes -} - pub fn lifetimes_in_bounds( resolver: &ResolverAstLowering, bounds: &GenericBounds, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 59239b49edd..068b255e9f2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -476,7 +476,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere Some(MacHeader::Path(&item.path)), false, None, - delim.to_token(), + *delim, tokens, true, span, @@ -640,7 +640,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere Some(MacHeader::Keyword(kw)), has_bang, Some(*ident), - macro_def.body.delim.to_token(), + macro_def.body.delim, ¯o_def.body.tokens.clone(), true, sp, @@ -1240,7 +1240,7 @@ impl<'a> State<'a> { Some(MacHeader::Path(&m.path)), true, None, - m.args.delim.to_token(), + m.args.delim, &m.args.tokens.clone(), true, m.span(), diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 8767bbcb210..39741a03930 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -477,7 +477,7 @@ impl<'a> State<'a> { self.word("."); self.print_ident(*ident); } - ast::ExprKind::Index(expr, index) => { + ast::ExprKind::Index(expr, index, _) => { self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); self.word("["); self.print_expr(index); diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 6cce3a56f08..6ed5fc77d75 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -28,7 +28,7 @@ pub fn rust_version_symbol() -> Symbol { } pub fn is_builtin_attr(attr: &Attribute) -> bool { - attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some() + attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) } enum AttrError { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 03b90f4ab18..fe4a45b3898 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -751,9 +751,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) .must_apply_modulo_regions() { + let msg = if let ty::Adt(def, _) = ty.kind() + && [ + tcx.get_diagnostic_item(sym::Arc), + tcx.get_diagnostic_item(sym::Rc), + ].contains(&Some(def.did())) + { + "clone the value to increment its reference count" + } else { + "consider cloning the value if the performance cost is acceptable" + }; err.span_suggestion_verbose( span.shrink_to_hi(), - "consider cloning the value if the performance cost is acceptable", + msg, suggestion, Applicability::MachineApplicable, ); @@ -2133,13 +2143,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.current -= 1; } fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) { - if self.span == expr.span { + if self.span == expr.span.source_callsite() { self.found = self.current; } walk_expr(self, expr); } } let source_info = self.body.source_info(location); + let proper_span = proper_span.source_callsite(); if let Some(scope) = self.body.source_scopes.get(source_info.scope) && let ClearCrossCrate::Set(scope_data) = &scope.local_data && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index c92f32071f4..c66a2447356 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -79,7 +79,7 @@ impl<'tcx> BorrowExplanation<'tcx> { | hir::ExprKind::Unary(hir::UnOp::Deref, inner) | hir::ExprKind::Field(inner, _) | hir::ExprKind::MethodCall(_, inner, _, _) - | hir::ExprKind::Index(inner, _) = &expr.kind + | hir::ExprKind::Index(inner, _, _) = &expr.kind { expr = inner; } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 3c32121a51a..d62541daf07 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -567,7 +567,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } }; if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind - && let hir::ExprKind::Index(val, index) = place.kind + && let hir::ExprKind::Index(val, index, _) = place.kind && (expr.span == self.assign_span || place.span == self.assign_span) { // val[index] = rv; @@ -620,7 +620,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ); self.suggested = true; } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind - && let hir::ExprKind::Index(val, index) = receiver.kind + && let hir::ExprKind::Index(val, index, _) = receiver.kind && expr.span == self.assign_span { // val[index].path(args..); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 9cad8fa5318..be6eb2d1d12 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -886,6 +886,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { .universal_regions() .defining_ty .upvar_tys() + .iter() .position(|ty| self.any_param_predicate_mentions(&predicates, ty, region)) { let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index 98418e2372f..8832d345df2 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -43,7 +43,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr: RegionVid, ) -> Option<usize> { let upvar_index = - self.universal_regions().defining_ty.upvar_tys().position(|upvar_ty| { + self.universal_regions().defining_ty.upvar_tys().iter().position(|upvar_ty| { debug!("get_upvar_index_for_region: upvar_ty={upvar_ty:?}"); tcx.any_free_region_meets(&upvar_ty, |r| { let r = r.as_var(); @@ -52,7 +52,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { }) })?; - let upvar_ty = self.universal_regions().defining_ty.upvar_tys().nth(upvar_index); + let upvar_ty = self.universal_regions().defining_ty.upvar_tys().get(upvar_index); debug!( "get_upvar_index_for_region: found {fr:?} in upvar {upvar_index} which has type {upvar_ty:?}", diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 317af8ae1f6..efe525c224d 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -11,6 +11,7 @@ #![feature(trusted_step)] #![feature(try_blocks)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate rustc_middle; diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 30dfb2d919a..b8cd94e5422 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -784,13 +784,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// is considered a *lower bound*. If possible, we will modify /// the constraint to set it equal to one of the option regions. /// If we make any changes, returns true, else false. + /// + /// This function only adds the member constraints to the region graph, + /// it does not check them. They are later checked in + /// `check_member_constraints` after the region graph has been computed. #[instrument(skip(self, member_constraint_index), level = "debug")] fn apply_member_constraint( &mut self, scc: ConstraintSccIndex, member_constraint_index: NllMemberConstraintIndex, choice_regions: &[ty::RegionVid], - ) -> bool { + ) { + // Lazily compute the reverse graph, we'll need it later. + self.compute_reverse_scc_graph(); + // Create a mutable vector of the options. We'll try to winnow // them down. let mut choice_regions: Vec<ty::RegionVid> = choice_regions.to_vec(); @@ -805,10 +812,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { *c_r = self.scc_representatives[scc]; } - // The 'member region' in a member constraint is part of the - // hidden type, which must be in the root universe. Therefore, - // it cannot have any placeholders in its value. - assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT); + // If the member region lives in a higher universe, we currently choose + // the most conservative option by leaving it unchanged. + if self.scc_universes[scc] != ty::UniverseIndex::ROOT { + return; + } debug_assert!( self.scc_values.placeholders_contained_in(scc).next().is_none(), "scc {:?} in a member constraint has placeholder value: {:?}", @@ -832,7 +840,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { // free region that must outlive the member region `R0` (`UB: // R0`). Therefore, we need only keep an option `O` if `UB: O` // for all UB. - self.compute_reverse_scc_graph(); let universal_region_relations = &self.universal_region_relations; for ub in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) { debug!(?ub); @@ -867,7 +874,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } }) else { debug!("no unique minimum choice"); - return false; + return; }; let min_choice_scc = self.constraint_sccs.scc(min_choice); @@ -878,10 +885,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { min_choice, member_constraint_index, }); - - true - } else { - false } } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 90bde88f792..68dddd65acb 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -185,6 +185,21 @@ impl<'tcx> RegionInferenceContext<'tcx> { { tcx.fold_regions(ty, |region, _| match *region { ty::ReVar(vid) => { + let scc = self.constraint_sccs.scc(vid); + + // Special handling of higher-ranked regions. + if self.scc_universes[scc] != ty::UniverseIndex::ROOT { + match self.scc_values.placeholders_contained_in(scc).enumerate().last() { + // If the region contains a single placeholder then they're equal. + Some((0, placeholder)) => { + return ty::Region::new_placeholder(tcx, placeholder); + } + + // Fallback: this will produce a cryptic error message. + _ => return region, + } + } + // Find something that we can name let upper_bound = self.approx_universal_upper_bound(vid); let upper_bound = &self.definitions[upper_bound]; diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index c19fbf20ca5..16f5e68a06f 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -90,11 +90,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ) { self.prove_predicate( ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Trait( - ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }, + ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Positive }, ))), locations, category, diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index eb02604b9d9..5702d39db32 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -161,8 +161,12 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { } } - // Runs dropck for locals whose liveness isn't relevant. This is - // necessary to eagerly detect unbound recursion during drop glue computation. + /// Runs dropck for locals whose liveness isn't relevant. This is + /// necessary to eagerly detect unbound recursion during drop glue computation. + /// + /// These are all the locals which do not potentially reference a region local + /// to this body. Locals which only reference free regions are always drop-live + /// and can therefore safely be dropped. fn dropck_boring_locals(&mut self, boring_locals: Vec<Local>) { for local in boring_locals { let local_ty = self.cx.body.local_decls[local].ty; diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index fd4a3ec1a5e..50d875dfae9 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -498,14 +498,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { /// Checks that the types internal to the `place` match up with /// what would be expected. + #[instrument(level = "debug", skip(self, location), ret)] fn sanitize_place( &mut self, place: &Place<'tcx>, location: Location, context: PlaceContext, ) -> PlaceTy<'tcx> { - debug!("sanitize_place: {:?}", place); - let mut place_ty = PlaceTy::from_ty(self.body().local_decls[place.local].ty); for elem in place.projection.iter() { @@ -608,7 +607,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } } - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self, location), ret, level = "debug")] fn sanitize_projection( &mut self, base: PlaceTy<'tcx>, @@ -617,7 +616,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { location: Location, context: PlaceContext, ) -> PlaceTy<'tcx> { - debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); let tcx = self.tcx(); let base_ty = base.ty; match pi { @@ -791,25 +789,20 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { (adt_def.variant(FIRST_VARIANT), args) } ty::Closure(_, args) => { - return match args - .as_closure() - .tupled_upvars_ty() - .tuple_fields() - .get(field.index()) - { + return match args.as_closure().upvar_tys().get(field.index()) { Some(&ty) => Ok(ty), None => Err(FieldAccessError::OutOfRange { - field_count: args.as_closure().upvar_tys().count(), + field_count: args.as_closure().upvar_tys().len(), }), }; } ty::Generator(_, args, _) => { // Only prefix fields (upvars and current state) are // accessible without a variant index. - return match args.as_generator().prefix_tys().nth(field.index()) { - Some(ty) => Ok(ty), + return match args.as_generator().prefix_tys().get(field.index()) { + Some(ty) => Ok(*ty), None => Err(FieldAccessError::OutOfRange { - field_count: args.as_generator().prefix_tys().count(), + field_count: args.as_generator().prefix_tys().len(), }), }; } @@ -1772,10 +1765,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } AggregateKind::Closure(_, args) => { - match args.as_closure().upvar_tys().nth(field_index.as_usize()) { - Some(ty) => Ok(ty), + match args.as_closure().upvar_tys().get(field_index.as_usize()) { + Some(ty) => Ok(*ty), None => Err(FieldAccessError::OutOfRange { - field_count: args.as_closure().upvar_tys().count(), + field_count: args.as_closure().upvar_tys().len(), }), } } @@ -1783,10 +1776,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // It doesn't make sense to look at a field beyond the prefix; // these require a variant index, and are not initialized in // aggregate rvalues. - match args.as_generator().prefix_tys().nth(field_index.as_usize()) { - Some(ty) => Ok(ty), + match args.as_generator().prefix_tys().get(field_index.as_usize()) { + Some(ty) => Ok(*ty), None => Err(FieldAccessError::OutOfRange { - field_count: args.as_generator().prefix_tys().count(), + field_count: args.as_generator().prefix_tys().len(), }), } } diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index a751a9732f0..56945f43fcd 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -12,7 +12,6 @@ //! The code in this file doesn't *do anything* with those results; it //! just returns them for other code to use. -use either::Either; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Diagnostic; use rustc_hir as hir; @@ -115,14 +114,12 @@ impl<'tcx> DefiningTy<'tcx> { /// not a closure or generator, there are no upvars, and hence it /// will be an empty list. The order of types in this list will /// match up with the upvar order in the HIR, typesystem, and MIR. - pub fn upvar_tys(self) -> impl Iterator<Item = Ty<'tcx>> + 'tcx { + pub fn upvar_tys(self) -> &'tcx ty::List<Ty<'tcx>> { match self { - DefiningTy::Closure(_, args) => Either::Left(args.as_closure().upvar_tys()), - DefiningTy::Generator(_, args, _) => { - Either::Right(Either::Left(args.as_generator().upvar_tys())) - } + DefiningTy::Closure(_, args) => args.as_closure().upvar_tys(), + DefiningTy::Generator(_, args, _) => args.as_generator().upvar_tys(), DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => { - Either::Right(Either::Right(iter::empty())) + ty::List::empty() } } } diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 79fae7f92f1..8d8db4c13fa 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -227,3 +227,5 @@ builtin_macros_unexpected_lit = expected path to a trait, found literal .label = not a trait .str_lit = try using `#[derive({$sym})]` .other = for example, write `#[derive(Debug)]` for `Debug` + +builtin_macros_unnameable_test_items = cannot test inner items diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index ab4ea9c8c20..3e90ae6907f 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -4,8 +4,9 @@ use crate::edition_panic::use_panic_2021; use crate::errors; use rustc_ast::ptr::P; use rustc_ast::token; +use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; -use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, MacDelimiter, Path, PathSegment, UnOp}; +use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp}; use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExtCtxt, MacEager, MacResult}; @@ -58,7 +59,7 @@ pub fn expand_assert<'cx>( path: panic_path(), args: P(DelimArgs { dspan: DelimSpan::from_single(call_site_span), - delim: MacDelimiter::Parenthesis, + delim: Delimiter::Parenthesis, tokens, }), })), diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 902c1e40c75..bda473120ed 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -1,9 +1,10 @@ use rustc_ast::{ ptr::P, token, + token::Delimiter, tokenstream::{DelimSpan, TokenStream, TokenTree}, - BinOpKind, BorrowKind, DelimArgs, Expr, ExprKind, ItemKind, MacCall, MacDelimiter, MethodCall, - Mutability, Path, PathSegment, Stmt, StructRest, UnOp, UseTree, UseTreeKind, DUMMY_NODE_ID, + BinOpKind, BorrowKind, DelimArgs, Expr, ExprKind, ItemKind, MacCall, MethodCall, Mutability, + Path, PathSegment, Stmt, StructRest, UnOp, UseTree, UseTreeKind, DUMMY_NODE_ID, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -179,7 +180,7 @@ impl<'cx, 'a> Context<'cx, 'a> { path: panic_path, args: P(DelimArgs { dspan: DelimSpan::from_single(self.span), - delim: MacDelimiter::Parenthesis, + delim: Delimiter::Parenthesis, tokens: initial.into_iter().chain(captures).collect::<TokenStream>(), }), })), @@ -236,7 +237,7 @@ impl<'cx, 'a> Context<'cx, 'a> { ExprKind::If(local_expr, _, _) => { self.manage_cond_expr(local_expr); } - ExprKind::Index(prefix, suffix) => { + ExprKind::Index(prefix, suffix, _) => { self.manage_cond_expr(prefix); self.manage_cond_expr(suffix); } diff --git a/compiler/rustc_builtin_macros/src/edition_panic.rs b/compiler/rustc_builtin_macros/src/edition_panic.rs index ef0db23ff2f..1e1dadab480 100644 --- a/compiler/rustc_builtin_macros/src/edition_panic.rs +++ b/compiler/rustc_builtin_macros/src/edition_panic.rs @@ -1,4 +1,5 @@ use rustc_ast::ptr::P; +use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::*; use rustc_expand::base::*; @@ -60,7 +61,7 @@ fn expand<'cx>( }, args: P(DelimArgs { dspan: DelimSpan::from_single(sp), - delim: MacDelimiter::Parenthesis, + delim: Delimiter::Parenthesis, tokens: tts, }), })), diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 053f5730f6e..1bec00add55 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -2,7 +2,7 @@ use crate::util::check_builtin_macro_attribute; use crate::errors; use rustc_ast::expand::allocator::{ - global_fn_name, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS, + global_fn_name, AllocatorMethod, AllocatorMethodInput, AllocatorTy, ALLOCATOR_METHODS, }; use rustc_ast::ptr::P; use rustc_ast::{self as ast, AttrVec, Expr, FnHeader, FnSig, Generics, Param, StmtKind}; @@ -70,19 +70,13 @@ struct AllocFnFactory<'a, 'b> { impl AllocFnFactory<'_, '_> { fn allocator_fn(&self, method: &AllocatorMethod) -> Stmt { let mut abi_args = ThinVec::new(); - let mut i = 0; - let mut mk = || { - let name = Ident::from_str_and_span(&format!("arg{i}"), self.span); - i += 1; - name - }; - let args = method.inputs.iter().map(|ty| self.arg_ty(ty, &mut abi_args, &mut mk)).collect(); + let args = method.inputs.iter().map(|input| self.arg_ty(input, &mut abi_args)).collect(); let result = self.call_allocator(method.name, args); - let (output_ty, output_expr) = self.ret_ty(&method.output, result); + let output_ty = self.ret_ty(&method.output); let decl = self.cx.fn_decl(abi_args, ast::FnRetTy::Ty(output_ty)); let header = FnHeader { unsafety: Unsafe::Yes(self.span), ..FnHeader::default() }; let sig = FnSig { decl, header, span: self.span }; - let body = Some(self.cx.block_expr(output_expr)); + let body = Some(self.cx.block_expr(result)); let kind = ItemKind::Fn(Box::new(Fn { defaultness: ast::Defaultness::Final, sig, @@ -113,18 +107,19 @@ impl AllocFnFactory<'_, '_> { thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)] } - fn arg_ty( - &self, - ty: &AllocatorTy, - args: &mut ThinVec<Param>, - ident: &mut dyn FnMut() -> Ident, - ) -> P<Expr> { - match *ty { + fn arg_ty(&self, input: &AllocatorMethodInput, args: &mut ThinVec<Param>) -> P<Expr> { + match input.ty { AllocatorTy::Layout => { + // If an allocator method is ever introduced having multiple + // Layout arguments, these argument names need to be + // disambiguated somehow. Currently the generated code would + // fail to compile with "identifier is bound more than once in + // this parameter list". + let size = Ident::from_str_and_span("size", self.span); + let align = Ident::from_str_and_span("align", self.span); + let usize = self.cx.path_ident(self.span, Ident::new(sym::usize, self.span)); let ty_usize = self.cx.ty_path(usize); - let size = ident(); - let align = ident(); args.push(self.cx.param(self.span, size, ty_usize.clone())); args.push(self.cx.param(self.span, align, ty_usize)); @@ -138,14 +133,13 @@ impl AllocFnFactory<'_, '_> { } AllocatorTy::Ptr => { - let ident = ident(); + let ident = Ident::from_str_and_span(input.name, self.span); args.push(self.cx.param(self.span, ident, self.ptr_u8())); - let arg = self.cx.expr_ident(self.span, ident); - self.cx.expr_cast(self.span, arg, self.ptr_u8()) + self.cx.expr_ident(self.span, ident) } AllocatorTy::Usize => { - let ident = ident(); + let ident = Ident::from_str_and_span(input.name, self.span); args.push(self.cx.param(self.span, ident, self.usize())); self.cx.expr_ident(self.span, ident) } @@ -156,18 +150,11 @@ impl AllocFnFactory<'_, '_> { } } - fn ret_ty(&self, ty: &AllocatorTy, expr: P<Expr>) -> (P<Ty>, P<Expr>) { + fn ret_ty(&self, ty: &AllocatorTy) -> P<Ty> { match *ty { - AllocatorTy::ResultPtr => { - // We're creating: - // - // #expr as *mut u8 - - let expr = self.cx.expr_cast(self.span, expr, self.ptr_u8()); - (self.ptr_u8(), expr) - } + AllocatorTy::ResultPtr => self.ptr_u8(), - AllocatorTy::Unit => (self.cx.ty(self.span, TyKind::Tup(ThinVec::new())), expr), + AllocatorTy::Unit => self.cx.ty(self.span, TyKind::Tup(ThinVec::new())), AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { panic!("can't convert `AllocatorTy` to an output") diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 81b618548da..507b74c2437 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -4,10 +4,12 @@ use rustc_ast as ast; use rustc_ast::entry::EntryPointType; use rustc_ast::mut_visit::{ExpectOne, *}; use rustc_ast::ptr::P; +use rustc_ast::visit::{walk_item, Visitor}; use rustc_ast::{attr, ModKind}; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; +use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS; use rustc_session::Session; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -137,11 +139,31 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { let prev_tests = mem::take(&mut self.tests); noop_visit_item_kind(&mut item.kind, self); self.add_test_cases(item.id, span, prev_tests); + } else { + // But in those cases, we emit a lint to warn the user of these missing tests. + walk_item(&mut InnerItemLinter { sess: self.cx.ext_cx.sess }, &item); } smallvec![P(item)] } } +struct InnerItemLinter<'a> { + sess: &'a Session, +} + +impl<'a> Visitor<'a> for InnerItemLinter<'_> { + fn visit_item(&mut self, i: &'a ast::Item) { + if let Some(attr) = attr::find_by_name(&i.attrs, sym::rustc_test_marker) { + self.sess.parse_sess.buffer_lint( + UNNAMEABLE_TEST_ITEMS, + attr.span, + i.id, + crate::fluent_generated::builtin_macros_unnameable_test_items, + ); + } + } +} + // Beware, this is duplicated in librustc_passes/entry.rs (with // `rustc_hir::Item`), so make sure to keep them in sync. fn entry_point_type(item: &ast::Item, depth: usize) -> EntryPointType { diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs index e92280b26b0..4e4c595de82 100644 --- a/compiler/rustc_codegen_cranelift/src/allocator.rs +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -39,8 +39,8 @@ fn codegen_inner( if kind == AllocatorKind::Default { for method in ALLOCATOR_METHODS { let mut arg_tys = Vec::with_capacity(method.inputs.len()); - for ty in method.inputs.iter() { - match *ty { + for input in method.inputs.iter() { + match input.ty { AllocatorTy::Layout => { arg_tys.push(usize_ty); // size arg_tys.push(usize_ty); // align diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 67ea20112fe..3081dcfa2b7 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -477,7 +477,7 @@ impl<'tcx> LayoutOfHelpers<'tcx> for RevealAllLayoutCx<'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let layout::LayoutError::SizeOverflow(_) = err { + if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { self.0.sess.span_fatal(span, err.to_string()) } else { span_bug!(span, "failed to get layout for `{}`: {}", ty, err) diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index e3006b253b7..36e9ba9c7f8 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1155,6 +1155,20 @@ fn codegen_regular_intrinsic_call<'tcx>( ret.write_cvalue(fx, CValue::by_val(is_eq_value, ret.layout())); } + sym::compare_bytes => { + intrinsic_args!(fx, args => (lhs_ptr, rhs_ptr, bytes_val); intrinsic); + let lhs_ptr = lhs_ptr.load_scalar(fx); + let rhs_ptr = rhs_ptr.load_scalar(fx); + let bytes_val = bytes_val.load_scalar(fx); + + let params = vec![AbiParam::new(fx.pointer_type); 3]; + let returns = vec![AbiParam::new(types::I32)]; + let args = &[lhs_ptr, rhs_ptr, bytes_val]; + // Here we assume that the `memcmp` provided by the target is a NOP for size 0. + let cmp = fx.lib_call("memcmp", params, returns, args)[0]; + ret.write_cvalue(fx, CValue::by_val(cmp, ret.layout())); + } + sym::const_allocate => { intrinsic_args!(fx, args => (_size, _align); intrinsic); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index d1c29f24ab9..9863e40b5b7 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -117,8 +117,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }); } - // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U - _ if intrinsic.as_str().starts_with("simd_shuffle") => { + // simd_shuffle<T, I, U>(x: T, y: T, idx: I) -> U + sym::simd_shuffle => { let (x, y, idx) = match args { [x, y, idx] => (x, y, idx), _ => { @@ -133,36 +133,26 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( return; } - // If this intrinsic is the older "simd_shuffleN" form, simply parse the integer. - // If there is no suffix, use the index array length. - let n: u16 = if intrinsic == sym::simd_shuffle { - // Make sure this is actually an array, since typeck only checks the length-suffixed - // version of this intrinsic. - let idx_ty = fx.monomorphize(idx.ty(fx.mir, fx.tcx)); - match idx_ty.kind() { - ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => len - .try_eval_target_usize(fx.tcx, ty::ParamEnv::reveal_all()) - .unwrap_or_else(|| { - span_bug!(span, "could not evaluate shuffle index array length") - }) - .try_into() - .unwrap(), - _ => { - fx.tcx.sess.span_err( - span, - format!( - "simd_shuffle index must be an array of `u32`, got `{}`", - idx_ty, - ), - ); - // Prevent verifier error - fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); - return; - } + // Make sure this is actually an array, since typeck only checks the length-suffixed + // version of this intrinsic. + let idx_ty = fx.monomorphize(idx.ty(fx.mir, fx.tcx)); + let n: u16 = match idx_ty.kind() { + ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => len + .try_eval_target_usize(fx.tcx, ty::ParamEnv::reveal_all()) + .unwrap_or_else(|| { + span_bug!(span, "could not evaluate shuffle index array length") + }) + .try_into() + .unwrap(), + _ => { + fx.tcx.sess.span_err( + span, + format!("simd_shuffle index must be an array of `u32`, got `{}`", idx_ty), + ); + // Prevent verifier error + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); + return; } - } else { - // FIXME remove this case - intrinsic.as_str()["simd_shuffle".len()..].parse().unwrap() }; assert_eq!(x.layout(), y.layout()); @@ -179,7 +169,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let indexes = { use rustc_middle::mir::interpret::*; let idx_const = crate::constant::mir_operand_get_const_val(fx, idx) - .expect("simd_shuffle* idx not const"); + .expect("simd_shuffle idx not const"); let idx_bytes = match idx_const { ConstValue::ByRef { alloc, offset } => { diff --git a/compiler/rustc_codegen_gcc/src/allocator.rs b/compiler/rustc_codegen_gcc/src/allocator.rs index 13f88192bbc..edd7ab722f6 100644 --- a/compiler/rustc_codegen_gcc/src/allocator.rs +++ b/compiler/rustc_codegen_gcc/src/allocator.rs @@ -27,8 +27,8 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, _module_nam if kind == AllocatorKind::Default { for method in ALLOCATOR_METHODS { let mut types = Vec::with_capacity(method.inputs.len()); - for ty in method.inputs.iter() { - match *ty { + for input in method.inputs.iter() { + match input.ty { AllocatorTy::Layout => { types.push(usize); types.push(usize); diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 08507e19652..88dcafa7370 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -476,7 +476,7 @@ impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) = err { + if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { self.sess().emit_fatal(respan(span, err.into_diagnostic())) } else { span_bug!(span, "failed to get layout for `{}`: {}", ty, err) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 68edde13829..f8c32c6dbbb 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -302,6 +302,21 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } } + sym::compare_bytes => { + let a = args[0].immediate(); + let b = args[1].immediate(); + let n = args[2].immediate(); + + let void_ptr_type = self.context.new_type::<*const ()>(); + let a_ptr = self.bitcast(a, void_ptr_type); + let b_ptr = self.bitcast(b, void_ptr_type); + + // Here we assume that the `memcmp` provided by the target is a NOP for size 0. + let builtin = self.context.get_builtin_function("memcmp"); + let cmp = self.context.new_call(None, builtin, &[a_ptr, b_ptr, n]); + self.sext(cmp, self.type_ix(32)) + } + sym::black_box => { args[0].val.store(self, result); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index f27de867d36..85d3e7234a0 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -1,11 +1,11 @@ -#[cfg(feature="master")] -use gccjit::{ComparisonOp, UnaryOp}; use gccjit::ToRValue; use gccjit::{BinaryOp, RValue, Type}; +#[cfg(feature = "master")] +use gccjit::{ComparisonOp, UnaryOp}; use rustc_codegen_ssa::base::compare_simd_types; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; -#[cfg(feature="master")] +#[cfg(feature = "master")] use rustc_codegen_ssa::errors::ExpectedPointerMutability; use rustc_codegen_ssa::errors::InvalidMonomorphization; use rustc_codegen_ssa::mir::operand::OperandRef; @@ -19,7 +19,7 @@ use rustc_span::{sym, Span, Symbol}; use rustc_target::abi::Align; use crate::builder::Builder; -#[cfg(feature="master")] +#[cfg(feature = "master")] use crate::context::CodegenCx; pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( @@ -57,7 +57,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let arg_tys = sig.inputs(); if name == sym::simd_select_bitmask { - require_simd!(arg_tys[1], InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] }); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] } + ); let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); let expected_int_bits = (len.max(8) - 1).next_power_of_two(); @@ -95,7 +98,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // NOTE: since the arguments can be vectors of floats, make sure the mask is a vector of // integer. let mask_element_type = bx.type_ix(arg1_element_type.get_size() as u64 * 8); - let vector_mask_type = bx.context.new_vector_type(mask_element_type, arg1_vector_type.get_num_units() as u64); + let vector_mask_type = + bx.context.new_vector_type(mask_element_type, arg1_vector_type.get_num_units() as u64); let mut elements = vec![]; let one = bx.context.new_rvalue_one(mask.get_type()); @@ -149,38 +153,24 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // compare them as equal, so bitcast. // FIXME(antoyo): allow comparing vector types as equal in libgccjit. let arg2 = bx.context.new_bitcast(None, args[1].immediate(), arg1.get_type()); - return Ok(compare_simd_types( - bx, - arg1, - arg2, - in_elem, - llret_ty, - cmp_op, - )); + return Ok(compare_simd_types(bx, arg1, arg2, in_elem, llret_ty, cmp_op)); } - if let Some(stripped) = name.as_str().strip_prefix("simd_shuffle") { - let n: u64 = if stripped.is_empty() { - // Make sure this is actually an array, since typeck only checks the length-suffixed - // version of this intrinsic. - match args[2].layout.ty.kind() { - ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => { - len.try_eval_target_usize(bx.cx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else( - || span_bug!(span, "could not evaluate shuffle index array length"), - ) - } - _ => return_error!(InvalidMonomorphization::SimdShuffle { - span, - name, - ty: args[2].layout.ty - }), + if name == sym::simd_shuffle { + // Make sure this is actually an array, since typeck only checks the length-suffixed + // version of this intrinsic. + let n: u64 = match args[2].layout.ty.kind() { + ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => { + len.try_eval_target_usize(bx.cx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else( + || span_bug!(span, "could not evaluate shuffle index array length"), + ) } - } else { - stripped.parse().unwrap_or_else(|_| { - span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?") - }) + _ => return_error!(InvalidMonomorphization::SimdShuffle { + span, + name, + ty: args[2].layout.ty + }), }; - require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); @@ -202,7 +192,13 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( if name == sym::simd_insert { require!( in_elem == arg_tys[2], - InvalidMonomorphization::InsertedType { span, name, in_elem, in_ty, out_ty: arg_tys[2] } + InvalidMonomorphization::InsertedType { + span, + name, + in_elem, + in_ty, + out_ty: arg_tys[2] + } ); let vector = args[0].immediate(); let index = args[1].immediate(); @@ -228,7 +224,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( if name == sym::simd_select { let m_elem_ty = in_elem; let m_len = in_len; - require_simd!(arg_tys[1], InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] }); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] } + ); let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); require!( m_len == v_len, @@ -241,7 +240,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return Ok(bx.vector_select(args[0].immediate(), args[1].immediate(), args[2].immediate())); } - #[cfg(feature="master")] + #[cfg(feature = "master")] if name == sym::simd_cast || name == sym::simd_as { require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); @@ -267,19 +266,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( Unsupported, } - let in_style = - match in_elem.kind() { - ty::Int(_) | ty::Uint(_) => Style::Int, - ty::Float(_) => Style::Float, - _ => Style::Unsupported, - }; + let in_style = match in_elem.kind() { + ty::Int(_) | ty::Uint(_) => Style::Int, + ty::Float(_) => Style::Float, + _ => Style::Unsupported, + }; - let out_style = - match out_elem.kind() { - ty::Int(_) | ty::Uint(_) => Style::Int, - ty::Float(_) => Style::Float, - _ => Style::Unsupported, - }; + let out_style = match out_elem.kind() { + ty::Int(_) | ty::Uint(_) => Style::Int, + ty::Float(_) => Style::Float, + _ => Style::Unsupported, + }; match (in_style, out_style) { (Style::Unsupported, Style::Unsupported) => { @@ -294,7 +291,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( out_elem } ); - }, + } _ => return Ok(bx.context.convert_vector(None, args[0].immediate(), llret_ty)), } } @@ -342,10 +339,13 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let mut shift = 0; for i in 0..in_len { - let elem = bx.extract_element(vector, bx.context.new_rvalue_from_int(bx.int_type, i as i32)); + let elem = + bx.extract_element(vector, bx.context.new_rvalue_from_int(bx.int_type, i as i32)); let shifted = elem >> sign_shift; let masked = shifted & one; - result = result | (bx.context.new_cast(None, masked, result_type) << bx.context.new_rvalue_from_int(result_type, shift)); + result = result + | (bx.context.new_cast(None, masked, result_type) + << bx.context.new_rvalue_from_int(result_type, shift)); shift += 1; } @@ -394,46 +394,50 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return Err(()); }}; } - let (elem_ty_str, elem_ty) = - if let ty::Float(f) = in_elem.kind() { - let elem_ty = bx.cx.type_float_from_ty(*f); - match f.bit_width() { - 32 => ("f", elem_ty), - 64 => ("", elem_ty), - _ => { - return_error!(InvalidMonomorphization::FloatingPointVector { span, name, f_ty: *f, in_ty }); - } + let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() { + let elem_ty = bx.cx.type_float_from_ty(*f); + match f.bit_width() { + 32 => ("f", elem_ty), + 64 => ("", elem_ty), + _ => { + return_error!(InvalidMonomorphization::FloatingPointVector { + span, + name, + f_ty: *f, + in_ty + }); } } - else { - return_error!(InvalidMonomorphization::FloatingPointType { span, name, in_ty }); - }; + } else { + return_error!(InvalidMonomorphization::FloatingPointType { span, name, in_ty }); + }; let vec_ty = bx.cx.type_vector(elem_ty, in_len); - let intr_name = - match name { - sym::simd_ceil => "ceil", - sym::simd_fabs => "fabs", // TODO(antoyo): pand with 170141183420855150465331762880109871103 - sym::simd_fcos => "cos", - sym::simd_fexp2 => "exp2", - sym::simd_fexp => "exp", - sym::simd_flog10 => "log10", - sym::simd_flog2 => "log2", - sym::simd_flog => "log", - sym::simd_floor => "floor", - sym::simd_fma => "fma", - sym::simd_fpowi => "__builtin_powi", - sym::simd_fpow => "pow", - sym::simd_fsin => "sin", - sym::simd_fsqrt => "sqrt", - sym::simd_round => "round", - sym::simd_trunc => "trunc", - _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }) - }; + let intr_name = match name { + sym::simd_ceil => "ceil", + sym::simd_fabs => "fabs", // TODO(antoyo): pand with 170141183420855150465331762880109871103 + sym::simd_fcos => "cos", + sym::simd_fexp2 => "exp2", + sym::simd_fexp => "exp", + sym::simd_flog10 => "log10", + sym::simd_flog2 => "log2", + sym::simd_flog => "log", + sym::simd_floor => "floor", + sym::simd_fma => "fma", + sym::simd_fpowi => "__builtin_powi", + sym::simd_fpow => "pow", + sym::simd_fsin => "sin", + sym::simd_fsqrt => "sqrt", + sym::simd_round => "round", + sym::simd_trunc => "trunc", + _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }), + }; let builtin_name = format!("{}{}", intr_name, elem_ty_str); let funcs = bx.cx.functions.borrow(); - let function = funcs.get(&builtin_name).unwrap_or_else(|| panic!("unable to find builtin function {}", builtin_name)); + let function = funcs + .get(&builtin_name) + .unwrap_or_else(|| panic!("unable to find builtin function {}", builtin_name)); // TODO(antoyo): add platform-specific behavior here for architectures that have these // intrinsics as instructions (for instance, gpus) @@ -479,8 +483,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args); } - #[cfg(feature="master")] - fn vector_ty<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, elem_ty: Ty<'tcx>, vec_len: u64) -> Type<'gcc> { + #[cfg(feature = "master")] + fn vector_ty<'gcc, 'tcx>( + cx: &CodegenCx<'gcc, 'tcx>, + elem_ty: Ty<'tcx>, + vec_len: u64, + ) -> Type<'gcc> { // FIXME: use cx.layout_of(ty).llvm_type() ? let elem_ty = match *elem_ty.kind() { ty::Int(v) => cx.type_int_from_ty(v), @@ -491,15 +499,22 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( cx.type_vector(elem_ty, vec_len) } - #[cfg(feature="master")] - fn gather<'a, 'gcc, 'tcx>(default: RValue<'gcc>, pointers: RValue<'gcc>, mask: RValue<'gcc>, pointer_count: usize, bx: &mut Builder<'a, 'gcc, 'tcx>, in_len: u64, underlying_ty: Ty<'tcx>, invert: bool) -> RValue<'gcc> { - let vector_type = - if pointer_count > 1 { - bx.context.new_vector_type(bx.usize_type, in_len) - } - else { - vector_ty(bx, underlying_ty, in_len) - }; + #[cfg(feature = "master")] + fn gather<'a, 'gcc, 'tcx>( + default: RValue<'gcc>, + pointers: RValue<'gcc>, + mask: RValue<'gcc>, + pointer_count: usize, + bx: &mut Builder<'a, 'gcc, 'tcx>, + in_len: u64, + underlying_ty: Ty<'tcx>, + invert: bool, + ) -> RValue<'gcc> { + let vector_type = if pointer_count > 1 { + bx.context.new_vector_type(bx.usize_type, in_len) + } else { + vector_ty(bx, underlying_ty, in_len) + }; let elem_type = vector_type.dyncast_vector().expect("vector type").get_element_type(); let mut values = vec![]; @@ -530,13 +545,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( if invert { bx.shuffle_vector(vector, default, mask) - } - else { + } else { bx.shuffle_vector(default, vector, mask) } } - #[cfg(feature="master")] + #[cfg(feature = "master")] if name == sym::simd_gather { // simd_gather(values: <N x T>, pointers: <N x *_ T>, // mask: <N x i{M}>) -> <N x T> @@ -546,8 +560,14 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // All types must be simd vector types require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty }); - require_simd!(arg_tys[1], InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] }); - require_simd!(arg_tys[2], InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] }); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] } + ); + require_simd!( + arg_tys[2], + InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] } + ); require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); // Of the same length: @@ -641,10 +661,19 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } } - return Ok(gather(args[0].immediate(), args[1].immediate(), args[2].immediate(), pointer_count, bx, in_len, underlying_ty, false)); + return Ok(gather( + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + pointer_count, + bx, + in_len, + underlying_ty, + false, + )); } - #[cfg(feature="master")] + #[cfg(feature = "master")] if name == sym::simd_scatter { // simd_scatter(values: <N x T>, pointers: <N x *mut T>, // mask: <N x i{M}>) -> () @@ -654,8 +683,14 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // All types must be simd vector types require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty }); - require_simd!(arg_tys[1], InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] }); - require_simd!(arg_tys[2], InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] }); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] } + ); + require_simd!( + arg_tys[2], + InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] } + ); // Of the same length: let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx()); @@ -744,17 +779,24 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } } - let result = gather(args[0].immediate(), args[1].immediate(), args[2].immediate(), pointer_count, bx, in_len, underlying_ty, true); + let result = gather( + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + pointer_count, + bx, + in_len, + underlying_ty, + true, + ); let pointers = args[1].immediate(); - let vector_type = - if pointer_count > 1 { - bx.context.new_vector_type(bx.usize_type, in_len) - } - else { - vector_ty(bx, underlying_ty, in_len) - }; + let vector_type = if pointer_count > 1 { + bx.context.new_vector_type(bx.usize_type, in_len) + } else { + vector_ty(bx, underlying_ty, in_len) + }; let elem_type = vector_type.dyncast_vector().expect("vector type").get_element_type(); for i in 0..in_len { @@ -809,11 +851,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let rhs = args[1].immediate(); let is_add = name == sym::simd_saturating_add; let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _; - let (signed, elem_width, elem_ty) = - match *in_elem.kind() { - ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_int_from_ty(i)), - ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_uint_from_ty(i)), - _ => { + let (signed, elem_width, elem_ty) = match *in_elem.kind() { + ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_int_from_ty(i)), + ty::Uint(i) => { + (false, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_uint_from_ty(i)) + } + _ => { return_error!(InvalidMonomorphization::ExpectedVectorElementType { span, name, @@ -823,77 +866,82 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } }; - let result = - match (signed, is_add) { - (false, true) => { - let res = lhs + rhs; - let cmp = bx.context.new_comparison(None, ComparisonOp::LessThan, res, lhs); - res | cmp - }, - (true, true) => { - // Algorithm from: https://codereview.stackexchange.com/questions/115869/saturated-signed-addition - // TODO(antoyo): improve using conditional operators if possible. - // TODO(antoyo): dyncast_vector should not require a call to unqualified. - let arg_type = lhs.get_type().unqualified(); - // TODO(antoyo): convert lhs and rhs to unsigned. - let sum = lhs + rhs; - let vector_type = arg_type.dyncast_vector().expect("vector type"); - let unit = vector_type.get_num_units(); - let a = bx.context.new_rvalue_from_int(elem_ty, ((elem_width as i32) << 3) - 1); - let width = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![a; unit]); - - let xor1 = lhs ^ rhs; - let xor2 = lhs ^ sum; - let and = bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, xor1) & xor2; - let mask = and >> width; - - let one = bx.context.new_rvalue_one(elem_ty); - let ones = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![one; unit]); - let shift1 = ones << width; - let shift2 = sum >> width; - let mask_min = shift1 ^ shift2; - - let and1 = bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, mask) & sum; - let and2 = mask & mask_min; - - and1 + and2 - }, - (false, false) => { - let res = lhs - rhs; - let cmp = bx.context.new_comparison(None, ComparisonOp::LessThanEquals, res, lhs); - res & cmp - }, - (true, false) => { - // TODO(antoyo): dyncast_vector should not require a call to unqualified. - let arg_type = lhs.get_type().unqualified(); - // TODO(antoyo): this uses the same algorithm from saturating add, but add the - // negative of the right operand. Find a proper subtraction algorithm. - let rhs = bx.context.new_unary_op(None, UnaryOp::Minus, arg_type, rhs); - - // TODO(antoyo): convert lhs and rhs to unsigned. - let sum = lhs + rhs; - let vector_type = arg_type.dyncast_vector().expect("vector type"); - let unit = vector_type.get_num_units(); - let a = bx.context.new_rvalue_from_int(elem_ty, ((elem_width as i32) << 3) - 1); - let width = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![a; unit]); - - let xor1 = lhs ^ rhs; - let xor2 = lhs ^ sum; - let and = bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, xor1) & xor2; - let mask = and >> width; - - let one = bx.context.new_rvalue_one(elem_ty); - let ones = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![one; unit]); - let shift1 = ones << width; - let shift2 = sum >> width; - let mask_min = shift1 ^ shift2; - - let and1 = bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, mask) & sum; - let and2 = mask & mask_min; - - and1 + and2 - } - }; + let result = match (signed, is_add) { + (false, true) => { + let res = lhs + rhs; + let cmp = bx.context.new_comparison(None, ComparisonOp::LessThan, res, lhs); + res | cmp + } + (true, true) => { + // Algorithm from: https://codereview.stackexchange.com/questions/115869/saturated-signed-addition + // TODO(antoyo): improve using conditional operators if possible. + // TODO(antoyo): dyncast_vector should not require a call to unqualified. + let arg_type = lhs.get_type().unqualified(); + // TODO(antoyo): convert lhs and rhs to unsigned. + let sum = lhs + rhs; + let vector_type = arg_type.dyncast_vector().expect("vector type"); + let unit = vector_type.get_num_units(); + let a = bx.context.new_rvalue_from_int(elem_ty, ((elem_width as i32) << 3) - 1); + let width = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![a; unit]); + + let xor1 = lhs ^ rhs; + let xor2 = lhs ^ sum; + let and = + bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, xor1) & xor2; + let mask = and >> width; + + let one = bx.context.new_rvalue_one(elem_ty); + let ones = + bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![one; unit]); + let shift1 = ones << width; + let shift2 = sum >> width; + let mask_min = shift1 ^ shift2; + + let and1 = + bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, mask) & sum; + let and2 = mask & mask_min; + + and1 + and2 + } + (false, false) => { + let res = lhs - rhs; + let cmp = bx.context.new_comparison(None, ComparisonOp::LessThanEquals, res, lhs); + res & cmp + } + (true, false) => { + // TODO(antoyo): dyncast_vector should not require a call to unqualified. + let arg_type = lhs.get_type().unqualified(); + // TODO(antoyo): this uses the same algorithm from saturating add, but add the + // negative of the right operand. Find a proper subtraction algorithm. + let rhs = bx.context.new_unary_op(None, UnaryOp::Minus, arg_type, rhs); + + // TODO(antoyo): convert lhs and rhs to unsigned. + let sum = lhs + rhs; + let vector_type = arg_type.dyncast_vector().expect("vector type"); + let unit = vector_type.get_num_units(); + let a = bx.context.new_rvalue_from_int(elem_ty, ((elem_width as i32) << 3) - 1); + let width = bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![a; unit]); + + let xor1 = lhs ^ rhs; + let xor2 = lhs ^ sum; + let and = + bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, xor1) & xor2; + let mask = and >> width; + + let one = bx.context.new_rvalue_one(elem_ty); + let ones = + bx.context.new_rvalue_from_vector(None, lhs.get_type(), &vec![one; unit]); + let shift1 = ones << width; + let shift2 = sum >> width; + let mask_min = shift1 ^ shift2; + + let and1 = + bx.context.new_unary_op(None, UnaryOp::BitwiseNegate, arg_type, mask) & sum; + let and2 = mask & mask_min; + + and1 + and2 + } + }; return Ok(result); } @@ -968,7 +1016,6 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( 1.0 ); - macro_rules! minmax_red { ($name:ident: $int_red:ident, $float_red:ident) => { if name == sym::$name { @@ -979,13 +1026,13 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => Ok(bx.$int_red(args[0].immediate())), ty::Float(_) => Ok(bx.$float_red(args[0].immediate())), - _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { - span, + _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { + span, name, symbol: sym::$name, in_ty, - in_elem, - ret_ty + in_elem, + ret_ty }), }; } @@ -1025,7 +1072,15 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => { let r = bx.vector_reduce_op(input, $op); - Ok(if !$boolean { r } else { bx.icmp(IntPredicate::IntNE, r, bx.context.new_rvalue_zero(r.get_type())) }) + Ok(if !$boolean { + r + } else { + bx.icmp( + IntPredicate::IntNE, + r, + bx.context.new_rvalue_zero(r.get_type()), + ) + }) } _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index ca123334fca..8bb93025c45 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -34,8 +34,8 @@ pub(crate) unsafe fn codegen( if kind == AllocatorKind::Default { for method in ALLOCATOR_METHODS { let mut args = Vec::with_capacity(method.inputs.len()); - for ty in method.inputs.iter() { - match *ty { + for input in method.inputs.iter() { + match input.ty { AllocatorTy::Layout => { args.push(usize); // size args.push(usize); // align diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index cb093996d1d..2d23be6cd52 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -891,7 +891,8 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.prefetch", fn(ptr, t_i32, t_i32, t_i32) -> void); // This isn't an "LLVM intrinsic", but LLVM's optimization passes - // recognize it like one and we assume it exists in `core::slice::cmp` + // recognize it like one (including turning it into `bcmp` sometimes) + // and we use it to implement intrinsics like `raw_eq` and `compare_bytes` match self.sess().target.arch.as_ref() { "avr" | "msp430" => ifn!("memcmp", fn(ptr, ptr, t_isize) -> t_i16), _ => ifn!("memcmp", fn(ptr, ptr, t_isize) -> t_i32), @@ -985,7 +986,7 @@ impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) = err { + if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { self.sess().emit_fatal(Spanned { span, node: err.into_diagnostic() }) } else { span_bug!(span, "failed to get layout for `{ty}`: {err:?}") diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index 6ca61aa43d7..7a82d05ce9e 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -85,3 +85,197 @@ impl CounterExpression { Self { kind, lhs, rhs } } } + +/// Corresponds to enum `llvm::coverage::CounterMappingRegion::RegionKind`. +/// +/// Must match the layout of `LLVMRustCounterMappingRegionKind`. +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub enum RegionKind { + /// A CodeRegion associates some code with a counter + CodeRegion = 0, + + /// An ExpansionRegion represents a file expansion region that associates + /// a source range with the expansion of a virtual source file, such as + /// for a macro instantiation or #include file. + ExpansionRegion = 1, + + /// A SkippedRegion represents a source range with code that was skipped + /// by a preprocessor or similar means. + SkippedRegion = 2, + + /// A GapRegion is like a CodeRegion, but its count is only set as the + /// line execution count when its the only region in the line. + GapRegion = 3, + + /// A BranchRegion represents leaf-level boolean expressions and is + /// associated with two counters, each representing the number of times the + /// expression evaluates to true or false. + BranchRegion = 4, +} + +/// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the +/// coverage map, in accordance with the +/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format). +/// The struct composes fields representing the `Counter` type and value(s) (injected counter +/// ID, or expression type and operands), the source file (an indirect index into a "filenames +/// array", encoded separately), and source location (start and end positions of the represented +/// code region). +/// +/// Corresponds to struct `llvm::coverage::CounterMappingRegion`. +/// +/// Must match the layout of `LLVMRustCounterMappingRegion`. +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct CounterMappingRegion { + /// The counter type and type-dependent counter data, if any. + counter: Counter, + + /// If the `RegionKind` is a `BranchRegion`, this represents the counter + /// for the false branch of the region. + false_counter: Counter, + + /// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the + /// file_id is an index into a function-specific `virtual_file_mapping` array of indexes + /// that, in turn, are used to look up the filename for this region. + file_id: u32, + + /// If the `RegionKind` is an `ExpansionRegion`, the `expanded_file_id` can be used to find + /// the mapping regions created as a result of macro expansion, by checking if their file id + /// matches the expanded file id. + expanded_file_id: u32, + + /// 1-based starting line of the mapping region. + start_line: u32, + + /// 1-based starting column of the mapping region. + start_col: u32, + + /// 1-based ending line of the mapping region. + end_line: u32, + + /// 1-based ending column of the mapping region. If the high bit is set, the current + /// mapping region is a gap area. + end_col: u32, + + kind: RegionKind, +} + +impl CounterMappingRegion { + pub(crate) fn code_region( + counter: Counter, + file_id: u32, + start_line: u32, + start_col: u32, + end_line: u32, + end_col: u32, + ) -> Self { + Self { + counter, + false_counter: Counter::zero(), + file_id, + expanded_file_id: 0, + start_line, + start_col, + end_line, + end_col, + kind: RegionKind::CodeRegion, + } + } + + // This function might be used in the future; the LLVM API is still evolving, as is coverage + // support. + #[allow(dead_code)] + pub(crate) fn branch_region( + counter: Counter, + false_counter: Counter, + file_id: u32, + start_line: u32, + start_col: u32, + end_line: u32, + end_col: u32, + ) -> Self { + Self { + counter, + false_counter, + file_id, + expanded_file_id: 0, + start_line, + start_col, + end_line, + end_col, + kind: RegionKind::BranchRegion, + } + } + + // This function might be used in the future; the LLVM API is still evolving, as is coverage + // support. + #[allow(dead_code)] + pub(crate) fn expansion_region( + file_id: u32, + expanded_file_id: u32, + start_line: u32, + start_col: u32, + end_line: u32, + end_col: u32, + ) -> Self { + Self { + counter: Counter::zero(), + false_counter: Counter::zero(), + file_id, + expanded_file_id, + start_line, + start_col, + end_line, + end_col, + kind: RegionKind::ExpansionRegion, + } + } + + // This function might be used in the future; the LLVM API is still evolving, as is coverage + // support. + #[allow(dead_code)] + pub(crate) fn skipped_region( + file_id: u32, + start_line: u32, + start_col: u32, + end_line: u32, + end_col: u32, + ) -> Self { + Self { + counter: Counter::zero(), + false_counter: Counter::zero(), + file_id, + expanded_file_id: 0, + start_line, + start_col, + end_line, + end_col, + kind: RegionKind::SkippedRegion, + } + } + + // This function might be used in the future; the LLVM API is still evolving, as is coverage + // support. + #[allow(dead_code)] + pub(crate) fn gap_region( + counter: Counter, + file_id: u32, + start_line: u32, + start_col: u32, + end_line: u32, + end_col: u32, + ) -> Self { + Self { + counter, + false_counter: Counter::zero(), + file_id, + expanded_file_id: 0, + start_line, + start_col, + end_line, + end_col: (1_u32 << 31) | end_col, + kind: RegionKind::GapRegion, + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs index 7e981af0a53..f1e68af25d4 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs @@ -1,4 +1,4 @@ -pub use super::ffi::*; +use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::bug; diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 9149b06886b..db9ba0abbde 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,9 +1,8 @@ use crate::common::CodegenCx; use crate::coverageinfo; -use crate::coverageinfo::map_data::{Counter, CounterExpression}; +use crate::coverageinfo::ffi::{Counter, CounterExpression, CounterMappingRegion}; use crate::llvm; -use llvm::coverageinfo::CounterMappingRegion; use rustc_codegen_ssa::traits::ConstMethods; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def::DefKind; diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index c1017ab8d7c..afceb7531e6 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -3,10 +3,10 @@ use crate::llvm; use crate::abi::Abi; use crate::builder::Builder; use crate::common::CodegenCx; -use crate::coverageinfo::map_data::{CounterExpression, FunctionCoverage}; +use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion}; +use crate::coverageinfo::map_data::FunctionCoverage; use libc::c_uint; -use llvm::coverageinfo::CounterMappingRegion; use rustc_codegen_ssa::traits::{ BaseTypeMethods, BuilderMethods, ConstMethods, CoverageInfoBuilderMethods, MiscMethods, StaticMethods, @@ -27,7 +27,7 @@ use rustc_middle::ty::Ty; use std::cell::RefCell; use std::ffi::CString; -mod ffi; +pub(crate) mod ffi; pub(crate) mod map_data; pub mod mapgen; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 40f0bcfdf58..f8cbcbd5ec8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -990,14 +990,8 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>( closure_or_generator_di_node: &'ll DIType, ) -> SmallVec<&'ll DIType> { let (&def_id, up_var_tys) = match closure_or_generator_ty.kind() { - ty::Generator(def_id, args, _) => { - let upvar_tys: SmallVec<_> = args.as_generator().prefix_tys().collect(); - (def_id, upvar_tys) - } - ty::Closure(def_id, args) => { - let upvar_tys: SmallVec<_> = args.as_closure().upvar_tys().collect(); - (def_id, upvar_tys) - } + ty::Generator(def_id, args, _) => (def_id, args.as_generator().prefix_tys()), + ty::Closure(def_id, args) => (def_id, args.as_closure().upvar_tys()), _ => { bug!( "build_upvar_field_di_nodes() called with non-closure-or-generator-type: {:?}", @@ -1007,9 +1001,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>( }; debug_assert!( - up_var_tys - .iter() - .all(|&t| t == cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)) + up_var_tys.iter().all(|t| t == cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)) ); let capture_names = cx.tcx.closure_saved_names_of_captured_variables(def_id); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index b4beb80ca8b..d3239d5c358 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -379,6 +379,7 @@ pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>( // Fields that are common to all states let common_fields: SmallVec<_> = generator_args .prefix_tys() + .iter() .zip(common_upvar_names) .enumerate() .map(|(index, (upvar_ty, upvar_name))| { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 86c39ab5e94..a9b06030e70 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -329,6 +329,16 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } + sym::compare_bytes => { + // Here we assume that the `memcmp` provided by the target is a NOP for size 0. + let cmp = self.call_intrinsic( + "memcmp", + &[args[0].immediate(), args[1].immediate(), args[2].immediate()], + ); + // Some targets have `memcmp` returning `i16`, but the intrinsic is always `i32`. + self.sext(cmp, self.type_ix(32)) + } + sym::black_box => { args[0].val.store(self, result); let result_val_span = [result.llval]; @@ -1020,28 +1030,20 @@ fn generic_simd_intrinsic<'ll, 'tcx>( )); } - if let Some(stripped) = name.as_str().strip_prefix("simd_shuffle") { - // If this intrinsic is the older "simd_shuffleN" form, simply parse the integer. - // If there is no suffix, use the index array length. - let n: u64 = if stripped.is_empty() { - // Make sure this is actually an array, since typeck only checks the length-suffixed - // version of this intrinsic. - match args[2].layout.ty.kind() { - ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => { - len.try_eval_target_usize(bx.cx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else( - || span_bug!(span, "could not evaluate shuffle index array length"), - ) - } - _ => return_error!(InvalidMonomorphization::SimdShuffle { - span, - name, - ty: args[2].layout.ty - }), + if name == sym::simd_shuffle { + // Make sure this is actually an array, since typeck only checks the length-suffixed + // version of this intrinsic. + let n: u64 = match args[2].layout.ty.kind() { + ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => { + len.try_eval_target_usize(bx.cx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else( + || span_bug!(span, "could not evaluate shuffle index array length"), + ) } - } else { - stripped.parse().unwrap_or_else(|_| { - span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?") - }) + _ => return_error!(InvalidMonomorphization::SimdShuffle { + span, + name, + ty: args[2].layout.ty + }), }; require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7407cfa8c5b..b167facfb02 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1,8 +1,6 @@ #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)] -use crate::coverageinfo::map_data as coverage_map; - use super::debuginfo::{ DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DILocation, DINameSpace, @@ -688,204 +686,6 @@ extern "C" { pub type DiagnosticHandlerTy = unsafe extern "C" fn(&DiagnosticInfo, *mut c_void); pub type InlineAsmDiagHandlerTy = unsafe extern "C" fn(&SMDiagnostic, *const c_void, c_uint); -pub mod coverageinfo { - use super::coverage_map; - - /// Corresponds to enum `llvm::coverage::CounterMappingRegion::RegionKind`. - /// - /// Must match the layout of `LLVMRustCounterMappingRegionKind`. - #[derive(Copy, Clone, Debug)] - #[repr(C)] - pub enum RegionKind { - /// A CodeRegion associates some code with a counter - CodeRegion = 0, - - /// An ExpansionRegion represents a file expansion region that associates - /// a source range with the expansion of a virtual source file, such as - /// for a macro instantiation or #include file. - ExpansionRegion = 1, - - /// A SkippedRegion represents a source range with code that was skipped - /// by a preprocessor or similar means. - SkippedRegion = 2, - - /// A GapRegion is like a CodeRegion, but its count is only set as the - /// line execution count when its the only region in the line. - GapRegion = 3, - - /// A BranchRegion represents leaf-level boolean expressions and is - /// associated with two counters, each representing the number of times the - /// expression evaluates to true or false. - BranchRegion = 4, - } - - /// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the - /// coverage map, in accordance with the - /// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format). - /// The struct composes fields representing the `Counter` type and value(s) (injected counter - /// ID, or expression type and operands), the source file (an indirect index into a "filenames - /// array", encoded separately), and source location (start and end positions of the represented - /// code region). - /// - /// Corresponds to struct `llvm::coverage::CounterMappingRegion`. - /// - /// Must match the layout of `LLVMRustCounterMappingRegion`. - #[derive(Copy, Clone, Debug)] - #[repr(C)] - pub struct CounterMappingRegion { - /// The counter type and type-dependent counter data, if any. - counter: coverage_map::Counter, - - /// If the `RegionKind` is a `BranchRegion`, this represents the counter - /// for the false branch of the region. - false_counter: coverage_map::Counter, - - /// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the - /// file_id is an index into a function-specific `virtual_file_mapping` array of indexes - /// that, in turn, are used to look up the filename for this region. - file_id: u32, - - /// If the `RegionKind` is an `ExpansionRegion`, the `expanded_file_id` can be used to find - /// the mapping regions created as a result of macro expansion, by checking if their file id - /// matches the expanded file id. - expanded_file_id: u32, - - /// 1-based starting line of the mapping region. - start_line: u32, - - /// 1-based starting column of the mapping region. - start_col: u32, - - /// 1-based ending line of the mapping region. - end_line: u32, - - /// 1-based ending column of the mapping region. If the high bit is set, the current - /// mapping region is a gap area. - end_col: u32, - - kind: RegionKind, - } - - impl CounterMappingRegion { - pub(crate) fn code_region( - counter: coverage_map::Counter, - file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter, - false_counter: coverage_map::Counter::zero(), - file_id, - expanded_file_id: 0, - start_line, - start_col, - end_line, - end_col, - kind: RegionKind::CodeRegion, - } - } - - // This function might be used in the future; the LLVM API is still evolving, as is coverage - // support. - #[allow(dead_code)] - pub(crate) fn branch_region( - counter: coverage_map::Counter, - false_counter: coverage_map::Counter, - file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter, - false_counter, - file_id, - expanded_file_id: 0, - start_line, - start_col, - end_line, - end_col, - kind: RegionKind::BranchRegion, - } - } - - // This function might be used in the future; the LLVM API is still evolving, as is coverage - // support. - #[allow(dead_code)] - pub(crate) fn expansion_region( - file_id: u32, - expanded_file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter: coverage_map::Counter::zero(), - false_counter: coverage_map::Counter::zero(), - file_id, - expanded_file_id, - start_line, - start_col, - end_line, - end_col, - kind: RegionKind::ExpansionRegion, - } - } - - // This function might be used in the future; the LLVM API is still evolving, as is coverage - // support. - #[allow(dead_code)] - pub(crate) fn skipped_region( - file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter: coverage_map::Counter::zero(), - false_counter: coverage_map::Counter::zero(), - file_id, - expanded_file_id: 0, - start_line, - start_col, - end_line, - end_col, - kind: RegionKind::SkippedRegion, - } - } - - // This function might be used in the future; the LLVM API is still evolving, as is coverage - // support. - #[allow(dead_code)] - pub(crate) fn gap_region( - counter: coverage_map::Counter, - file_id: u32, - start_line: u32, - start_col: u32, - end_line: u32, - end_col: u32, - ) -> Self { - Self { - counter, - false_counter: coverage_map::Counter::zero(), - file_id, - expanded_file_id: 0, - start_line, - start_col, - end_line, - end_col: (1_u32 << 31) | end_col, - kind: RegionKind::GapRegion, - } - } - } -} - pub mod debuginfo { use super::{InvariantOpaque, Metadata}; use bitflags::bitflags; @@ -1911,9 +1711,9 @@ extern "C" { pub fn LLVMRustCoverageWriteMappingToBuffer( VirtualFileMappingIDs: *const c_uint, NumVirtualFileMappingIDs: c_uint, - Expressions: *const coverage_map::CounterExpression, + Expressions: *const crate::coverageinfo::ffi::CounterExpression, NumExpressions: c_uint, - MappingRegions: *const coverageinfo::CounterMappingRegion, + MappingRegions: *const crate::coverageinfo::ffi::CounterMappingRegion, NumMappingRegions: c_uint, BufferOut: &RustString, ); diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 32e71c6a641..2be7bce115d 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -61,9 +61,6 @@ fn uncached_llvm_type<'a, 'tcx>( } Some(name) } - // Use identified structure types for ADT. Due to pointee types in LLVM IR their definition - // might be recursive. Other cases are non-recursive and we can use literal structure types. - ty::Adt(..) => Some(String::new()), _ => None, }; diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 6582fd62387..8f383f68bcd 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -3,9 +3,6 @@ name = "rustc_codegen_ssa" version = "0.0.0" edition = "2021" -[lib] -test = false - [dependencies] ar_archive_writer = "0.1.3" bitflags = "1.2.1" diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index 18268622341..ebf04e7a399 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -1,6 +1,7 @@ use pathdiff::diff_paths; use rustc_data_structures::fx::FxHashSet; use std::env; +use std::ffi::OsString; use std::fs; use std::path::{Path, PathBuf}; @@ -12,7 +13,7 @@ pub struct RPathConfig<'a> { pub linker_is_gnu: bool, } -pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec<String> { +pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec<OsString> { // No rpath on windows if !config.has_rpath { return Vec::new(); @@ -21,36 +22,38 @@ pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec<String> { debug!("preparing the RPATH!"); let rpaths = get_rpaths(config); - let mut flags = rpaths_to_flags(&rpaths); + let mut flags = rpaths_to_flags(rpaths); if config.linker_is_gnu { // Use DT_RUNPATH instead of DT_RPATH if available - flags.push("-Wl,--enable-new-dtags".to_owned()); + flags.push("-Wl,--enable-new-dtags".into()); // Set DF_ORIGIN for substitute $ORIGIN - flags.push("-Wl,-z,origin".to_owned()); + flags.push("-Wl,-z,origin".into()); } flags } -fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> { +fn rpaths_to_flags(rpaths: Vec<OsString>) -> Vec<OsString> { let mut ret = Vec::with_capacity(rpaths.len()); // the minimum needed capacity for rpath in rpaths { - if rpath.contains(',') { + if rpath.to_string_lossy().contains(',') { ret.push("-Wl,-rpath".into()); ret.push("-Xlinker".into()); - ret.push(rpath.clone()); + ret.push(rpath); } else { - ret.push(format!("-Wl,-rpath,{}", &(*rpath))); + let mut single_arg = OsString::from("-Wl,-rpath,"); + single_arg.push(rpath); + ret.push(single_arg); } } ret } -fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec<String> { +fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec<OsString> { debug!("output: {:?}", config.out_filename.display()); debug!("libs:"); for libpath in config.libs { @@ -64,18 +67,18 @@ fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec<String> { debug!("rpaths:"); for rpath in &rpaths { - debug!(" {}", rpath); + debug!(" {:?}", rpath); } // Remove duplicates minimize_rpaths(&rpaths) } -fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>) -> Vec<String> { +fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>) -> Vec<OsString> { config.libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect() } -fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> String { +fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> OsString { // Mac doesn't appear to support $ORIGIN let prefix = if config.is_like_osx { "@loader_path" } else { "$ORIGIN" }; @@ -87,8 +90,11 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> Str let output = fs::canonicalize(&output).unwrap_or(output); let relative = path_relative_from(&lib, &output) .unwrap_or_else(|| panic!("couldn't create relative path from {output:?} to {lib:?}")); - // FIXME (#9639): This needs to handle non-utf8 paths - format!("{}/{}", prefix, relative.to_str().expect("non-utf8 component in path")) + + let mut rpath = OsString::from(prefix); + rpath.push("/"); + rpath.push(relative); + rpath } // This routine is adapted from the *old* Path's `path_relative_from` @@ -99,7 +105,7 @@ fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> { diff_paths(path, base) } -fn minimize_rpaths(rpaths: &[String]) -> Vec<String> { +fn minimize_rpaths(rpaths: &[OsString]) -> Vec<OsString> { let mut set = FxHashSet::default(); let mut minimized = Vec::new(); for rpath in rpaths { diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs index 604f19144a6..ac2e54072c4 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs @@ -1,32 +1,33 @@ use super::RPathConfig; use super::{get_rpath_relative_to_output, minimize_rpaths, rpaths_to_flags}; +use std::ffi::OsString; use std::path::{Path, PathBuf}; #[test] fn test_rpaths_to_flags() { - let flags = rpaths_to_flags(&["path1".to_string(), "path2".to_string()]); + let flags = rpaths_to_flags(vec!["path1".into(), "path2".into()]); assert_eq!(flags, ["-Wl,-rpath,path1", "-Wl,-rpath,path2"]); } #[test] fn test_minimize1() { - let res = minimize_rpaths(&["rpath1".to_string(), "rpath2".to_string(), "rpath1".to_string()]); + let res = minimize_rpaths(&["rpath1".into(), "rpath2".into(), "rpath1".into()]); assert!(res == ["rpath1", "rpath2",]); } #[test] fn test_minimize2() { let res = minimize_rpaths(&[ - "1a".to_string(), - "2".to_string(), - "2".to_string(), - "1a".to_string(), - "4a".to_string(), - "1a".to_string(), - "2".to_string(), - "3".to_string(), - "4a".to_string(), - "3".to_string(), + "1a".into(), + "2".into(), + "2".into(), + "1a".into(), + "4a".into(), + "1a".into(), + "2".into(), + "3".into(), + "4a".into(), + "3".into(), ]); assert!(res == ["1a", "2", "4a", "3",]); } @@ -58,15 +59,15 @@ fn test_rpath_relative() { #[test] fn test_xlinker() { - let args = rpaths_to_flags(&["a/normal/path".to_string(), "a,comma,path".to_string()]); + let args = rpaths_to_flags(vec!["a/normal/path".into(), "a,comma,path".into()]); assert_eq!( args, vec![ - "-Wl,-rpath,a/normal/path".to_string(), - "-Wl,-rpath".to_string(), - "-Xlinker".to_string(), - "a,comma,path".to_string() + OsString::from("-Wl,-rpath,a/normal/path"), + OsString::from("-Wl,-rpath"), + OsString::from("-Xlinker"), + OsString::from("a,comma,path") ] ); } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 92792ab6477..f6936c80b77 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -215,14 +215,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, sym::track_caller => { - if !tcx.is_closure(did.to_def_id()) + let is_closure = tcx.is_closure(did.to_def_id()); + + if !is_closure && let Some(fn_sig) = fn_sig() && fn_sig.skip_binder().abi() != abi::Abi::Rust { struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI") .emit(); } - if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller { + if is_closure + && !tcx.features().closure_track_caller + && !attr.span.allows_unstable(sym::closure_track_caller) + { feature_err( &tcx.sess.parse_sess, sym::closure_track_caller, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index f13dfd96b8e..4f26383ed05 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -862,11 +862,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { .iter() .enumerate() .map(|(i, arg)| { - // The indices passed to simd_shuffle* in the + // The indices passed to simd_shuffle in the // third argument must be constant. This is // checked by const-qualification, which also // promotes any complex rvalues to constants. - if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") { + if i == 2 && intrinsic == sym::simd_shuffle { if let mir::Operand::Constant(constant) = arg { let (llval, ty) = self.simd_shuffle_indices(&bx, constant); return OperandRef { diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 64c6d17469b..e7c3906d977 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -441,7 +441,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { LocalRef::Place(place) => place, LocalRef::UnsizedPlace(place) => bx.load_operand(place).deref(cx), LocalRef::Operand(..) => { - if place_ref.has_deref() { + if place_ref.is_indirect_first_projection() { base = 1; let cg_base = self.codegen_consume( bx, diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 671c2be1de9..0b336109921 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -15,9 +15,6 @@ const_eval_await_non_const = cannot convert `{$ty}` into a future in {const_eval_const_context}s const_eval_bounds_check_failed = indexing out of bounds: the len is {$len} but the index is {$index} -const_eval_box_to_mut = {$front_matter}: encountered a box pointing to mutable memory in a constant -const_eval_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant -const_eval_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty} const_eval_call_nonzero_intrinsic = `{$name}` called on 0 @@ -41,18 +38,12 @@ const_eval_const_context = {$kind -> const_eval_copy_nonoverlapping_overlapping = `copy_nonoverlapping` called on overlapping ranges -const_eval_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance) -const_eval_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation) -const_eval_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free) const_eval_dangling_int_pointer = {$bad_pointer_message}: {$pointer} is a dangling pointer (it has no provenance) const_eval_dangling_null_pointer = {$bad_pointer_message}: null pointer is a dangling pointer (it has no provenance) const_eval_dangling_ptr_in_final = encountered dangling pointer in final constant -const_eval_dangling_ref_no_provenance = {$front_matter}: encountered a dangling reference ({$pointer} has no provenance) -const_eval_dangling_ref_out_of_bounds = {$front_matter}: encountered a dangling reference (going beyond the bounds of its allocation) -const_eval_dangling_ref_use_after_free = {$front_matter}: encountered a dangling reference (use-after-free) const_eval_dead_local = accessing a dead local variable const_eval_dealloc_immutable = @@ -105,7 +96,6 @@ const_eval_error = {$error_kind -> const_eval_exact_div_has_remainder = exact_div: {$a} cannot be divided by {$b} without remainder -const_eval_expected_non_ptr = {$front_matter}: encountered `{$value}`, but expected plain (non-pointer) bytes const_eval_fn_ptr_call = function pointers need an RFC before allowed to be called in {const_eval_const_context}s const_eval_for_loop_into_iter_non_const = @@ -156,8 +146,6 @@ const_eval_invalid_align_details = const_eval_invalid_bool = interpreting an invalid 8-bit value as a bool: 0x{$value} -const_eval_invalid_box_meta = {$front_matter}: encountered invalid box metadata: total size is bigger than largest supported object -const_eval_invalid_box_slice_meta = {$front_matter}: encountered invalid box metadata: slice is bigger than largest supported object const_eval_invalid_char = interpreting an invalid 32-bit value as a char: 0x{$value} const_eval_invalid_dealloc = @@ -168,16 +156,12 @@ const_eval_invalid_dealloc = *[other] {""} } -const_eval_invalid_enum_tag = {$front_matter}: encountered {$value}, but expected a valid enum tag -const_eval_invalid_fn_ptr = {$front_matter}: encountered {$value}, but expected a function pointer const_eval_invalid_function_pointer = using {$pointer} as function pointer but it does not point to a function const_eval_invalid_meta = invalid metadata in wide pointer: total size is bigger than largest supported object const_eval_invalid_meta_slice = invalid metadata in wide pointer: slice is bigger than largest supported object -const_eval_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object -const_eval_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object const_eval_invalid_str = this string is not valid UTF-8: {$err} const_eval_invalid_tag = @@ -189,14 +173,10 @@ const_eval_invalid_uninit_bytes = reading memory at {$alloc}{$access}, but memory is uninitialized at {$uninit}, and this operation requires initialized memory const_eval_invalid_uninit_bytes_unknown = using uninitialized data, but this operation requires initialized memory -const_eval_invalid_value = constructing invalid value -const_eval_invalid_value_with_path = constructing invalid value at {$path} -## The `front_matter`s here refer to either `middle_invalid_value` or `middle_invalid_value_with_path`. const_eval_invalid_vtable_pointer = using {$pointer} as vtable pointer but it does not point to a vtable -const_eval_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer const_eval_live_drop = destructor of `{$dropped_ty}` cannot be evaluated at compile-time @@ -218,14 +198,13 @@ const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant const_eval_memory_access_test = memory access failed const_eval_memory_exhausted = tried to allocate more memory than available to compiler + const_eval_modified_global = modifying a static's initial value from another static's initializer const_eval_mut_deref = mutation through a reference is not allowed in {const_eval_const_context}s -const_eval_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const` -const_eval_never_val = {$front_matter}: encountered a value of the never type `!` const_eval_non_const_fmt_macro_call = cannot call non-const formatting macro in {const_eval_const_context}s @@ -241,10 +220,6 @@ const_eval_noreturn_asm_returned = const_eval_not_enough_caller_args = calling a function with fewer arguments than it requires -const_eval_null_box = {$front_matter}: encountered a null box -const_eval_null_fn_ptr = {$front_matter}: encountered a null function pointer -const_eval_null_ref = {$front_matter}: encountered a null reference -const_eval_nullable_ptr_out_of_range = {$front_matter}: encountered a potentially null pointer, but expected something that cannot possibly fail to be {$in_range} const_eval_nullary_intrinsic_fail = could not evaluate nullary intrinsic @@ -257,7 +232,6 @@ const_eval_offset_from_underflow = const_eval_operator_non_const = cannot call non-const operator in {const_eval_const_context}s -const_eval_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range} const_eval_overflow = overflow executing `{$name}` @@ -282,12 +256,11 @@ const_eval_pointer_out_of_bounds = *[many] bytes } starting at offset {$ptr_offset} is out-of-bounds const_eval_pointer_use_after_free = - pointer to {$allocation} was dereferenced after this allocation got freed + {$bad_pointer_message}: {$alloc_id} has been freed, so this pointer is dangling const_eval_ptr_as_bytes_1 = this code performed an operation that depends on the underlying bytes representing a pointer const_eval_ptr_as_bytes_2 = the absolute address of a pointer is not known at compile-time, so such operations are not supported -const_eval_ptr_out_of_range = {$front_matter}: encountered a pointer, but expected something that cannot possibly fail to be {$in_range} const_eval_question_branch_non_const = `?` cannot determine the branch of `{$ty}` in {const_eval_const_context}s @@ -315,8 +288,8 @@ const_eval_raw_ptr_to_int = const_eval_read_extern_static = cannot read from extern static ({$did}) -const_eval_read_pointer_as_bytes = - unable to turn pointer into raw bytes +const_eval_read_pointer_as_int = + unable to turn pointer into integer const_eval_realloc_or_alloc_with_offset = {$kind -> [dealloc] deallocating @@ -324,9 +297,6 @@ const_eval_realloc_or_alloc_with_offset = *[other] {""} } {$ptr} which does not point to the beginning of an object -const_eval_ref_to_mut = {$front_matter}: encountered a reference pointing to mutable memory in a constant -const_eval_ref_to_static = {$front_matter}: encountered a reference pointing to a static variable in a constant -const_eval_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty} const_eval_remainder_by_zero = calculating the remainder with a divisor of zero const_eval_remainder_overflow = @@ -363,8 +333,6 @@ const_eval_transient_mut_borrow_raw = raw mutable references are not allowed in const_eval_try_block_from_output_non_const = `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s -const_eval_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes}) -const_eval_unaligned_ref = {$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes}) const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s const_eval_unallowed_heap_allocations = @@ -408,29 +376,14 @@ const_eval_undefined_behavior = const_eval_undefined_behavior_note = The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. -const_eval_uninhabited_enum_tag = {$front_matter}: encountered an uninhabited enum variant const_eval_uninhabited_enum_variant_read = read discriminant of an uninhabited enum variant const_eval_uninhabited_enum_variant_written = writing discriminant of an uninhabited enum variant -const_eval_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}` -const_eval_uninit = {$front_matter}: encountered uninitialized bytes -const_eval_uninit_bool = {$front_matter}: encountered uninitialized memory, but expected a boolean -const_eval_uninit_box = {$front_matter}: encountered uninitialized memory, but expected a box -const_eval_uninit_char = {$front_matter}: encountered uninitialized memory, but expected a unicode scalar value -const_eval_uninit_enum_tag = {$front_matter}: encountered uninitialized bytes, but expected a valid enum tag -const_eval_uninit_float = {$front_matter}: encountered uninitialized memory, but expected a floating point number -const_eval_uninit_fn_ptr = {$front_matter}: encountered uninitialized memory, but expected a function pointer -const_eval_uninit_init_scalar = {$front_matter}: encountered uninitialized memory, but expected initialized scalar value -const_eval_uninit_int = {$front_matter}: encountered uninitialized memory, but expected an integer -const_eval_uninit_raw_ptr = {$front_matter}: encountered uninitialized memory, but expected a raw pointer -const_eval_uninit_ref = {$front_matter}: encountered uninitialized memory, but expected a reference -const_eval_uninit_str = {$front_matter}: encountered uninitialized data in `str` const_eval_unreachable = entering unreachable code const_eval_unreachable_unwind = unwinding past a stack frame that does not allow unwinding -const_eval_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in a `const` const_eval_unsigned_offset_from_overflow = `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: {$a_offset} < {$b_offset} @@ -453,8 +406,63 @@ const_eval_unwind_past_top = const_eval_upcast_mismatch = upcast on a pointer whose vtable does not match its type +## The `front_matter`s here refer to either `const_eval_front_matter_invalid_value` or `const_eval_front_matter_invalid_value_with_path`. +## (We'd love to sort this differently to make that more clear but tidy won't let us...) +const_eval_validation_box_to_mut = {$front_matter}: encountered a box pointing to mutable memory in a constant +const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant +const_eval_validation_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty} +const_eval_validation_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance) +const_eval_validation_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation) +const_eval_validation_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free) +const_eval_validation_dangling_ref_no_provenance = {$front_matter}: encountered a dangling reference ({$pointer} has no provenance) +const_eval_validation_dangling_ref_out_of_bounds = {$front_matter}: encountered a dangling reference (going beyond the bounds of its allocation) +const_eval_validation_dangling_ref_use_after_free = {$front_matter}: encountered a dangling reference (use-after-free) + +const_eval_validation_expected_bool = expected a boolean +const_eval_validation_expected_box = expected a box +const_eval_validation_expected_char = expected a unicode scalar value +const_eval_validation_expected_enum_tag = expected a valid enum tag +const_eval_validation_expected_float = expected a floating point number +const_eval_validation_expected_fn_ptr = expected a function pointer +const_eval_validation_expected_init_scalar = expected initialized scalar value +const_eval_validation_expected_int = expected an integer +const_eval_validation_expected_raw_ptr = expected a raw pointer +const_eval_validation_expected_ref = expected a reference +const_eval_validation_expected_str = expected a string + +const_eval_validation_front_matter_invalid_value = constructing invalid value +const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path} + const_eval_validation_invalid_bool = {$front_matter}: encountered {$value}, but expected a boolean +const_eval_validation_invalid_box_meta = {$front_matter}: encountered invalid box metadata: total size is bigger than largest supported object +const_eval_validation_invalid_box_slice_meta = {$front_matter}: encountered invalid box metadata: slice is bigger than largest supported object const_eval_validation_invalid_char = {$front_matter}: encountered {$value}, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`) + +const_eval_validation_invalid_enum_tag = {$front_matter}: encountered {$value}, but expected a valid enum tag +const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, but expected a function pointer +const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object +const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object +const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer +const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const` +const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` +const_eval_validation_null_box = {$front_matter}: encountered a null box +const_eval_validation_null_fn_ptr = {$front_matter}: encountered a null function pointer +const_eval_validation_null_ref = {$front_matter}: encountered a null reference +const_eval_validation_nullable_ptr_out_of_range = {$front_matter}: encountered a potentially null pointer, but expected something that cannot possibly fail to be {$in_range} +const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range} +const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers +const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected} +const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer, but expected something that cannot possibly fail to be {$in_range} +const_eval_validation_ref_to_mut = {$front_matter}: encountered a reference pointing to mutable memory in a constant +const_eval_validation_ref_to_static = {$front_matter}: encountered a reference pointing to a static variable in a constant +const_eval_validation_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty} +const_eval_validation_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes}) +const_eval_validation_unaligned_ref = {$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes}) +const_eval_validation_uninhabited_enum_variant = {$front_matter}: encountered an uninhabited enum variant +const_eval_validation_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}` +const_eval_validation_uninit = {$front_matter}: encountered uninitialized memory, but {$expected} +const_eval_validation_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in a `const` + const_eval_write_to_read_only = writing to {$allocation} which is read-only const_eval_zst_pointer_out_of_bounds = diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 6630eeca27e..032f4be6c99 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -214,9 +214,9 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { // &str or &&str assert!(args.len() == 1); - let mut msg_place = self.deref_operand(&args[0])?; + let mut msg_place = self.deref_pointer(&args[0])?; while msg_place.layout.ty.is_ref() { - msg_place = self.deref_operand(&msg_place)?; + msg_place = self.deref_pointer(&msg_place)?; } let msg = Symbol::intern(self.read_str(&msg_place)?); diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 7c1dbddfc26..f785bcfed6c 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -102,7 +102,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>( ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType), ty::Ref(_, _, _) => { - let Ok(derefd_place)= ecx.deref_operand(place) else { + let Ok(derefd_place)= ecx.deref_pointer(place) else { return Err(ValTreeCreationError::Other); }; debug!(?derefd_place); diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index e1109e584b7..3a4e2648b80 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -492,7 +492,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { InvalidMeta(InvalidMetaKind::SliceTooBig) => const_eval_invalid_meta_slice, InvalidMeta(InvalidMetaKind::TooBig) => const_eval_invalid_meta, UnterminatedCString(_) => const_eval_unterminated_c_string, - PointerUseAfterFree(_) => const_eval_pointer_use_after_free, + PointerUseAfterFree(_, _) => const_eval_pointer_use_after_free, PointerOutOfBounds { ptr_size: Size::ZERO, .. } => const_eval_zst_pointer_out_of_bounds, PointerOutOfBounds { .. } => const_eval_pointer_out_of_bounds, DanglingIntPointer(0, _) => const_eval_dangling_null_pointer, @@ -513,7 +513,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch, UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written, UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read, - Validation(e) => e.diagnostic_message(), + ValidationError(e) => e.diagnostic_message(), Custom(x) => (x.msg)(), } } @@ -545,8 +545,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => { builder.set_arg("pointer", ptr); } - PointerUseAfterFree(allocation) => { - builder.set_arg("allocation", allocation); + PointerUseAfterFree(alloc_id, msg) => { + builder + .set_arg("alloc_id", alloc_id) + .set_arg("bad_pointer_message", bad_pointer_message(msg, handler)); } PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => { builder @@ -585,13 +587,13 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { InvalidUninitBytes(Some((alloc, info))) => { builder.set_arg("alloc", alloc); builder.set_arg("access", info.access); - builder.set_arg("uninit", info.uninit); + builder.set_arg("uninit", info.bad); } ScalarSizeMismatch(info) => { builder.set_arg("target_size", info.target_size); builder.set_arg("data_size", info.data_size); } - Validation(e) => e.add_args(handler, builder), + ValidationError(e) => e.add_args(handler, builder), Custom(custom) => { (custom.add_args)(&mut |name, value| { builder.set_arg(name, value); @@ -606,74 +608,72 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { use crate::fluent_generated::*; use rustc_middle::mir::interpret::ValidationErrorKind::*; match self.kind { - PtrToUninhabited { ptr_kind: PointerKind::Box, .. } => const_eval_box_to_uninhabited, - PtrToUninhabited { ptr_kind: PointerKind::Ref, .. } => const_eval_ref_to_uninhabited, - - PtrToStatic { ptr_kind: PointerKind::Box } => const_eval_box_to_static, - PtrToStatic { ptr_kind: PointerKind::Ref } => const_eval_ref_to_static, - - PtrToMut { ptr_kind: PointerKind::Box } => const_eval_box_to_mut, - PtrToMut { ptr_kind: PointerKind::Ref } => const_eval_ref_to_mut, - - ExpectedNonPtr { .. } => const_eval_expected_non_ptr, - MutableRefInConst => const_eval_mutable_ref_in_const, - NullFnPtr => const_eval_null_fn_ptr, - NeverVal => const_eval_never_val, - NullablePtrOutOfRange { .. } => const_eval_nullable_ptr_out_of_range, - PtrOutOfRange { .. } => const_eval_ptr_out_of_range, - OutOfRange { .. } => const_eval_out_of_range, - UnsafeCell => const_eval_unsafe_cell, - UninhabitedVal { .. } => const_eval_uninhabited_val, - InvalidEnumTag { .. } => const_eval_invalid_enum_tag, - UninhabitedEnumTag => const_eval_uninhabited_enum_tag, - UninitEnumTag => const_eval_uninit_enum_tag, - UninitStr => const_eval_uninit_str, - Uninit { expected: ExpectedKind::Bool } => const_eval_uninit_bool, - Uninit { expected: ExpectedKind::Reference } => const_eval_uninit_ref, - Uninit { expected: ExpectedKind::Box } => const_eval_uninit_box, - Uninit { expected: ExpectedKind::RawPtr } => const_eval_uninit_raw_ptr, - Uninit { expected: ExpectedKind::InitScalar } => const_eval_uninit_init_scalar, - Uninit { expected: ExpectedKind::Char } => const_eval_uninit_char, - Uninit { expected: ExpectedKind::Float } => const_eval_uninit_float, - Uninit { expected: ExpectedKind::Int } => const_eval_uninit_int, - Uninit { expected: ExpectedKind::FnPtr } => const_eval_uninit_fn_ptr, - UninitVal => const_eval_uninit, - InvalidVTablePtr { .. } => const_eval_invalid_vtable_ptr, + PtrToUninhabited { ptr_kind: PointerKind::Box, .. } => { + const_eval_validation_box_to_uninhabited + } + PtrToUninhabited { ptr_kind: PointerKind::Ref, .. } => { + const_eval_validation_ref_to_uninhabited + } + + PtrToStatic { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_static, + PtrToStatic { ptr_kind: PointerKind::Ref } => const_eval_validation_ref_to_static, + + PtrToMut { ptr_kind: PointerKind::Box } => const_eval_validation_box_to_mut, + PtrToMut { ptr_kind: PointerKind::Ref } => const_eval_validation_ref_to_mut, + + PointerAsInt { .. } => const_eval_validation_pointer_as_int, + PartialPointer => const_eval_validation_partial_pointer, + MutableRefInConst => const_eval_validation_mutable_ref_in_const, + NullFnPtr => const_eval_validation_null_fn_ptr, + NeverVal => const_eval_validation_never_val, + NullablePtrOutOfRange { .. } => const_eval_validation_nullable_ptr_out_of_range, + PtrOutOfRange { .. } => const_eval_validation_ptr_out_of_range, + OutOfRange { .. } => const_eval_validation_out_of_range, + UnsafeCell => const_eval_validation_unsafe_cell, + UninhabitedVal { .. } => const_eval_validation_uninhabited_val, + InvalidEnumTag { .. } => const_eval_validation_invalid_enum_tag, + UninhabitedEnumVariant => const_eval_validation_uninhabited_enum_variant, + Uninit { .. } => const_eval_validation_uninit, + InvalidVTablePtr { .. } => const_eval_validation_invalid_vtable_ptr, InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => { - const_eval_invalid_box_slice_meta + const_eval_validation_invalid_box_slice_meta } InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Ref } => { - const_eval_invalid_ref_slice_meta + const_eval_validation_invalid_ref_slice_meta } - InvalidMetaTooLarge { ptr_kind: PointerKind::Box } => const_eval_invalid_box_meta, - InvalidMetaTooLarge { ptr_kind: PointerKind::Ref } => const_eval_invalid_ref_meta, - UnalignedPtr { ptr_kind: PointerKind::Ref, .. } => const_eval_unaligned_ref, - UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_unaligned_box, + InvalidMetaTooLarge { ptr_kind: PointerKind::Box } => { + const_eval_validation_invalid_box_meta + } + InvalidMetaTooLarge { ptr_kind: PointerKind::Ref } => { + const_eval_validation_invalid_ref_meta + } + UnalignedPtr { ptr_kind: PointerKind::Ref, .. } => const_eval_validation_unaligned_ref, + UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_unaligned_box, - NullPtr { ptr_kind: PointerKind::Box } => const_eval_null_box, - NullPtr { ptr_kind: PointerKind::Ref } => const_eval_null_ref, + NullPtr { ptr_kind: PointerKind::Box } => const_eval_validation_null_box, + NullPtr { ptr_kind: PointerKind::Ref } => const_eval_validation_null_ref, DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => { - const_eval_dangling_box_no_provenance + const_eval_validation_dangling_box_no_provenance } DanglingPtrNoProvenance { ptr_kind: PointerKind::Ref, .. } => { - const_eval_dangling_ref_no_provenance + const_eval_validation_dangling_ref_no_provenance } DanglingPtrOutOfBounds { ptr_kind: PointerKind::Box } => { - const_eval_dangling_box_out_of_bounds + const_eval_validation_dangling_box_out_of_bounds } DanglingPtrOutOfBounds { ptr_kind: PointerKind::Ref } => { - const_eval_dangling_ref_out_of_bounds + const_eval_validation_dangling_ref_out_of_bounds } DanglingPtrUseAfterFree { ptr_kind: PointerKind::Box } => { - const_eval_dangling_box_use_after_free + const_eval_validation_dangling_box_use_after_free } DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref } => { - const_eval_dangling_ref_use_after_free + const_eval_validation_dangling_ref_use_after_free } InvalidBool { .. } => const_eval_validation_invalid_bool, InvalidChar { .. } => const_eval_validation_invalid_char, - InvalidFnPtr { .. } => const_eval_invalid_fn_ptr, + InvalidFnPtr { .. } => const_eval_validation_invalid_fn_ptr, } } @@ -681,13 +681,21 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { use crate::fluent_generated as fluent; use rustc_middle::mir::interpret::ValidationErrorKind::*; + if let PointerAsInt { .. } | PartialPointer = self.kind { + err.help(fluent::const_eval_ptr_as_bytes_1); + err.help(fluent::const_eval_ptr_as_bytes_2); + } + let message = if let Some(path) = self.path { handler.eagerly_translate_to_string( - fluent::const_eval_invalid_value_with_path, + fluent::const_eval_validation_front_matter_invalid_value_with_path, [("path".into(), DiagnosticArgValue::Str(path.into()))].iter().map(|(a, b)| (a, b)), ) } else { - handler.eagerly_translate_to_string(fluent::const_eval_invalid_value, [].into_iter()) + handler.eagerly_translate_to_string( + fluent::const_eval_validation_front_matter_invalid_value, + [].into_iter(), + ) }; err.set_arg("front_matter", message); @@ -727,8 +735,24 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { PtrToUninhabited { ty, .. } | UninhabitedVal { ty } => { err.set_arg("ty", ty); } - ExpectedNonPtr { value } - | InvalidEnumTag { value } + PointerAsInt { expected } | Uninit { expected } => { + let msg = match expected { + ExpectedKind::Reference => fluent::const_eval_validation_expected_ref, + ExpectedKind::Box => fluent::const_eval_validation_expected_box, + ExpectedKind::RawPtr => fluent::const_eval_validation_expected_raw_ptr, + ExpectedKind::InitScalar => fluent::const_eval_validation_expected_init_scalar, + ExpectedKind::Bool => fluent::const_eval_validation_expected_bool, + ExpectedKind::Char => fluent::const_eval_validation_expected_char, + ExpectedKind::Float => fluent::const_eval_validation_expected_float, + ExpectedKind::Int => fluent::const_eval_validation_expected_int, + ExpectedKind::FnPtr => fluent::const_eval_validation_expected_fn_ptr, + ExpectedKind::EnumTag => fluent::const_eval_validation_expected_enum_tag, + ExpectedKind::Str => fluent::const_eval_validation_expected_str, + }; + let msg = handler.eagerly_translate_to_string(msg, [].into_iter()); + err.set_arg("expected", msg); + } + InvalidEnumTag { value } | InvalidVTablePtr { value } | InvalidBool { value } | InvalidChar { value } @@ -756,15 +780,12 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { | NullFnPtr | NeverVal | UnsafeCell - | UninitEnumTag - | UninitStr - | Uninit { .. } - | UninitVal | InvalidMetaSliceTooLarge { .. } | InvalidMetaTooLarge { .. } | DanglingPtrUseAfterFree { .. } | DanglingPtrOutOfBounds { .. } - | UninhabitedEnumTag => {} + | UninhabitedEnumVariant + | PartialPointer => {} } } } @@ -774,9 +795,9 @@ impl ReportErrorExt for UnsupportedOpInfo { use crate::fluent_generated::*; match self { UnsupportedOpInfo::Unsupported(s) => s.clone().into(), - UnsupportedOpInfo::PartialPointerOverwrite(_) => const_eval_partial_pointer_overwrite, - UnsupportedOpInfo::PartialPointerCopy(_) => const_eval_partial_pointer_copy, - UnsupportedOpInfo::ReadPointerAsBytes => const_eval_read_pointer_as_bytes, + UnsupportedOpInfo::OverwritePartialPointer(_) => const_eval_partial_pointer_overwrite, + UnsupportedOpInfo::ReadPartialPointer(_) => const_eval_partial_pointer_copy, + UnsupportedOpInfo::ReadPointerAsInt(_) => const_eval_read_pointer_as_int, UnsupportedOpInfo::ThreadLocalStatic(_) => const_eval_thread_local_static, UnsupportedOpInfo::ReadExternStatic(_) => const_eval_read_extern_static, } @@ -785,13 +806,16 @@ impl ReportErrorExt for UnsupportedOpInfo { use crate::fluent_generated::*; use UnsupportedOpInfo::*; - if let ReadPointerAsBytes | PartialPointerOverwrite(_) | PartialPointerCopy(_) = self { + if let ReadPointerAsInt(_) | OverwritePartialPointer(_) | ReadPartialPointer(_) = self { builder.help(const_eval_ptr_as_bytes_1); builder.help(const_eval_ptr_as_bytes_2); } match self { - Unsupported(_) | ReadPointerAsBytes => {} - PartialPointerOverwrite(ptr) | PartialPointerCopy(ptr) => { + // `ReadPointerAsInt(Some(info))` is never printed anyway, it only serves as an error to + // be further processed by validity checking which then turns it into something nice to + // print. So it's not worth the effort of having diagnostics that can print the `info`. + Unsupported(_) | ReadPointerAsInt(_) => {} + OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => { builder.set_arg("ptr", ptr); } ThreadLocalStatic(did) | ReadExternStatic(did) => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index be7c14f33c2..f22cd919c36 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -1,4 +1,4 @@ -//! Intrinsics and other functions that the miri engine executes without +//! Intrinsics and other functions that the interpreter executes without //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE //! and miri. @@ -144,7 +144,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::min_align_of_val | sym::size_of_val => { - // Avoid `deref_operand` -- this is not a deref, the ptr does not have to be + // Avoid `deref_pointer` -- this is not a deref, the ptr does not have to be // dereferenceable! let place = self.ref_to_mplace(&self.read_immediate(&args[0])?)?; let (size, align) = self @@ -225,7 +225,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.write_scalar(val, dest)?; } sym::discriminant_value => { - let place = self.deref_operand(&args[0])?; + let place = self.deref_pointer(&args[0])?; let variant = self.read_discriminant(&place)?; let discr = self.discriminant_for_variant(place.layout, variant)?; self.write_scalar(discr, dest)?; @@ -261,6 +261,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::write_bytes => { self.write_bytes_intrinsic(&args[0], &args[1], &args[2])?; } + sym::compare_bytes => { + let result = self.compare_bytes_intrinsic(&args[0], &args[1], &args[2])?; + self.write_scalar(result, dest)?; + } sym::arith_offset => { let ptr = self.read_pointer(&args[0])?; let offset_count = self.read_target_isize(&args[1])?; @@ -643,6 +647,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.write_bytes_ptr(dst, bytes) } + pub(crate) fn compare_bytes_intrinsic( + &mut self, + left: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + right: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + byte_count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + ) -> InterpResult<'tcx, Scalar<M::Provenance>> { + let left = self.read_pointer(left)?; + let right = self.read_pointer(right)?; + let n = Size::from_bytes(self.read_target_usize(byte_count)?); + + let left_bytes = self.read_bytes_ptr_strip_provenance(left, n)?; + let right_bytes = self.read_bytes_ptr_strip_provenance(right, n)?; + + // `Ordering`'s discriminants are -1/0/+1, so casting does the right thing. + let result = Ord::cmp(left_bytes, right_bytes) as i32; + Ok(Scalar::from_i32(result)) + } + pub(crate) fn raw_eq_intrinsic( &mut self, lhs: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 02d022a2252..11bffedf54a 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -91,7 +91,7 @@ impl<'tcx, Other> FnVal<'tcx, Other> { // `Memory` has to depend on the `Machine` because some of its operations // (e.g., `get`) call a `Machine` hook. pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> { - /// Allocations local to this instance of the miri engine. The kind + /// Allocations local to this instance of the interpreter. The kind /// helps ensure that the same mechanism is used for allocation and /// deallocation. When an allocation is not found here, it is a /// global and looked up in the `tcx` for read access. Some machines may @@ -317,7 +317,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { kind = "static_mem" ) } - None => err_ub!(PointerUseAfterFree(alloc_id)), + None => err_ub!(PointerUseAfterFree(alloc_id, CheckInAllocMsg::MemoryAccessTest)), } .into()); }; @@ -380,7 +380,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { M::enforce_alignment(self), CheckInAllocMsg::MemoryAccessTest, |alloc_id, offset, prov| { - let (size, align) = self.get_live_alloc_size_and_align(alloc_id)?; + let (size, align) = self + .get_live_alloc_size_and_align(alloc_id, CheckInAllocMsg::MemoryAccessTest)?; Ok((size, align, (alloc_id, offset, prov))) }, ) @@ -404,7 +405,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { CheckAlignment::Error, msg, |alloc_id, _, _| { - let (size, align) = self.get_live_alloc_size_and_align(alloc_id)?; + let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?; Ok((size, align, ())) }, )?; @@ -414,7 +415,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Low-level helper function to check if a ptr is in-bounds and potentially return a reference /// to the allocation it points to. Supports both shared and mutable references, as the actual /// checking is offloaded to a helper closure. `align` defines whether and which alignment check - /// is done. Returns `None` for size 0, and otherwise `Some` of what `alloc_size` returned. + /// is done. + /// + /// If this returns `None`, the size is 0; it can however return `Some` even for size 0. fn check_and_deref_ptr<T>( &self, ptr: Pointer<Option<M::Provenance>>, @@ -515,7 +518,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)), - None => throw_ub!(PointerUseAfterFree(id)), + None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccessTest)), Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); assert!(!self.tcx.is_thread_local_static(def_id)); @@ -761,11 +764,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - /// Obtain the size and alignment of a live allocation. - pub fn get_live_alloc_size_and_align(&self, id: AllocId) -> InterpResult<'tcx, (Size, Align)> { + /// Obtain the size and alignment of a *live* allocation. + fn get_live_alloc_size_and_align( + &self, + id: AllocId, + msg: CheckInAllocMsg, + ) -> InterpResult<'tcx, (Size, Align)> { let (size, align, kind) = self.get_alloc_info(id); if matches!(kind, AllocKind::Dead) { - throw_ub!(PointerUseAfterFree(id)) + throw_ub!(PointerUseAfterFree(id, msg)) } Ok((size, align)) } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 96a960118ce..ec226808f1b 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -157,7 +157,6 @@ impl<Prov: Provenance> MemPlace<Prov> { } /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space. - /// This is the inverse of `ref_to_mplace`. #[inline(always)] pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> { match self.meta { @@ -415,11 +414,11 @@ where } /// Take a value, which represents a (thin or wide) reference, and make it a place. - /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. + /// Alignment is just based on the type. This is the inverse of `mplace_to_ref()`. /// /// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not /// want to ever use the place for memory access! - /// Generally prefer `deref_operand`. + /// Generally prefer `deref_pointer`. pub fn ref_to_mplace( &self, val: &ImmTy<'tcx, M::Provenance>, @@ -438,9 +437,22 @@ where Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.to_pointer(self)?, layout, meta)) } + /// Turn a mplace into a (thin or wide) mutable raw pointer, pointing to the same space. + /// `align` information is lost! + /// This is the inverse of `ref_to_mplace`. + pub fn mplace_to_ref( + &self, + mplace: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { + let imm = mplace.to_ref(self); + let layout = self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, mplace.layout.ty))?; + Ok(ImmTy::from_immediate(imm, layout)) + } + /// Take an operand, representing a pointer, and dereference it to a place. + /// Corresponds to the `*` operator in Rust. #[instrument(skip(self), level = "debug")] - pub fn deref_operand( + pub fn deref_pointer( &self, src: &impl Readable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { @@ -814,10 +826,10 @@ where // predicate like `where Self: Sized` with `Self = dyn Trait`. // See #102553 for an example of such a predicate. if src.layout().is_unsized() { - throw_inval!(SizeOfUnsizedType(src.layout().ty)); + throw_inval!(ConstPropNonsense); } if dest.layout().is_unsized() { - throw_inval!(SizeOfUnsizedType(dest.layout().ty)); + throw_inval!(ConstPropNonsense); } assert_eq!(src.layout().size, dest.layout().size); // Yay, we got a value that we can write directly. diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index bce43aedb69..882097ad2c3 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -101,7 +101,7 @@ where let (meta, offset) = if field_layout.is_unsized() { if base.layout().is_sized() { // An unsized field of a sized type? Sure... - // But const-prop actually feeds us such nonsense MIR! + // But const-prop actually feeds us such nonsense MIR! (see test `const_prop/issue-86351.rs`) throw_inval!(ConstPropNonsense); } let base_meta = base.meta(self)?; @@ -290,7 +290,7 @@ where OpaqueCast(ty) => base.transmute(self.layout_of(ty)?, self)?, Field(field, _) => self.project_field(base, field.index())?, Downcast(_, variant) => self.project_downcast(base, variant)?, - Deref => self.deref_operand(&base.to_op(self)?)?.into(), + Deref => self.deref_pointer(&base.to_op(self)?)?.into(), Index(local) => { let layout = self.layout_of(self.tcx.types.usize)?; let n = self.local_to_op(self.frame(), local, Some(layout))?; diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index ead172f04a3..f04c73105d2 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -224,8 +224,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Len(place) => { let src = self.eval_place(place)?; - let op = self.place_to_op(&src)?; - let len = op.len(self)?; + let len = src.len(self)?; self.write_scalar(Scalar::from_target_usize(len, self), &dest)?; } @@ -248,7 +247,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { AddressOf(_, place) => { // Figure out whether this is an addr_of of an already raw place. - let place_base_raw = if place.has_deref() { + let place_base_raw = if place.is_indirect_first_projection() { let ty = self.frame().body.local_decls[place.local].ty; ty.is_unsafe_ptr() } else { @@ -270,7 +269,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty)?; let layout = self.layout_of(ty)?; if let mir::NullOp::SizeOf | mir::NullOp::AlignOf = null_op && layout.is_unsized() { - // FIXME: This should be a span_bug (#80742) + // FIXME: This should be a span_bug, but const generics can run MIR + // that is not properly type-checked yet (#97477). self.tcx.sess.delay_span_bug( self.frame().current_span(), format!("{null_op:?} MIR operator called for unsized type {ty}"), diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index d0191ea978a..1bd21473182 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -661,7 +661,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let receiver_place = loop { match receiver.layout.ty.kind() { ty::Ref(..) | ty::RawPtr(..) => { - // We do *not* use `deref_operand` here: we don't want to conceptually + // We do *not* use `deref_pointer` here: we don't want to conceptually // create a place that must be dereferenceable, since the receiver might // be a raw pointer and (for `*const dyn Trait`) we don't need to // actually access memory to resolve this method. @@ -852,10 +852,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; - let arg = ImmTy::from_immediate( - place.to_ref(self), - self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, place.layout.ty))?, - ); + let arg = self.mplace_to_ref(&place)?; let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?); self.eval_fn_call( diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index aee95f70bc2..d3f05af1c72 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -25,13 +25,17 @@ use rustc_target::abi::{ use std::hash::Hash; -// for the validation errors -use super::UndefinedBehaviorInfo::*; use super::{ AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor, }; +// for the validation errors +use super::InterpError::UndefinedBehavior as Ub; +use super::InterpError::Unsupported as Unsup; +use super::UndefinedBehaviorInfo::*; +use super::UnsupportedOpInfo::*; + macro_rules! throw_validation_failure { ($where:expr, $kind: expr) => {{ let where_ = &$where; @@ -43,7 +47,7 @@ macro_rules! throw_validation_failure { None }; - throw_ub!(Validation(ValidationErrorInfo { path, kind: $kind })) + throw_ub!(ValidationError(ValidationErrorInfo { path, kind: $kind })) }}; } @@ -85,16 +89,16 @@ macro_rules! try_validation { Ok(x) => x, // We catch the error and turn it into a validation failure. We are okay with // allocation here as this can only slow down builds that fail anyway. - Err(e) => match e.into_parts() { + Err(e) => match e.kind() { $( - (InterpError::UndefinedBehavior($($p)|+), _) => + $($p)|+ => throw_validation_failure!( $where, $kind ) ),+, #[allow(unreachable_patterns)] - (e, rest) => Err::<!, _>($crate::interpret::InterpErrorInfo::from_parts(e, rest))?, + _ => Err::<!, _>(e)?, } } }}; @@ -294,7 +298,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' Ok(try_validation!( self.ecx.read_immediate(op), self.path, - InvalidUninitBytes(None) => Uninit { expected } + Ub(InvalidUninitBytes(None)) => + Uninit { expected }, + // The `Unsup` cases can only occur during CTFE + Unsup(ReadPointerAsInt(_)) => + PointerAsInt { expected }, + Unsup(ReadPartialPointer(_)) => + PartialPointer, )) } @@ -319,8 +329,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' let (_ty, _trait) = try_validation!( self.ecx.get_ptr_vtable(vtable), self.path, - DanglingIntPointer(..) | - InvalidVTablePointer(..) => InvalidVTablePtr { value: format!("{vtable}") } + Ub(DanglingIntPointer(..) | InvalidVTablePointer(..)) => + InvalidVTablePtr { value: format!("{vtable}") } ); // FIXME: check if the type/trait match what ty::Dynamic says? } @@ -345,6 +355,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' value: &OpTy<'tcx, M::Provenance>, ptr_kind: PointerKind, ) -> InterpResult<'tcx> { + // Not using `deref_pointer` since we do the dereferenceable check ourselves below. let place = self.ecx.ref_to_mplace(&self.read_immediate(value, ptr_kind.into())?)?; // Handle wide pointers. // Check metadata early, for better diagnostics @@ -355,7 +366,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' let size_and_align = try_validation!( self.ecx.size_and_align_of_mplace(&place), self.path, - InvalidMeta(msg) => match msg { + Ub(InvalidMeta(msg)) => match msg { InvalidMetaKind::SliceTooBig => InvalidMetaSliceTooLarge { ptr_kind }, InvalidMetaKind::TooBig => InvalidMetaTooLarge { ptr_kind }, } @@ -374,23 +385,23 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message ), self.path, - AlignmentCheckFailed { required, has } => UnalignedPtr { + Ub(AlignmentCheckFailed { required, has }) => UnalignedPtr { ptr_kind, required_bytes: required.bytes(), found_bytes: has.bytes() }, - DanglingIntPointer(0, _) => NullPtr { ptr_kind }, - DanglingIntPointer(i, _) => DanglingPtrNoProvenance { + Ub(DanglingIntPointer(0, _)) => NullPtr { ptr_kind }, + Ub(DanglingIntPointer(i, _)) => DanglingPtrNoProvenance { ptr_kind, // FIXME this says "null pointer" when null but we need translate - pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(i)) + pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(*i)) }, - PointerOutOfBounds { .. } => DanglingPtrOutOfBounds { + Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds { ptr_kind }, // This cannot happen during const-eval (because interning already detects // dangling pointers), but it can happen in Miri. - PointerUseAfterFree(..) => DanglingPtrUseAfterFree { + Ub(PointerUseAfterFree(..)) => DanglingPtrUseAfterFree { ptr_kind, }, ); @@ -476,7 +487,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' try_validation!( value.to_bool(), self.path, - InvalidBool(..) => ValidationErrorKind::InvalidBool { + Ub(InvalidBool(..)) => ValidationErrorKind::InvalidBool { value: format!("{value:x}"), } ); @@ -487,7 +498,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' try_validation!( value.to_char(), self.path, - InvalidChar(..) => ValidationErrorKind::InvalidChar { + Ub(InvalidChar(..)) => ValidationErrorKind::InvalidChar { value: format!("{value:x}"), } ); @@ -496,7 +507,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ty::Float(_) | ty::Int(_) | ty::Uint(_) => { // NOTE: Keep this in sync with the array optimization for int/float // types below! - let value = self.read_scalar( + self.read_scalar( value, if matches!(ty.kind(), ty::Float(..)) { ExpectedKind::Float @@ -504,20 +515,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ExpectedKind::Int }, )?; - // As a special exception we *do* match on a `Scalar` here, since we truly want - // to know its underlying representation (and *not* cast it to an integer). - if matches!(value, Scalar::Ptr(..)) { - throw_validation_failure!( - self.path, - ExpectedNonPtr { value: format!("{value:x}") } - ) - } Ok(true) } ty::RawPtr(..) => { - // We are conservative with uninit for integers, but try to - // actually enforce the strict rules for raw pointers (mostly because - // that lets us re-use `ref_to_mplace`). let place = self.ecx.ref_to_mplace(&self.read_immediate(value, ExpectedKind::RawPtr)?)?; if place.layout.is_unsized() { @@ -548,10 +548,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' let _fn = try_validation!( self.ecx.get_ptr_fn(ptr), self.path, - DanglingIntPointer(..) | - InvalidFunctionPointer(..) => InvalidFnPtr { - value: format!("{ptr}"), - }, + Ub(DanglingIntPointer(..) | InvalidFunctionPointer(..)) => + InvalidFnPtr { value: format!("{ptr}") }, ); // FIXME: Check if the signature matches } else { @@ -659,11 +657,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> Ok(try_validation!( this.ecx.read_discriminant(op), this.path, - InvalidTag(val) => InvalidEnumTag { + Ub(InvalidTag(val)) => InvalidEnumTag { value: format!("{val:x}"), }, - UninhabitedEnumVariantRead(_) => UninhabitedEnumTag, - InvalidUninitBytes(None) => UninitEnumTag, + Ub(UninhabitedEnumVariantRead(_)) => UninhabitedEnumVariant, + // Uninit / bad provenance are not possible since the field was already previously + // checked at its integer type. )) }) } @@ -742,7 +741,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> try_validation!( self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr, Size::from_bytes(len)), self.path, - InvalidUninitBytes(..) => { UninitStr }, + Ub(InvalidUninitBytes(..)) => Uninit { expected: ExpectedKind::Str }, + Unsup(ReadPointerAsInt(_)) => PointerAsInt { expected: ExpectedKind::Str } ); } ty::Array(tys, ..) | ty::Slice(tys) @@ -754,6 +754,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> if matches!(tys.kind(), ty::Int(..) | ty::Uint(..) | ty::Float(..)) => { + let expected = if tys.is_integral() { ExpectedKind::Int } else { ExpectedKind::Float }; // Optimized handling for arrays of integer/float type. // This is the length of the array/slice. @@ -772,7 +773,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> Left(mplace) => mplace, Right(imm) => match *imm { Immediate::Uninit => - throw_validation_failure!(self.path, UninitVal), + throw_validation_failure!(self.path, Uninit { expected }), Immediate::Scalar(..) | Immediate::ScalarPair(..) => bug!("arrays/slices can never have Scalar/ScalarPair layout"), } @@ -798,17 +799,21 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // For some errors we might be able to provide extra information. // (This custom logic does not fit the `try_validation!` macro.) match err.kind() { - err_ub!(InvalidUninitBytes(Some((_alloc_id, access)))) => { + Ub(InvalidUninitBytes(Some((_alloc_id, access)))) | Unsup(ReadPointerAsInt(Some((_alloc_id, access)))) => { // Some byte was uninitialized, determine which // element that byte belongs to so we can // provide an index. let i = usize::try_from( - access.uninit.start.bytes() / layout.size.bytes(), + access.bad.start.bytes() / layout.size.bytes(), ) .unwrap(); self.path.push(PathElem::ArrayElem(i)); - throw_validation_failure!(self.path, UninitVal) + if matches!(err.kind(), Ub(InvalidUninitBytes(_))) { + throw_validation_failure!(self.path, Uninit { expected }) + } else { + throw_validation_failure!(self.path, PointerAsInt { expected }) + } } // Propagate upwards (that will also check for unexpected errors). @@ -894,17 +899,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Run it. match visitor.visit_value(&op) { Ok(()) => Ok(()), - // Pass through validation failures. - Err(err) if matches!(err.kind(), err_ub!(Validation { .. })) => Err(err), - // Complain about any other kind of UB error -- those are bad because we'd like to + // Pass through validation failures and "invalid program" issues. + Err(err) + if matches!( + err.kind(), + err_ub!(ValidationError { .. }) | InterpError::InvalidProgram(_) + ) => + { + Err(err) + } + // Complain about any other kind of error -- those are bad because we'd like to // report them in a way that shows *where* in the value the issue lies. - Err(err) if matches!(err.kind(), InterpError::UndefinedBehavior(_)) => { + Err(err) => { let (err, backtrace) = err.into_parts(); backtrace.print_backtrace(); bug!("Unexpected Undefined Behavior error during validation: {err:?}"); } - // Pass through everything else. - Err(err) => Err(err), } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index ad5ffa6511f..b077c10907e 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -746,6 +746,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } // Attempting to call a trait method? + // FIXME(effects) do we need this? if let Some(trait_id) = tcx.trait_of_item(callee) { trace!("attempting to call a trait method"); if !self.tcx.features().const_trait_impl { @@ -761,7 +762,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } let trait_ref = TraitRef::from_method(tcx, trait_id, fn_args); - let trait_ref = trait_ref.with_constness(ty::BoundConstness::ConstIfConst); let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index b152644a551..8293bdfd969 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -155,12 +155,12 @@ impl Qualif for NeedsNonConstDrop { return false; } + // FIXME(effects) constness let obligation = Obligation::new( cx.tcx, ObligationCause::dummy_with_span(cx.body.span), cx.param_env, - ty::TraitRef::from_lang_item(cx.tcx, LangItem::Destruct, cx.body.span, [ty]) - .with_constness(ty::BoundConstness::ConstIfConst), + ty::TraitRef::from_lang_item(cx.tcx, LangItem::Destruct, cx.body.span, [ty]), ); let infcx = cx.tcx.infer_ctxt().build(); diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 31effadd2c2..83004492c8b 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -630,7 +630,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } ty::Closure(_, args) => { let args = args.as_closure(); - let Some(f_ty) = args.upvar_tys().nth(f.as_usize()) else { + let Some(&f_ty) = args.upvar_tys().get(f.as_usize()) else { fail_out_of_bounds(self, location); return; }; @@ -667,7 +667,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { f_ty.ty } else { - let Some(f_ty) = args.as_generator().prefix_tys().nth(f.index()) else { + let Some(&f_ty) = args.as_generator().prefix_tys().get(f.index()) + else { fail_out_of_bounds(self, location); return; }; diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index a5c3cb3f857..f77bd53e76c 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -35,7 +35,7 @@ elsa = "=1.7.1" itertools = "0.10.1" [dependencies.parking_lot] -version = "0.11" +version = "0.12" [target.'cfg(windows)'.dependencies.windows] version = "0.48.0" diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 3deb9c5c2f5..33772089744 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -37,6 +37,7 @@ #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #![deny(unsafe_op_in_unsafe_fn)] #[macro_use] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 8f8b9eaa274..60dc9b20077 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -27,9 +27,7 @@ use rustc_data_structures::profiling::{ use rustc_data_structures::sync::SeqCst; use rustc_errors::registry::{InvalidErrorCode, Registry}; use rustc_errors::{markdown, ColorConfig}; -use rustc_errors::{ - DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage, TerminalUrl, -}; +use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage}; use rustc_feature::find_gated_cfg; use rustc_fluent_macro::fluent_messages; use rustc_interface::util::{self, collect_crate_types, get_codegen_backend}; @@ -393,6 +391,10 @@ fn run_compiler( pretty::print_after_hir_lowering(tcx, *ppm); Ok(()) })?; + + // Make sure the `output_filenames` query is run for its side + // effects of writing the dep-info and reporting errors. + queries.global_ctxt()?.enter(|tcx| tcx.output_filenames(())); } else { let krate = queries.parse()?.steal(); pretty::print_after_parsing(sess, &krate, *ppm); @@ -1405,15 +1407,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, - None, - None, fallback_bundle, - false, - false, - None, - false, - false, - TerminalUrl::No, )); let handler = rustc_errors::Handler::with_emitter(emitter); diff --git a/compiler/rustc_error_codes/src/error_codes/E0092.md b/compiler/rustc_error_codes/src/error_codes/E0092.md index 5cbe2a188b0..84ec0656d1a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0092.md +++ b/compiler/rustc_error_codes/src/error_codes/E0092.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0092 #![feature(intrinsics)] +#![allow(internal_features)] extern "rust-intrinsic" { fn atomic_foo(); // error: unrecognized atomic operation @@ -17,6 +18,7 @@ functions are defined in `compiler/rustc_codegen_llvm/src/intrinsic.rs` and in ``` #![feature(intrinsics)] +#![allow(internal_features)] extern "rust-intrinsic" { fn atomic_fence_seqcst(); // ok! diff --git a/compiler/rustc_error_codes/src/error_codes/E0093.md b/compiler/rustc_error_codes/src/error_codes/E0093.md index b1683cf4fc4..2bda4d74f72 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0093.md +++ b/compiler/rustc_error_codes/src/error_codes/E0093.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0093 #![feature(intrinsics)] +#![allow(internal_features)] extern "rust-intrinsic" { fn foo(); // error: unrecognized intrinsic function: `foo` @@ -22,6 +23,7 @@ functions are defined in `compiler/rustc_codegen_llvm/src/intrinsic.rs` and in ``` #![feature(intrinsics)] +#![allow(internal_features)] extern "rust-intrinsic" { fn atomic_fence_seqcst(); // ok! diff --git a/compiler/rustc_error_codes/src/error_codes/E0094.md b/compiler/rustc_error_codes/src/error_codes/E0094.md index cc546bdbb3b..67a8c3678c5 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0094.md +++ b/compiler/rustc_error_codes/src/error_codes/E0094.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0094 #![feature(intrinsics)] +#![allow(internal_features)] extern "rust-intrinsic" { #[rustc_safe_intrinsic] @@ -18,6 +19,7 @@ Example: ``` #![feature(intrinsics)] +#![allow(internal_features)] extern "rust-intrinsic" { #[rustc_safe_intrinsic] diff --git a/compiler/rustc_error_codes/src/error_codes/E0208.md b/compiler/rustc_error_codes/src/error_codes/E0208.md index c6db9b5d61b..2b811b4b850 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0208.md +++ b/compiler/rustc_error_codes/src/error_codes/E0208.md @@ -8,6 +8,7 @@ Erroneous code example: ```compile_fail // NOTE: this feature is perma-unstable and should *only* be used for // testing purposes. +#![allow(internal_features)] #![feature(rustc_attrs)] #[rustc_variance] diff --git a/compiler/rustc_error_codes/src/error_codes/E0211.md b/compiler/rustc_error_codes/src/error_codes/E0211.md index 8c2462ebd9b..70f14fffae6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0211.md +++ b/compiler/rustc_error_codes/src/error_codes/E0211.md @@ -5,6 +5,7 @@ used. Erroneous code examples: ```compile_fail #![feature(intrinsics)] +#![allow(internal_features)] extern "rust-intrinsic" { #[rustc_safe_intrinsic] @@ -41,6 +42,7 @@ For the first code example, please check the function definition. Example: ``` #![feature(intrinsics)] +#![allow(internal_features)] extern "rust-intrinsic" { #[rustc_safe_intrinsic] diff --git a/compiler/rustc_error_codes/src/error_codes/E0230.md b/compiler/rustc_error_codes/src/error_codes/E0230.md index cfb72e74319..87ea90e73c9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0230.md +++ b/compiler/rustc_error_codes/src/error_codes/E0230.md @@ -5,6 +5,7 @@ compiled: ```compile_fail,E0230 #![feature(rustc_attrs)] +#![allow(internal_features)] #[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{B}>`"] // error trait BadAnnotation<A> {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0231.md b/compiler/rustc_error_codes/src/error_codes/E0231.md index 23a0a88ecdd..a1aaf90df49 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0231.md +++ b/compiler/rustc_error_codes/src/error_codes/E0231.md @@ -5,6 +5,7 @@ compiled: ```compile_fail,E0231 #![feature(rustc_attrs)] +#![allow(internal_features)] #[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{}>`"] // error! trait BadAnnotation<A> {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0232.md b/compiler/rustc_error_codes/src/error_codes/E0232.md index b310caefa6e..0e50cf589ee 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0232.md +++ b/compiler/rustc_error_codes/src/error_codes/E0232.md @@ -5,6 +5,7 @@ compiled: ```compile_fail,E0232 #![feature(rustc_attrs)] +#![allow(internal_features)] #[rustc_on_unimplemented(lorem="")] // error! trait BadAnnotation {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0264.md b/compiler/rustc_error_codes/src/error_codes/E0264.md index e2a27f7b106..d7906076229 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0264.md +++ b/compiler/rustc_error_codes/src/error_codes/E0264.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0264 #![feature(lang_items)] +#![allow(internal_features)] extern "C" { #[lang = "cake"] // error: unknown external lang item: `cake` @@ -16,6 +17,7 @@ A list of available external lang items is available in ``` #![feature(lang_items)] +#![allow(internal_features)] extern "C" { #[lang = "panic_impl"] // ok! diff --git a/compiler/rustc_error_codes/src/error_codes/E0439.md b/compiler/rustc_error_codes/src/error_codes/E0439.md index 24268aef222..369226c383e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0439.md +++ b/compiler/rustc_error_codes/src/error_codes/E0439.md @@ -16,7 +16,7 @@ extern "platform-intrinsic" { The `simd_shuffle` function needs the length of the array passed as last parameter in its name. Example: -``` +```ignore (no longer compiles) #![feature(platform_intrinsics)] extern "platform-intrinsic" { diff --git a/compiler/rustc_error_codes/src/error_codes/E0539.md b/compiler/rustc_error_codes/src/error_codes/E0539.md index c53d60a5f47..cd28afbc48d 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0539.md +++ b/compiler/rustc_error_codes/src/error_codes/E0539.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0539 #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[deprecated(note)] // error! @@ -28,6 +29,7 @@ To fix these issues you need to give required key-value pairs. ``` #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[deprecated(since = "1.39.0", note = "reason")] // ok! diff --git a/compiler/rustc_error_codes/src/error_codes/E0542.md b/compiler/rustc_error_codes/src/error_codes/E0542.md index c69e574179b..be186dbd2cc 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0542.md +++ b/compiler/rustc_error_codes/src/error_codes/E0542.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0542 #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[stable(feature = "_stable_fn")] // invalid @@ -23,6 +24,7 @@ To fix this issue, you need to provide the `since` field. Example: ``` #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[stable(feature = "_stable_fn", since = "1.0.0")] // ok! diff --git a/compiler/rustc_error_codes/src/error_codes/E0543.md b/compiler/rustc_error_codes/src/error_codes/E0543.md index d0b2e2f7a7d..5051c72a151 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0543.md +++ b/compiler/rustc_error_codes/src/error_codes/E0543.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0543 #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[stable(since = "0.1.0", feature = "_deprecated_fn")] @@ -17,6 +18,7 @@ To fix this issue, you need to provide the `note` field. Example: ``` #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[stable(since = "0.1.0", feature = "_deprecated_fn")] diff --git a/compiler/rustc_error_codes/src/error_codes/E0544.md b/compiler/rustc_error_codes/src/error_codes/E0544.md index 2227e2a06bf..202401f9d45 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0544.md +++ b/compiler/rustc_error_codes/src/error_codes/E0544.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0544 #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "rust1")] #[stable(feature = "rust1", since = "1.0.0")] @@ -15,6 +16,7 @@ To fix this issue, ensure that each item has at most one stability attribute. ``` #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "rust1")] #[stable(feature = "test", since = "2.0.0")] // ok! diff --git a/compiler/rustc_error_codes/src/error_codes/E0545.md b/compiler/rustc_error_codes/src/error_codes/E0545.md index 7aba084f4d3..880378ebd3a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0545.md +++ b/compiler/rustc_error_codes/src/error_codes/E0545.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0545 #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[unstable(feature = "_unstable_fn", issue = "0")] // invalid @@ -18,6 +19,7 @@ Example: ``` #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[unstable(feature = "_unstable_fn", issue = "none")] // ok! diff --git a/compiler/rustc_error_codes/src/error_codes/E0546.md b/compiler/rustc_error_codes/src/error_codes/E0546.md index a33dcb7a9ac..8c98eaa07bb 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0546.md +++ b/compiler/rustc_error_codes/src/error_codes/E0546.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0546 #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[unstable(issue = "none")] // invalid @@ -17,6 +18,7 @@ To fix this issue, you need to provide the `feature` field. Example: ``` #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[unstable(feature = "unstable_fn", issue = "none")] // ok! diff --git a/compiler/rustc_error_codes/src/error_codes/E0547.md b/compiler/rustc_error_codes/src/error_codes/E0547.md index 4950325df64..5b0f7cd44cf 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0547.md +++ b/compiler/rustc_error_codes/src/error_codes/E0547.md @@ -4,6 +4,7 @@ Erroneous code example: ```compile_fail,E0547 #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[unstable(feature = "_unstable_fn")] // invalid @@ -17,6 +18,7 @@ To fix this issue, you need to provide the `issue` field. Example: ``` #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[unstable(feature = "_unstable_fn", issue = "none")] // ok! diff --git a/compiler/rustc_error_codes/src/error_codes/E0549.md b/compiler/rustc_error_codes/src/error_codes/E0549.md index 70e458a9867..cc6a47fe253 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0549.md +++ b/compiler/rustc_error_codes/src/error_codes/E0549.md @@ -5,6 +5,7 @@ Erroneous code example: ```compile_fail,E0549 #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[deprecated( @@ -19,6 +20,7 @@ Example: ``` #![feature(staged_api)] +#![allow(internal_features)] #![stable(since = "1.0.0", feature = "test")] #[stable(since = "1.0.0", feature = "test")] diff --git a/compiler/rustc_error_codes/src/error_codes/E0577.md b/compiler/rustc_error_codes/src/error_codes/E0577.md index eba2d3b1417..383ca61f6c4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0577.md +++ b/compiler/rustc_error_codes/src/error_codes/E0577.md @@ -3,7 +3,7 @@ Something other than a module was found in visibility scope. Erroneous code example: ```compile_fail,E0577,edition2018 -pub struct Sea; +pub enum Sea {} pub (in crate::Sea) struct Shark; // error! diff --git a/compiler/rustc_error_codes/src/error_codes/E0622.md b/compiler/rustc_error_codes/src/error_codes/E0622.md index 3ba3ed10e5c..5d71ee9949d 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0622.md +++ b/compiler/rustc_error_codes/src/error_codes/E0622.md @@ -4,6 +4,8 @@ Erroneous code example: ```compile_fail,E0622 #![feature(intrinsics)] +#![allow(internal_features)] + extern "rust-intrinsic" { pub static breakpoint: fn(); // error: intrinsic must be a function } @@ -17,6 +19,8 @@ error, just declare a function. Example: ```no_run #![feature(intrinsics)] +#![allow(internal_features)] + extern "rust-intrinsic" { pub fn breakpoint(); // ok! } diff --git a/compiler/rustc_error_codes/src/error_codes/E0773.md b/compiler/rustc_error_codes/src/error_codes/E0773.md index b19a58bf33d..aa65a475a4f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0773.md +++ b/compiler/rustc_error_codes/src/error_codes/E0773.md @@ -5,6 +5,7 @@ Erroneous code example: ```compile_fail,E0773 #![feature(decl_macro)] #![feature(rustc_attrs)] +#![allow(internal_features)] #[rustc_builtin_macro] pub macro test($item:item) { @@ -24,6 +25,7 @@ To fix the issue, remove the duplicate declaration: ``` #![feature(decl_macro)] #![feature(rustc_attrs)] +#![allow(internal_features)] #[rustc_builtin_macro] pub macro test($item:item) { diff --git a/compiler/rustc_error_codes/src/error_codes/E0789.md b/compiler/rustc_error_codes/src/error_codes/E0789.md index 89b7cd422fe..2c0018cc799 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0789.md +++ b/compiler/rustc_error_codes/src/error_codes/E0789.md @@ -10,6 +10,7 @@ Erroneous code example: // used outside of the compiler and standard library. #![feature(rustc_attrs)] #![feature(staged_api)] +#![allow(internal_features)] #![unstable(feature = "foo_module", reason = "...", issue = "123")] diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 77e2c900c0d..3bf15505090 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -4,6 +4,7 @@ #![feature(type_alias_impl_trait)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate tracing; diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index e8bcd7c1184..faab9f09da8 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -25,6 +25,7 @@ annotate-snippets = "0.9" termize = "0.1.1" serde = { version = "1.0.125", features = [ "derive" ] } serde_json = "1.0.59" +derive_setters = "0.1.6" [target.'cfg(windows)'.dependencies.windows] version = "0.48.0" diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 44654571d43..0cae06881b1 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -7,8 +7,6 @@ //! //! The output types are defined in `rustc_session::config::ErrorOutputType`. -use Destination::*; - use rustc_span::source_map::SourceMap; use rustc_span::{FileLines, SourceFile, Span}; @@ -24,6 +22,7 @@ use crate::{ }; use rustc_lint_defs::pluralize; +use derive_setters::Setters; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync::Lrc; use rustc_error_messages::{FluentArgs, SpanLabel}; @@ -35,8 +34,8 @@ use std::io::prelude::*; use std::io::{self, IsTerminal}; use std::iter; use std::path::Path; -use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream}; -use termcolor::{Buffer, Color, WriteColor}; +use termcolor::{Ansi, Buffer, BufferWriter, ColorChoice, ColorSpec, StandardStream}; +use termcolor::{Color, WriteColor}; /// Default column width, used in tests and when terminal dimensions cannot be determined. const DEFAULT_COLUMN_WIDTH: usize = 140; @@ -60,31 +59,15 @@ impl HumanReadableErrorType { } pub fn new_emitter( self, - dst: Box<dyn Write + Send>, - source_map: Option<Lrc<SourceMap>>, - bundle: Option<Lrc<FluentBundle>>, + mut dst: Box<dyn WriteColor + Send>, fallback_bundle: LazyFallbackBundle, - teach: bool, - diagnostic_width: Option<usize>, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, ) -> EmitterWriter { let (short, color_config) = self.unzip(); let color = color_config.suggests_using_colors(); - EmitterWriter::new( - dst, - source_map, - bundle, - fallback_bundle, - short, - teach, - color, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, - ) + if !dst.supports_color() && color { + dst = Box::new(Ansi::new(dst)); + } + EmitterWriter::new(dst, fallback_bundle).short_message(short) } } @@ -639,10 +622,13 @@ impl ColorConfig { } /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short` +#[derive(Setters)] pub struct EmitterWriter { + #[setters(skip)] dst: Destination, sm: Option<Lrc<SourceMap>>, fluent_bundle: Option<Lrc<FluentBundle>>, + #[setters(skip)] fallback_bundle: LazyFallbackBundle, short_message: bool, teach: bool, @@ -662,65 +648,32 @@ pub struct FileWithAnnotatedLines { } impl EmitterWriter { - pub fn stderr( - color_config: ColorConfig, - source_map: Option<Lrc<SourceMap>>, - fluent_bundle: Option<Lrc<FluentBundle>>, - fallback_bundle: LazyFallbackBundle, - short_message: bool, - teach: bool, - diagnostic_width: Option<usize>, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, - ) -> EmitterWriter { - let dst = Destination::from_stderr(color_config); + pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> EmitterWriter { + let dst = from_stderr(color_config); + Self::create(dst, fallback_bundle) + } + + fn create(dst: Destination, fallback_bundle: LazyFallbackBundle) -> EmitterWriter { EmitterWriter { dst, - sm: source_map, - fluent_bundle, + sm: None, + fluent_bundle: None, fallback_bundle, - short_message, - teach, + short_message: false, + teach: false, ui_testing: false, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, + diagnostic_width: None, + macro_backtrace: false, + track_diagnostics: false, + terminal_url: TerminalUrl::No, } } pub fn new( - dst: Box<dyn Write + Send>, - source_map: Option<Lrc<SourceMap>>, - fluent_bundle: Option<Lrc<FluentBundle>>, + dst: Box<dyn WriteColor + Send>, fallback_bundle: LazyFallbackBundle, - short_message: bool, - teach: bool, - colored: bool, - diagnostic_width: Option<usize>, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, ) -> EmitterWriter { - EmitterWriter { - dst: Raw(dst, colored), - sm: source_map, - fluent_bundle, - fallback_bundle, - short_message, - teach, - ui_testing: false, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, - } - } - - pub fn ui_testing(mut self, ui_testing: bool) -> Self { - self.ui_testing = ui_testing; - self + Self::create(dst, fallback_bundle) } fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> { @@ -2203,11 +2156,10 @@ impl EmitterWriter { Err(e) => panic!("failed to emit error: {e}"), } - let mut dst = self.dst.writable(); - match writeln!(dst) { + match writeln!(self.dst) { Err(e) => panic!("failed to emit error: {e}"), _ => { - if let Err(e) = dst.flush() { + if let Err(e) = self.dst.flush() { panic!("failed to emit error: {e}") } } @@ -2618,8 +2570,6 @@ fn emit_to_destination( ) -> io::Result<()> { use crate::lock; - let mut dst = dst.writable(); - // In order to prevent error message interleaving, where multiple error lines get intermixed // when multiple compiler processes error simultaneously, we emit errors with additional // steps. @@ -2635,7 +2585,8 @@ fn emit_to_destination( let _buffer_lock = lock::acquire_global_lock("rustc_errors"); for (pos, line) in rendered_buffer.iter().enumerate() { for part in line { - dst.apply_style(*lvl, part.style)?; + let style = part.style.color_spec(*lvl); + dst.set_color(&style)?; write!(dst, "{}", part.text)?; dst.reset()?; } @@ -2647,61 +2598,69 @@ fn emit_to_destination( Ok(()) } -pub enum Destination { - Terminal(StandardStream), - Buffered(BufferWriter), - // The bool denotes whether we should be emitting ansi color codes or not - Raw(Box<(dyn Write + Send)>, bool), -} +pub type Destination = Box<(dyn WriteColor + Send)>; -pub enum WritableDst<'a> { - Terminal(&'a mut StandardStream), - Buffered(&'a mut BufferWriter, Buffer), - Raw(&'a mut (dyn Write + Send)), - ColoredRaw(Ansi<&'a mut (dyn Write + Send)>), +struct Buffy { + buffer_writer: BufferWriter, + buffer: Buffer, } -impl Destination { - fn from_stderr(color: ColorConfig) -> Destination { - let choice = color.to_color_choice(); - // On Windows we'll be performing global synchronization on the entire - // system for emitting rustc errors, so there's no need to buffer - // anything. - // - // On non-Windows we rely on the atomicity of `write` to ensure errors - // don't get all jumbled up. - if cfg!(windows) { - Terminal(StandardStream::stderr(choice)) - } else { - Buffered(BufferWriter::stderr(choice)) - } +impl Write for Buffy { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.buffer.write(buf) } - fn writable(&mut self) -> WritableDst<'_> { - match *self { - Destination::Terminal(ref mut t) => WritableDst::Terminal(t), - Destination::Buffered(ref mut t) => { - let buf = t.buffer(); - WritableDst::Buffered(t, buf) - } - Destination::Raw(ref mut t, false) => WritableDst::Raw(t), - Destination::Raw(ref mut t, true) => WritableDst::ColoredRaw(Ansi::new(t)), + fn flush(&mut self) -> io::Result<()> { + self.buffer_writer.print(&self.buffer)?; + self.buffer.clear(); + Ok(()) + } +} + +impl Drop for Buffy { + fn drop(&mut self) { + if !self.buffer.is_empty() { + self.flush().unwrap(); + panic!("buffers need to be flushed in order to print their contents"); } } +} +impl WriteColor for Buffy { fn supports_color(&self) -> bool { - match *self { - Self::Terminal(ref stream) => stream.supports_color(), - Self::Buffered(ref buffer) => buffer.buffer().supports_color(), - Self::Raw(_, supports_color) => supports_color, - } + self.buffer.supports_color() + } + + fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { + self.buffer.set_color(spec) + } + + fn reset(&mut self) -> io::Result<()> { + self.buffer.reset() + } +} + +fn from_stderr(color: ColorConfig) -> Destination { + let choice = color.to_color_choice(); + // On Windows we'll be performing global synchronization on the entire + // system for emitting rustc errors, so there's no need to buffer + // anything. + // + // On non-Windows we rely on the atomicity of `write` to ensure errors + // don't get all jumbled up. + if cfg!(windows) { + Box::new(StandardStream::stderr(choice)) + } else { + let buffer_writer = BufferWriter::stderr(choice); + let buffer = buffer_writer.buffer(); + Box::new(Buffy { buffer_writer, buffer }) } } -impl<'a> WritableDst<'a> { - fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> { +impl Style { + fn color_spec(&self, lvl: Level) -> ColorSpec { let mut spec = ColorSpec::new(); - match style { + match self { Style::Addition => { spec.set_fg(Some(Color::Green)).set_intense(true); } @@ -2746,53 +2705,7 @@ impl<'a> WritableDst<'a> { spec.set_bold(true); } } - self.set_color(&spec) - } - - fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> { - match *self { - WritableDst::Terminal(ref mut t) => t.set_color(color), - WritableDst::Buffered(_, ref mut t) => t.set_color(color), - WritableDst::ColoredRaw(ref mut t) => t.set_color(color), - WritableDst::Raw(_) => Ok(()), - } - } - - fn reset(&mut self) -> io::Result<()> { - match *self { - WritableDst::Terminal(ref mut t) => t.reset(), - WritableDst::Buffered(_, ref mut t) => t.reset(), - WritableDst::ColoredRaw(ref mut t) => t.reset(), - WritableDst::Raw(_) => Ok(()), - } - } -} - -impl<'a> Write for WritableDst<'a> { - fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { - match *self { - WritableDst::Terminal(ref mut t) => t.write(bytes), - WritableDst::Buffered(_, ref mut buf) => buf.write(bytes), - WritableDst::Raw(ref mut w) => w.write(bytes), - WritableDst::ColoredRaw(ref mut t) => t.write(bytes), - } - } - - fn flush(&mut self) -> io::Result<()> { - match *self { - WritableDst::Terminal(ref mut t) => t.flush(), - WritableDst::Buffered(_, ref mut buf) => buf.flush(), - WritableDst::Raw(ref mut w) => w.flush(), - WritableDst::ColoredRaw(ref mut w) => w.flush(), - } - } -} - -impl<'a> Drop for WritableDst<'a> { - fn drop(&mut self) { - if let WritableDst::Buffered(ref mut dst, ref mut buf) = self { - drop(dst.print(buf)); - } + spec } } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 55f7c485024..b8f58e3057c 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -10,6 +10,7 @@ // FIXME: spec the JSON output properly. use rustc_span::source_map::{FilePathMapping, SourceMap}; +use termcolor::{ColorSpec, WriteColor}; use crate::emitter::{Emitter, HumanReadableErrorType}; use crate::registry::Registry; @@ -356,20 +357,29 @@ impl Diagnostic { self.0.lock().unwrap().flush() } } + impl WriteColor for BufWriter { + fn supports_color(&self) -> bool { + false + } + + fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> { + Ok(()) + } + + fn reset(&mut self) -> io::Result<()> { + Ok(()) + } + } let buf = BufWriter::default(); let output = buf.clone(); je.json_rendered - .new_emitter( - Box::new(buf), - Some(je.sm.clone()), - je.fluent_bundle.clone(), - je.fallback_bundle.clone(), - false, - je.diagnostic_width, - je.macro_backtrace, - je.track_diagnostics, - je.terminal_url, - ) + .new_emitter(Box::new(buf), je.fallback_bundle.clone()) + .sm(Some(je.sm.clone())) + .fluent_bundle(je.fluent_bundle.clone()) + .diagnostic_width(je.diagnostic_width) + .macro_backtrace(je.macro_backtrace) + .track_diagnostics(je.track_diagnostics) + .terminal_url(je.terminal_url) .ui_testing(je.ui_testing) .emit_diagnostic(diag); let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 3d1639db4af..7d660d2dbaa 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -15,6 +15,7 @@ #![feature(box_patterns)] #![feature(error_reporter)] #![allow(incomplete_features)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate rustc_macros; @@ -556,18 +557,7 @@ impl Handler { sm: Option<Lrc<SourceMap>>, fallback_bundle: LazyFallbackBundle, ) -> Self { - let emitter = Box::new(EmitterWriter::stderr( - ColorConfig::Auto, - sm, - None, - fallback_bundle, - false, - false, - None, - false, - false, - TerminalUrl::No, - )); + let emitter = Box::new(EmitterWriter::stderr(ColorConfig::Auto, fallback_bundle).sm(sm)); Self::with_emitter(emitter) } pub fn disable_warnings(mut self) -> Self { diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 2dae0e3f53c..02da5b5dc53 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -27,3 +27,4 @@ rustc_span = { path = "../rustc_span" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" +termcolor = "1.2" diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 83a5043b0aa..c4a9b2ace9a 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -11,6 +11,7 @@ #![feature(try_blocks)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index e9af688ee2b..82cac229284 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -2,6 +2,7 @@ use crate::expand::{AstFragment, AstFragmentKind}; use rustc_ast as ast; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; +use rustc_ast::token::Delimiter; use rustc_data_structures::fx::FxHashMap; use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::Ident; @@ -18,7 +19,7 @@ pub fn placeholder( path: ast::Path { span: DUMMY_SP, segments: ThinVec::new(), tokens: None }, args: P(ast::DelimArgs { dspan: ast::tokenstream::DelimSpan::dummy(), - delim: ast::MacDelimiter::Parenthesis, + delim: Delimiter::Parenthesis, tokens: ast::tokenstream::TokenStream::new(Vec::new()), }), }) diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 30fa5fea407..8e3219c138c 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -8,7 +8,8 @@ use rustc_span::{BytePos, Span}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Handler, MultiSpan, PResult, TerminalUrl}; +use rustc_errors::{Handler, MultiSpan, PResult}; +use termcolor::WriteColor; use std::io; use std::io::prelude::*; @@ -29,19 +30,9 @@ fn create_test_handler() -> (Handler, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) { vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], false, ); - let emitter = EmitterWriter::new( - Box::new(Shared { data: output.clone() }), - Some(source_map.clone()), - None, - fallback_bundle, - false, - false, - false, - Some(140), - false, - false, - TerminalUrl::No, - ); + let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }), fallback_bundle) + .sm(Some(source_map.clone())) + .diagnostic_width(Some(140)); let handler = Handler::with_emitter(Box::new(emitter)); (handler, source_map, output) } @@ -165,6 +156,20 @@ pub(crate) struct Shared<T: Write> { pub data: Arc<Mutex<T>>, } +impl<T: Write> WriteColor for Shared<T> { + fn supports_color(&self) -> bool { + false + } + + fn set_color(&mut self, _spec: &termcolor::ColorSpec) -> io::Result<()> { + Ok(()) + } + + fn reset(&mut self) -> io::Result<()> { + Ok(()) + } +} + impl<T: Write> Write for Shared<T> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.data.lock().unwrap().write(buf) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index bbc3d291e20..2a0dab64af5 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -16,12 +16,22 @@ macro_rules! set { }}; } +#[derive(PartialEq)] +enum FeatureStatus { + Default, + Incomplete, + Internal, +} + macro_rules! declare_features { - (__status_to_bool active) => { - false + (__status_to_enum active) => { + FeatureStatus::Default }; - (__status_to_bool incomplete) => { - true + (__status_to_enum incomplete) => { + FeatureStatus::Incomplete + }; + (__status_to_enum internal) => { + FeatureStatus::Internal }; ($( $(#[doc = $doc:tt])* ($status:ident, $feature:ident, $ver:expr, $issue:expr, $edition:expr), @@ -83,7 +93,7 @@ macro_rules! declare_features { pub fn incomplete(&self, feature: Symbol) -> bool { match feature { $( - sym::$feature => declare_features!(__status_to_bool $status), + sym::$feature => declare_features!(__status_to_enum $status) == FeatureStatus::Incomplete, )* // accepted and removed features aren't in this file but are never incomplete _ if self.declared_lang_features.iter().any(|f| f.0 == feature) => false, @@ -91,6 +101,22 @@ macro_rules! declare_features { _ => panic!("`{}` was not listed in `declare_features`", feature), } } + + /// Some features are internal to the compiler and standard library and should not + /// be used in normal projects. We warn the user about these + /// to alert them. + pub fn internal(&self, feature: Symbol) -> bool { + match feature { + $( + sym::$feature => declare_features!(__status_to_enum $status) == FeatureStatus::Internal, + )* + // accepted and removed features aren't in this file but are never internal + // (a removed feature might have been internal, but it doesn't matter anymore) + _ if self.declared_lang_features.iter().any(|f| f.0 == feature) => false, + _ if self.declared_lib_features.iter().any(|f| f.0 == feature) => false, + _ => panic!("`{}` was not listed in `declare_features`", feature), + } + } } }; } @@ -137,29 +163,29 @@ declare_features! ( /// Allows using the `vectorcall` ABI. (active, abi_vectorcall, "1.7.0", None, None), /// Allows using `#![needs_allocator]`, an implementation detail of `#[global_allocator]`. - (active, allocator_internals, "1.20.0", None, None), + (internal, allocator_internals, "1.20.0", None, None), /// Allows using `#[allow_internal_unsafe]`. This is an /// attribute on `macro_rules!` and can't use the attribute handling /// below (it has to be checked before expansion possibly makes /// macros disappear). - (active, allow_internal_unsafe, "1.0.0", None, None), + (internal, allow_internal_unsafe, "1.0.0", None, None), /// Allows using `#[allow_internal_unstable]`. This is an /// attribute on `macro_rules!` and can't use the attribute handling /// below (it has to be checked before expansion possibly makes /// macros disappear). - (active, allow_internal_unstable, "1.0.0", None, None), + (internal, allow_internal_unstable, "1.0.0", None, None), /// Allows using anonymous lifetimes in argument-position impl-trait. (active, anonymous_lifetime_in_impl_trait, "1.63.0", None, None), /// Allows identifying the `compiler_builtins` crate. - (active, compiler_builtins, "1.13.0", None, None), + (internal, compiler_builtins, "1.13.0", None, None), /// Allows writing custom MIR - (active, custom_mir, "1.65.0", None, None), + (internal, custom_mir, "1.65.0", None, None), /// Outputs useful `assert!` messages (active, generic_assert, "1.63.0", None, None), /// Allows using the `rust-intrinsic`'s "ABI". - (active, intrinsics, "1.0.0", None, None), + (internal, intrinsics, "1.0.0", None, None), /// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic. - (active, lang_items, "1.0.0", None, None), + (internal, lang_items, "1.0.0", None, None), /// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406 (active, link_cfg, "1.14.0", None, None), /// Allows the `multiple_supertrait_upcastable` lint. @@ -167,22 +193,22 @@ declare_features! ( /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver! (incomplete, negative_bounds, "1.71.0", None, None), /// Allows using `#[omit_gdb_pretty_printer_section]`. - (active, omit_gdb_pretty_printer_section, "1.5.0", None, None), + (internal, omit_gdb_pretty_printer_section, "1.5.0", None, None), /// Allows using `#[prelude_import]` on glob `use` items. - (active, prelude_import, "1.2.0", None, None), + (internal, prelude_import, "1.2.0", None, None), /// Used to identify crates that contain the profiler runtime. - (active, profiler_runtime, "1.18.0", None, None), + (internal, profiler_runtime, "1.18.0", None, None), /// Allows using `rustc_*` attributes (RFC 572). - (active, rustc_attrs, "1.0.0", None, None), + (internal, rustc_attrs, "1.0.0", None, None), /// Allows using the `#[stable]` and `#[unstable]` attributes. - (active, staged_api, "1.0.0", None, None), + (internal, staged_api, "1.0.0", None, None), /// Added for testing E0705; perma-unstable. - (active, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)), + (internal, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)), /// Added for testing unstable lints; perma-unstable. - (active, test_unstable_lint, "1.60.0", None, None), + (internal, test_unstable_lint, "1.60.0", None, None), /// Allows non-`unsafe` —and thus, unsound— access to `Pin` constructions. - /// Marked `incomplete` since perma-unstable and unsound. - (incomplete, unsafe_pin_internals, "1.60.0", None, None), + /// Marked `internal` since perma-unstable and unsound. + (internal, unsafe_pin_internals, "1.60.0", None, None), /// Use for stable + negative coherence and strict coherence depending on trait's /// rustc_strict_coherence value. (active, with_negative_coherence, "1.60.0", None, None), @@ -216,19 +242,19 @@ declare_features! ( /// Allows using the `#[linkage = ".."]` attribute. (active, linkage, "1.0.0", Some(29603), None), /// Allows declaring with `#![needs_panic_runtime]` that a panic runtime is needed. - (active, needs_panic_runtime, "1.10.0", Some(32837), None), + (internal, needs_panic_runtime, "1.10.0", Some(32837), None), /// Allows using `+bundled,+whole-archive` native libs. (active, packed_bundled_libs, "1.69.0", Some(108081), None), /// Allows using the `#![panic_runtime]` attribute. - (active, panic_runtime, "1.10.0", Some(32837), None), + (internal, panic_runtime, "1.10.0", Some(32837), None), /// Allows using `#[rustc_allow_const_fn_unstable]`. /// This is an attribute on `const fn` for the same /// purpose as `#[allow_internal_unstable]`. - (active, rustc_allow_const_fn_unstable, "1.49.0", Some(69399), None), + (internal, rustc_allow_const_fn_unstable, "1.49.0", Some(69399), None), /// Allows using compiler's own crates. (active, rustc_private, "1.0.0", Some(27812), None), /// Allows using internal rustdoc features like `doc(keyword)`. - (active, rustdoc_internals, "1.58.0", Some(90418), None), + (internal, rustdoc_internals, "1.58.0", Some(90418), None), /// Allows using the `rustdoc::missing_doc_code_examples` lint (active, rustdoc_missing_doc_code_examples, "1.31.0", Some(101730), None), /// Allows using `#[start]` on a function indicating that it is the program entrypoint. @@ -313,6 +339,8 @@ declare_features! ( (active, async_closure, "1.37.0", Some(62290), None), /// Allows async functions to be declared, implemented, and used in traits. (active, async_fn_in_trait, "1.66.0", Some(91611), None), + /// Allows `#[track_caller]` on async functions. + (active, async_fn_track_caller, "CURRENT_RUSTC_VERSION", Some(110011), None), /// Allows builtin # foo() syntax (active, builtin_syntax, "1.71.0", Some(110680), None), /// Allows `c"foo"` literals. diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index beb6307846d..69e33115922 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -108,8 +108,6 @@ impl UnstableFeatures { fn find_lang_feature_issue(feature: Symbol) -> Option<NonZeroU32> { if let Some(info) = ACTIVE_FEATURES.iter().find(|t| t.name == feature) { - // FIXME (#28244): enforce that active features have issue numbers - // assert!(info.issue.is_some()) info.issue } else { // search in Accepted, Removed, or Stable Removed features diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6b76e16825f..c6f8d1e211d 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1754,7 +1754,7 @@ impl Expr<'_> { ExprKind::Unary(UnOp::Deref, _) => true, - ExprKind::Field(ref base, _) | ExprKind::Index(ref base, _) => { + ExprKind::Field(ref base, _) | ExprKind::Index(ref base, _, _) => { allow_projections_from(base) || base.is_place_expr(allow_projections_from) } @@ -1831,7 +1831,7 @@ impl Expr<'_> { ExprKind::Type(base, _) | ExprKind::Unary(_, base) | ExprKind::Field(base, _) - | ExprKind::Index(base, _) + | ExprKind::Index(base, _, _) | ExprKind::AddrOf(.., base) | ExprKind::Cast(base, _) => { // This isn't exactly true for `Index` and all `Unary`, but we are using this @@ -2015,7 +2015,9 @@ pub enum ExprKind<'hir> { /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct or tuple field. Field(&'hir Expr<'hir>, Ident), /// An indexing operation (`foo[2]`). - Index(&'hir Expr<'hir>, &'hir Expr<'hir>), + /// Similar to [`ExprKind::MethodCall`], the final `Span` represents the span of the brackets + /// and index. + Index(&'hir Expr<'hir>, &'hir Expr<'hir>, Span), /// Path to a definition, possibly containing lifetime or type parameters. Path(QPath<'hir>), @@ -2673,7 +2675,7 @@ pub struct OpaqueTy<'hir> { /// /// This mapping associated a captured lifetime (first parameter) with the new /// early-bound lifetime that was generated for the opaque. - pub lifetime_mapping: Option<&'hir [(Lifetime, LocalDefId)]>, + pub lifetime_mapping: Option<&'hir [(&'hir Lifetime, LocalDefId)]>, /// Whether the opaque is a return-position impl trait (or async future) /// originating from a trait method. This makes it so that the opaque is /// lowered as an associated type. diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index a8a94e6a476..9d1d899eef0 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -780,7 +780,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) visitor.visit_expr(subexpression); visitor.visit_ident(ident); } - ExprKind::Index(ref main_expression, ref index_expression) => { + ExprKind::Index(ref main_expression, ref index_expression, _) => { visitor.visit_expr(main_expression); visitor.visit_expr(index_expression) } diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 616de57dc63..34214931a08 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -13,6 +13,7 @@ #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 3235a9ceba1..b40e3123522 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -713,7 +713,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); debug!(?poly_trait_ref, ?assoc_bindings); - bounds.push_trait_bound(tcx, poly_trait_ref, span, constness, polarity); + bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity); let mut dup_bindings = FxHashMap::default(); for binding in &assoc_bindings { @@ -1462,7 +1462,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let traits: Vec<_> = self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident); - // Don't print `TyErr` to the user. + // Don't print `ty::Error` to the user. self.report_ambiguous_associated_type( span, &[qself_ty.to_string()], diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs index a36ec0c5730..30c2ab8f545 100644 --- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs @@ -62,11 +62,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { match bound_pred.skip_binder() { ty::ClauseKind::Trait(trait_pred) => { assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive); - trait_bounds.push(( - bound_pred.rebind(trait_pred.trait_ref), - span, - trait_pred.constness, - )); + trait_bounds.push((bound_pred.rebind(trait_pred.trait_ref), span)); } ty::ClauseKind::Projection(proj) => { projection_bounds.push((bound_pred.rebind(proj), span)); @@ -86,7 +82,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Expand trait aliases recursively and check that only one regular (non-auto) trait // is used and no 'maybe' bounds are used. let expanded_traits = - traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b, _)| (a, b))); + traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b)| (a, b))); let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits .filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self) @@ -126,7 +122,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { if regular_traits.is_empty() && auto_traits.is_empty() { let trait_alias_span = trait_bounds .iter() - .map(|&(trait_ref, _, _)| trait_ref.def_id()) + .map(|&(trait_ref, _)| trait_ref.def_id()) .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) .map(|trait_ref| tcx.def_span(trait_ref)); let reported = @@ -157,10 +153,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let regular_traits_refs_spans = trait_bounds .into_iter() - .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id())); + .filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); - for (base_trait_ref, span, constness) in regular_traits_refs_spans { - assert_eq!(constness, ty::BoundConstness::NotConst); + for (base_trait_ref, span) in regular_traits_refs_spans { let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx); for pred in traits::elaborate(tcx, [base_pred]) { debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred); diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 531100e1fe6..1d9ae2b9cb7 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -42,13 +42,12 @@ impl<'tcx> Bounds<'tcx> { tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, span: Span, - constness: ty::BoundConstness, polarity: ty::ImplPolarity, ) { self.clauses.push(( trait_ref .map_bound(|trait_ref| { - ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, constness, polarity }) + ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity }) }) .to_predicate(tcx), span, diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 064021b1ea4..f89e2e5c25b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -273,6 +273,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ], Ty::new_unit(tcx), ), + sym::compare_bytes => { + let byte_ptr = Ty::new_imm_ptr(tcx, tcx.types.u8); + (0, vec![byte_ptr, byte_ptr, tcx.types.usize], tcx.types.i32) + } sym::write_bytes | sym::volatile_set_memory => ( 1, vec![ @@ -567,20 +571,6 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) | sym::simd_reduce_min_nanless | sym::simd_reduce_max_nanless => (2, vec![param(0)], param(1)), sym::simd_shuffle => (3, vec![param(0), param(0), param(1)], param(2)), - name if name.as_str().starts_with("simd_shuffle") => { - match name.as_str()["simd_shuffle".len()..].parse() { - Ok(n) => { - let params = vec![param(0), param(0), Ty::new_array(tcx, tcx.types.u32, n)]; - (2, params, param(1)) - } - Err(_) => { - let msg = - format!("unrecognized platform-specific intrinsic function: `{name}`"); - tcx.sess.struct_span_err(it.span, msg).emit(); - return; - } - } - } _ => { let msg = format!("unrecognized platform-specific intrinsic function: `{name}`"); tcx.sess.struct_span_err(it.span, msg).emit(); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index ad886ce82b1..16d4d099c7e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -194,7 +194,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span. match (tcx.impl_polarity(def_id), impl_.polarity) { (ty::ImplPolarity::Positive, _) => { - check_impl(tcx, item, impl_.self_ty, &impl_.of_trait, impl_.constness); + check_impl(tcx, item, impl_.self_ty, &impl_.of_trait); } (ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => { // FIXME(#27579): what amount of WF checking do we need for neg impls? @@ -245,13 +245,14 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { } // `ForeignItem`s are handled separately. hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::TyAlias(hir_ty, ..) => { + hir::ItemKind::TyAlias(hir_ty, ast_generics) => { if tcx.features().lazy_type_alias || tcx.type_of(item.owner_id).skip_binder().has_opaque_types() { // Bounds of lazy type aliases and of eager ones that contain opaque types are respected. // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`. check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow); + check_variances_for_type_defn(tcx, item, ast_generics); } } _ => {} @@ -555,8 +556,8 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( for (region_a, region_a_idx) in ®ions { // Ignore `'static` lifetimes for the purpose of this lint: it's // because we know it outlives everything and so doesn't give meaningful - // clues - if let ty::ReStatic = **region_a { + // clues. Also ignore `ReError`, to avoid knock-down errors. + if let ty::ReStatic | ty::ReError(_) = **region_a { continue; } // For each region argument (e.g., `'a` in our example), check for a @@ -599,8 +600,9 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( // on the GAT itself. for (region_b, region_b_idx) in ®ions { // Again, skip `'static` because it outlives everything. Also, we trivially - // know that a region outlives itself. - if ty::ReStatic == **region_b || region_a == region_b { + // know that a region outlives itself. Also ignore `ReError`, to avoid + // knock-down errors. + if matches!(**region_b, ty::ReStatic | ty::ReError(_)) || region_a == region_b { continue; } if region_known_to_outlive( @@ -1190,7 +1192,6 @@ fn check_impl<'tcx>( item: &'tcx hir::Item<'tcx>, ast_self_ty: &hir::Ty<'_>, ast_trait_ref: &Option<hir::TraitRef<'_>>, - constness: hir::Constness, ) { enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| { match ast_trait_ref { @@ -1204,14 +1205,8 @@ fn check_impl<'tcx>( Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), trait_ref, ); - let trait_pred = ty::TraitPredicate { - trait_ref, - constness: match constness { - hir::Constness::Const => ty::BoundConstness::ConstIfConst, - hir::Constness::NotConst => ty::BoundConstness::NotConst, - }, - polarity: ty::ImplPolarity::Positive, - }; + let trait_pred = + ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Positive }; let mut obligations = traits::wf::trait_obligations( wfcx.infcx, wfcx.param_env, @@ -1706,10 +1701,27 @@ fn check_variances_for_type_defn<'tcx>( hir_generics: &hir::Generics<'_>, ) { let identity_args = ty::GenericArgs::identity_for_item(tcx, item.owner_id); - for field in tcx.adt_def(item.owner_id).all_fields() { - if field.ty(tcx, identity_args).references_error() { - return; + + match item.kind { + ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => { + for field in tcx.adt_def(item.owner_id).all_fields() { + if field.ty(tcx, identity_args).references_error() { + return; + } + } + } + ItemKind::TyAlias(..) => { + let ty = tcx.type_of(item.owner_id).instantiate_identity(); + + if tcx.features().lazy_type_alias || ty.has_opaque_types() { + if ty.references_error() { + return; + } + } else { + bug!(); + } } + _ => bug!(), } let ty_predicates = tcx.predicates_of(item.owner_id); diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index ab3b2dde078..83220be6883 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -10,7 +10,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{GenericPredicates, Generics, ImplTraitInTraitData, ToPredicate}; -use rustc_span::symbol::{sym, Ident}; +use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol, DUMMY_SP}; /// Returns a list of all type predicates (explicit and implicit) for the definition with @@ -37,17 +37,10 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic // from the trait itself that *shouldn't* be shown as the source of // an obligation and instead be skipped. Otherwise we'd use // `tcx.def_span(def_id);` - - let constness = if tcx.has_attr(def_id, sym::const_trait) { - ty::BoundConstness::ConstIfConst - } else { - ty::BoundConstness::NotConst - }; - let span = rustc_span::DUMMY_SP; result.predicates = tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( - ty::TraitRef::identity(tcx, def_id).with_constness(constness).to_predicate(tcx), + ty::TraitRef::identity(tcx, def_id).to_predicate(tcx), span, )))); } @@ -89,7 +82,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen tcx, def_id, lifetime_mapping.iter().map(|(lifetime, def_id)| { - (*lifetime, (*def_id, lifetime.ident.name, lifetime.ident.span)) + (**lifetime, (*def_id, lifetime.ident.name, lifetime.ident.span)) }), tcx.generics_of(def_id.to_def_id()), &mut predicates, @@ -204,7 +197,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // (see below). Recall that a default impl is not itself an impl, but rather a // set of defaults that can be incorporated into another impl. if let Some(trait_ref) = is_default_impl_trait { - predicates.insert((trait_ref.without_const().to_predicate(tcx), tcx.def_span(def_id))); + predicates.insert((trait_ref.to_predicate(tcx), tcx.def_span(def_id))); } // Collect the region predicates that were declared inline as @@ -777,8 +770,7 @@ pub(super) fn type_param_predicates( if param_id == item_hir_id { let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id.to_def_id()); - extend = - Some((identity_trait_ref.without_const().to_predicate(tcx), item.span)); + extend = Some((identity_trait_ref.to_predicate(tcx), item.span)); } generics } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 8b2c93d8fd3..3760195a5e8 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -431,45 +431,13 @@ fn check_predicates<'tcx>( /// /// So we make that check in this function and try to raise a helpful error message. fn trait_predicates_eq<'tcx>( - tcx: TyCtxt<'tcx>, + _tcx: TyCtxt<'tcx>, predicate1: ty::Predicate<'tcx>, predicate2: ty::Predicate<'tcx>, - span: Span, + _span: Span, ) -> bool { - let pred1_kind = predicate1.kind().skip_binder(); - let pred2_kind = predicate2.kind().skip_binder(); - let (trait_pred1, trait_pred2) = match (pred1_kind, pred2_kind) { - ( - ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred1)), - ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred2)), - ) => (pred1, pred2), - // Just use plain syntactic equivalence if either of the predicates aren't - // trait predicates or have bound vars. - _ => return predicate1 == predicate2, - }; - - let predicates_equal_modulo_constness = { - let pred1_unconsted = - ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred1 }; - let pred2_unconsted = - ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..trait_pred2 }; - pred1_unconsted == pred2_unconsted - }; - - if !predicates_equal_modulo_constness { - return false; - } - - // Check that the predicate on the specializing impl is at least as const as - // the one on the base. - match (trait_pred2.constness, trait_pred1.constness) { - (ty::BoundConstness::ConstIfConst, ty::BoundConstness::NotConst) => { - tcx.sess.emit_err(errors::MissingTildeConst { span }); - } - _ => {} - } - - true + // FIXME(effects) + predicate1 == predicate2 } #[instrument(level = "debug", skip(tcx))] @@ -482,7 +450,6 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc // items. ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, - constness: _, polarity: _, })) => { if !matches!( @@ -536,7 +503,6 @@ fn trait_predicate_kind<'tcx>( match predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, - constness: _, polarity: _, })) => Some(tcx.trait_def(trait_ref.def_id).specialization_kind), ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(_)) diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index ec8889781f4..4a3d522e488 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -6,7 +6,7 @@ use hir::def_id::{DefId, LocalDefId}; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; use super::terms::VarianceTerm::*; @@ -78,6 +78,12 @@ pub fn add_constraints_from_crate<'a, 'tcx>( } } DefKind::Fn | DefKind::AssocFn => constraint_cx.build_constraints_for_item(def_id), + DefKind::TyAlias + if tcx.features().lazy_type_alias + || tcx.type_of(def_id).instantiate_identity().has_opaque_types() => + { + constraint_cx.build_constraints_for_item(def_id) + } _ => {} } } @@ -101,7 +107,18 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { let inferred_start = self.terms_cx.inferred_starts[&def_id]; let current_item = &CurrentItem { inferred_start }; - match tcx.type_of(def_id).instantiate_identity().kind() { + let ty = tcx.type_of(def_id).instantiate_identity(); + + // The type as returned by `type_of` is the underlying type and generally not a weak projection. + // Therefore we need to check the `DefKind` first. + if let DefKind::TyAlias = tcx.def_kind(def_id) + && (tcx.features().lazy_type_alias || ty.has_opaque_types()) + { + self.add_constraints_from_ty(current_item, ty, self.covariant); + return; + } + + match ty.kind() { ty::Adt(def, _) => { // Not entirely obvious: constraints on structs/enums do not // affect the variance of their type parameters. See discussion @@ -127,6 +144,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::Error(_) => {} + _ => { span_bug!( tcx.def_span(def_id), @@ -252,10 +270,14 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_args(current, def.did(), args, variance); } - ty::Alias(_, ref data) => { + ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, ref data) => { self.add_constraints_from_invariant_args(current, data.args, variance); } + ty::Alias(ty::Weak, ref data) => { + self.add_constraints_from_args(current, data.def_id, data.args, variance); + } + ty::Dynamic(data, r, _) => { // The type `dyn Trait<T> +'a` is covariant w/r/t `'a`: self.add_constraints_from_region(current, r, variance); diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 1ef257e87d6..2ef294c6793 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -8,7 +8,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, CrateVariancesMap, GenericArgsRef, Ty, TyCtxt}; -use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable}; +use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; use std::ops::ControlFlow; /// Defines the `TermsContext` basically houses an arena where we can @@ -56,6 +56,14 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { let crate_map = tcx.crate_variances(()); return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]); } + DefKind::TyAlias + if tcx.features().lazy_type_alias + || tcx.type_of(item_def_id).instantiate_identity().has_opaque_types() => + { + // These are inferred. + let crate_map = tcx.crate_variances(()); + return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]); + } DefKind::OpaqueTy => { return variance_of_opaque(tcx, item_def_id); } @@ -156,7 +164,6 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc match pred.kind().skip_binder() { ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref: ty::TraitRef { def_id: _, args, .. }, - constness: _, polarity: _, }) => { for subst in &args[1..] { diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index ed03c5da26f..1ef3d383bd8 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -12,7 +12,7 @@ use rustc_arena::DroplessArena; use rustc_hir::def::DefKind; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use std::fmt; use self::VarianceTerm::*; @@ -97,6 +97,12 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( } } DefKind::Fn | DefKind::AssocFn => terms_cx.add_inferreds_for_item(def_id), + DefKind::TyAlias + if tcx.features().lazy_type_alias + || tcx.type_of(def_id).instantiate_identity().has_opaque_types() => + { + terms_cx.add_inferreds_for_item(def_id) + } _ => {} } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 2d8b956771b..fda46798708 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1526,7 +1526,7 @@ impl<'a> State<'a> { self.word("."); self.print_ident(ident); } - hir::ExprKind::Index(expr, index) => { + hir::ExprKind::Index(expr, index, _) => { self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); self.word("["); self.print_expr(index); @@ -2419,7 +2419,7 @@ fn contains_exterior_struct_lit(value: &hir::Expr<'_>) -> bool { | hir::ExprKind::Cast(x, _) | hir::ExprKind::Type(x, _) | hir::ExprKind::Field(x, _) - | hir::ExprKind::Index(x, _) => { + | hir::ExprKind::Index(x, _, _) => { // `&X { y: 1 }, X { y: 1 }.y` contains_exterior_struct_lit(x) } diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index d5f03a6aaed..6d926cd8aa1 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // #55810: Type check patterns first so we get types for all bindings. let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span); for arm in arms { - self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut)); + self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut), None); } // Now typecheck the blocks. @@ -136,15 +136,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &cause, Some(&arm.body), arm_ty, - Some(&mut |err| { - self.suggest_removing_semicolon_for_coerce( - err, - expr, - orig_expected, - arm_ty, - prior_arm, - ) - }), + |err| self.suggest_removing_semicolon_for_coerce(err, expr, arm_ty, prior_arm), false, ); @@ -181,7 +173,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, diag: &mut Diagnostic, expr: &hir::Expr<'tcx>, - expectation: Expectation<'tcx>, arm_ty: Ty<'tcx>, prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>, ) { @@ -195,7 +186,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::ExprKind::Block(block, _) = body.value.kind else { return; }; - let Some(hir::Stmt { kind: hir::StmtKind::Semi(last_expr), .. }) = + let Some(hir::Stmt { kind: hir::StmtKind::Semi(last_expr), span: semi_span, .. }) = block.innermost_block().stmts.last() else { return; @@ -212,9 +203,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { else { return; }; - let Expectation::IsLast(stmt) = expectation else { - return; - }; let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { Some(ret_coercion) => { @@ -231,7 +219,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); + let semi_span = expr.span.shrink_to_hi().with_hi(semi_span.hi()); let mut ret_span: MultiSpan = semi_span.into(); ret_span.push_span_label( expr.span, @@ -279,7 +267,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coercion.coerce_forced_unit( self, &cause, - &mut |err| { + |err| { if let Some((span, msg)) = &ret_reason { err.span_label(*span, msg.clone()); } else if let ExprKind::Block(block, _) = &then_expr.kind diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 53bae315d78..1fc1e5aca2b 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -89,7 +89,7 @@ pub(super) fn check_fn<'a, 'tcx>( for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { // Check the pattern. let ty_span = try { inputs_hir?.get(idx)?.span }; - fcx.check_pat_top(¶m.pat, param_ty, ty_span, None); + fcx.check_pat_top(¶m.pat, param_ty, ty_span, None, None); // Check that argument is Sized. if !params_can_be_unsized { diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 0624a4baf79..3e67afb1c3d 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -795,7 +795,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Converts the types that the user supplied, in case that doing /// so should yield an error, but returns back a signature where - /// all parameters are of type `TyErr`. + /// all parameters are of type `ty::Error`. fn error_sig_of_closure( &self, decl: &hir::FnDecl<'_>, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 4fdfc51bc86..fa6bad84376 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1418,7 +1418,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expression: &'tcx hir::Expr<'tcx>, expression_ty: Ty<'tcx>, ) { - self.coerce_inner(fcx, cause, Some(expression), expression_ty, None, false) + self.coerce_inner(fcx, cause, Some(expression), expression_ty, |_| {}, false) } /// Indicates that one of the inputs is a "forced unit". This @@ -1437,7 +1437,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { &mut self, fcx: &FnCtxt<'a, 'tcx>, cause: &ObligationCause<'tcx>, - augment_error: &mut dyn FnMut(&mut Diagnostic), + augment_error: impl FnOnce(&mut Diagnostic), label_unit_as_expected: bool, ) { self.coerce_inner( @@ -1445,7 +1445,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { cause, None, Ty::new_unit(fcx.tcx), - Some(augment_error), + augment_error, label_unit_as_expected, ) } @@ -1460,7 +1460,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { cause: &ObligationCause<'tcx>, expression: Option<&'tcx hir::Expr<'tcx>>, mut expression_ty: Ty<'tcx>, - augment_error: Option<&mut dyn FnMut(&mut Diagnostic)>, + augment_error: impl FnOnce(&mut Diagnostic), label_expression_as_expected: bool, ) { // Incorporate whatever type inference information we have @@ -1639,9 +1639,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } - if let Some(augment_error) = augment_error { - augment_error(&mut err); - } + augment_error(&mut err); let is_insufficiently_polymorphic = matches!(coercion_error, TypeError::RegionsInsufficientlyPolymorphic(..)); diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 26fa3d80d55..48383bd90fe 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -880,6 +880,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, expr_ty: Ty<'tcx>, ) -> bool { + if in_external_macro(self.tcx.sess, expr.span) { + return false; + } if let ty::Adt(expected_adt, args) = expected.kind() { if let hir::ExprKind::Field(base, ident) = expr.kind { let base_ty = self.typeck_results.borrow().expr_ty(base); diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index 4f086cf597d..35e5fb769a5 100644 --- a/compiler/rustc_hir_typeck/src/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs @@ -21,8 +21,6 @@ pub enum Expectation<'tcx> { /// This rvalue expression will be wrapped in `&` or `Box` and coerced /// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`. ExpectRvalueLikeUnsized(Ty<'tcx>), - - IsLast(Span), } impl<'a, 'tcx> Expectation<'tcx> { @@ -88,13 +86,12 @@ impl<'a, 'tcx> Expectation<'tcx> { ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(t)), ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(t)), ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(t)), - IsLast(sp) => IsLast(sp), } } pub(super) fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> { match self.resolve(fcx) { - NoExpectation | IsLast(_) => None, + NoExpectation => None, ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty), } } @@ -106,9 +103,7 @@ impl<'a, 'tcx> Expectation<'tcx> { pub(super) fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> { match self { ExpectHasType(ty) => Some(fcx.resolve_vars_if_possible(ty)), - NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) | IsLast(_) => { - None - } + NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) => None, } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 8f5737dd4ad..2af8648455b 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -60,28 +60,13 @@ use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCauseCode}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) { - let ty = self.check_expr_with_hint(expr, expected); - self.demand_eqtype(expr.span, expected, ty); - } - pub fn check_expr_has_type_or_error( &self, expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - extend_err: impl FnMut(&mut Diagnostic), + expected_ty: Ty<'tcx>, + extend_err: impl FnOnce(&mut Diagnostic), ) -> Ty<'tcx> { - self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err) - } - - fn check_expr_meets_expectation_or_error( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - mut extend_err: impl FnMut(&mut Diagnostic), - ) -> Ty<'tcx> { - let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool); - let mut ty = self.check_expr_with_expectation(expr, expected); + let mut ty = self.check_expr_with_expectation(expr, ExpectHasType(expected_ty)); // While we don't allow *arbitrary* coercions here, we *do* allow // coercions from ! to `expected`. @@ -341,9 +326,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr), ExprKind::Type(e, t) => { - let ty = self.to_ty_saving_user_provided_ty(&t); - self.check_expr_eq_type(&e, ty); - ty + let ascribed_ty = self.to_ty_saving_user_provided_ty(&t); + let ty = self.check_expr_with_hint(e, ascribed_ty); + self.demand_eqtype(e.span, ascribed_ty, ty); + ascribed_ty } ExprKind::If(cond, then_expr, opt_else_expr) => { self.check_then_else(cond, then_expr, opt_else_expr, expr.span, expected) @@ -359,7 +345,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_struct(expr, expected, qpath, fields, base_expr) } ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected), - ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr), + ExprKind::Index(base, idx, brackets_span) => { + self.check_expr_index(base, idx, expr, brackets_span) + } ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src), hir::ExprKind::Err(guar) => Ty::new_error(tcx, guar), } @@ -666,7 +654,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coerce.coerce_forced_unit( self, &cause, - &mut |mut err| { + |mut err| { self.suggest_mismatched_types_on_tail( &mut err, expr, ty, e_ty, target_id, ); @@ -762,7 +750,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coercion.coerce_forced_unit( self, &cause, - &mut |db| { + |db| { let span = fn_decl.output.span(); if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { db.span_label( @@ -774,7 +762,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true, ); } else { - coercion.coerce_forced_unit(self, &cause, &mut |_| (), true); + coercion.coerce_forced_unit(self, &cause, |_| (), true); } } self.tcx.types.never @@ -2854,6 +2842,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, idx: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>, + brackets_span: Span, ) -> Ty<'tcx> { let base_t = self.check_expr(&base); let idx_t = self.check_expr(&idx); @@ -2887,7 +2876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = type_error_struct!( self.tcx.sess, - expr.span, + brackets_span, base_t, E0608, "cannot index into a value of type `{base_t}`", @@ -2901,16 +2890,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node && i < types.len().try_into().expect("expected tuple index to be < usize length") { - let snip = self.tcx.sess.source_map().span_to_snippet(base.span); - if let Ok(snip) = snip { - err.span_suggestion( - expr.span, - "to access tuple elements, use", - format!("{snip}.{i}"), - Applicability::MachineApplicable, - ); - needs_note = false; - } + + err.span_suggestion( + brackets_span, + "to access tuple elements, use", + format!(".{i}"), + Applicability::MachineApplicable, + ); + needs_note = false; } else if let ExprKind::Path(..) = idx.peel_borrows().kind { err.span_label(idx.span, "cannot access tuple elements at a variable index"); } @@ -2988,7 +2975,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Binder::dummy(ty::TraitPredicate { trait_ref: impl_trait_ref, polarity: ty::ImplPolarity::Positive, - constness: ty::BoundConstness::NotConst, }), |derived| { traits::ImplDerivedObligation(Box::new( diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index a59061cbf67..840910732d8 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -211,7 +211,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.select_from_expr(base); } - hir::ExprKind::Index(lhs, rhs) => { + hir::ExprKind::Index(lhs, rhs, _) => { // lhs[rhs] self.select_from_expr(lhs); self.consume_expr(rhs); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index ecafb50f420..1433c67d55d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1461,10 +1461,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_env, bounds, ) { - // N.B. We are remapping all predicates to non-const since we don't know if we just - // want them as function pointers or we are calling them from a const-context. The - // actual checking will occur in `rustc_const_eval::transform::check_consts`. - self.register_predicate(obligation.without_const(self.tcx)); + self.register_predicate(obligation); } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index e102c51c7ee..c63dab63145 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1463,11 +1463,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Type check the pattern. Override if necessary to avoid knock-on errors. - self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr); + self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr, Some(decl.origin)); let pat_ty = self.node_ty(decl.pat.hir_id); self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty); - if let Some(blk) = decl.els { + if let Some(blk) = decl.origin.try_get_else() { let previous_diverges = self.diverges.get(); let else_ty = self.check_block_with_expected(blk, NoExpectation); let cause = self.cause(blk.span, ObligationCauseCode::LetElse); @@ -1485,7 +1485,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_decl(local.into()); } - pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>, is_last: bool) { + pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) { // Don't do all the complex logic below for `DeclItem`. match stmt.kind { hir::StmtKind::Item(..) => return, @@ -1512,14 +1512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); } hir::StmtKind::Semi(ref expr) => { - // All of this is equivalent to calling `check_expr`, but it is inlined out here - // in order to capture the fact that this `match` is the last statement in its - // function. This is done for better suggestions to remove the `;`. - let expectation = match expr.kind { - hir::ExprKind::Match(..) if is_last => IsLast(stmt.span), - _ => NoExpectation, - }; - self.check_expr_with_expectation(expr, expectation); + self.check_expr(expr); } } @@ -1570,8 +1563,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { - for (pos, s) in blk.stmts.iter().enumerate() { - self.check_stmt(s, blk.stmts.len() - 1 == pos); + for s in blk.stmts { + self.check_stmt(s); } // check the tail expression **without** holding the @@ -1594,9 +1587,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &cause, Some(tail_expr), tail_expr_ty, - Some(&mut |diag: &mut Diagnostic| { + |diag| { self.suggest_block_to_brackets(diag, blk, tail_expr_ty, ty_for_diagnostic); - }), + }, false, ); } else { @@ -1633,7 +1626,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coerce.coerce_forced_unit( self, &self.misc(sp), - &mut |err| { + |err| { if let Some(expected_ty) = expected.only_has_type(self) { if blk.stmts.is_empty() && blk.expr.is_none() { self.suggest_boxing_when_appropriate( @@ -1856,19 +1849,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.adjust_fulfillment_error_for_expr_obligation(error) || before_span != error.obligation.cause.span { - // Store both the predicate and the predicate *without constness* - // since sometimes we instantiate and check both of these in a - // method call, for example. remap_cause.insert(( before_span, error.obligation.predicate, error.obligation.cause.clone(), )); - remap_cause.insert(( - before_span, - error.obligation.predicate.without_const(self.tcx), - error.obligation.cause.clone(), - )); } else { // If it failed to be adjusted once around, it may be adjusted // via the "remap cause" mapping the second time... diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index c49e4381897..89bbb4c2203 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -994,14 +994,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let ty = self.normalize(expr.span, ty); if self.can_coerce(found, ty) { - err.multipart_suggestion( - "you might have meant to return this value", - vec![ - (expr.span.shrink_to_lo(), "return ".to_string()), - (expr.span.shrink_to_hi(), ";".to_string()), - ], - Applicability::MaybeIncorrect, - ); + if let Some(node) = self.tcx.hir().find(fn_id) + && let Some(owner_node) = node.as_owner() + && let Some(span) = expr.span.find_ancestor_inside(owner_node.span()) + { + err.multipart_suggestion( + "you might have meant to return this value", + vec![ + (span.shrink_to_lo(), "return ".to_string()), + (span.shrink_to_hi(), ";".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } } } } @@ -1185,10 +1190,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), )) { + let mut span = expr.span; + while expr.span.eq_ctxt(span) && let Some(parent_callsite) = span.parent_callsite() + { + span = parent_callsite; + } + let sugg = if expr.precedence().order() >= PREC_POSTFIX { - vec![(expr.span.shrink_to_hi(), ".into()".to_owned())] + vec![(span.shrink_to_hi(), ".into()".to_owned())] } else { - vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())] + vec![(span.shrink_to_lo(), "(".to_owned()), (span.shrink_to_hi(), ").into()".to_owned())] }; diag.multipart_suggestion( format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"), @@ -1609,8 +1620,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .enumerate() .filter(|x| x.1.hir_id == *hir_id) - .map(|(i, _)| init_tup.get(i).unwrap()) - .next() + .find_map(|(i, _)| init_tup.get(i)) { self.note_type_is_not_clone_inner_expr(init) } else { diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 4f45a24b216..ed4c63f171c 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -9,6 +9,26 @@ use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_trait_selection::traits; +/// Provides context for checking patterns in declarations. More specifically this +/// allows us to infer array types if the pattern is irrefutable and allows us to infer +/// the size of the array. See issue #76342. +#[derive(Debug, Copy, Clone)] +pub(super) enum DeclOrigin<'a> { + // from an `if let` expression + LetExpr, + // from `let x = ..` + LocalDecl { els: Option<&'a hir::Block<'a>> }, +} + +impl<'a> DeclOrigin<'a> { + pub(super) fn try_get_else(&self) -> Option<&'a hir::Block<'a>> { + match self { + Self::LocalDecl { els } => *els, + Self::LetExpr => None, + } + } +} + /// A declaration is an abstraction of [hir::Local] and [hir::Let]. /// /// It must have a hir_id, as this is how we connect gather_locals to the check functions. @@ -18,20 +38,20 @@ pub(super) struct Declaration<'a> { pub ty: Option<&'a hir::Ty<'a>>, pub span: Span, pub init: Option<&'a hir::Expr<'a>>, - pub els: Option<&'a hir::Block<'a>>, + pub origin: DeclOrigin<'a>, } impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> { fn from(local: &'a hir::Local<'a>) -> Self { let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local; - Declaration { hir_id, pat, ty, span, init, els } + Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } } } } impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> { fn from(let_expr: &'a hir::Let<'a>) -> Self { let hir::Let { hir_id, pat, ty, span, init } = *let_expr; - Declaration { hir_id, pat, ty, span, init: Some(init), els: None } + Declaration { hir_id, pat, ty, span, init: Some(init), origin: DeclOrigin::LetExpr } } } diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 0700e2e0554..4ea53c7eaae 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -198,13 +198,14 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } /// Like `pat_ty`, but ignores implicit `&` patterns. + #[instrument(level = "debug", skip(self), ret)] fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { let base_ty = self.node_ty(pat.hir_id)?; - debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty); + trace!(?base_ty); // This code detects whether we are looking at a `ref x`, // and if so, figures out what the type *being borrowed* is. - let ret_ty = match pat.kind { + match pat.kind { PatKind::Binding(..) => { let bm = *self .typeck_results @@ -217,21 +218,18 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. match base_ty.builtin_deref(false) { - Some(t) => t.ty, + Some(t) => Ok(t.ty), None => { - debug!("By-ref binding of non-derefable type {:?}", base_ty); - return Err(()); + debug!("By-ref binding of non-derefable type"); + Err(()) } } } else { - base_ty + Ok(base_ty) } } - _ => base_ty, - }; - debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty); - - Ok(ret_ty) + _ => Ok(base_ty), + } } pub(crate) fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult<PlaceWithHirId<'tcx>> { @@ -299,13 +297,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub(crate) fn cat_expr_unadjusted( &self, expr: &hir::Expr<'_>, ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr); - let expr_ty = self.expr_ty(expr)?; match expr.kind { hir::ExprKind::Unary(hir::UnOp::Deref, ref e_base) => { @@ -319,7 +315,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { hir::ExprKind::Field(ref base, _) => { let base = self.cat_expr(base)?; - debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", expr.hir_id, expr, base); + debug!(?base); let field_idx = self .typeck_results @@ -336,7 +332,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { )) } - hir::ExprKind::Index(ref base, _) => { + hir::ExprKind::Index(ref base, _, _) => { if self.typeck_results.is_method_call(expr) { // If this is an index implemented by a method call, then it // will include an implicit deref of the result. @@ -389,7 +385,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - #[instrument(level = "debug", skip(self, span))] + #[instrument(level = "debug", skip(self, span), ret)] pub(crate) fn cat_res( &self, hir_id: hir::HirId, @@ -430,6 +426,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Note: the actual upvar access contains invisible derefs of closure /// environment and upvar reference as appropriate. Only regionck cares /// about these dereferences, so we let it compute them as needed. + #[instrument(level = "debug", skip(self), ret)] fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult<PlaceWithHirId<'tcx>> { let closure_expr_def_id = self.body_owner; @@ -439,24 +436,20 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { }; let var_ty = self.node_ty(var_id)?; - let ret = PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()); - - debug!("cat_upvar ret={:?}", ret); - Ok(ret) + Ok(PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new())) } + #[instrument(level = "debug", skip(self), ret)] pub(crate) fn cat_rvalue( &self, hir_id: hir::HirId, span: Span, expr_ty: Ty<'tcx>, ) -> PlaceWithHirId<'tcx> { - debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span); - let ret = PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()); - debug!("cat_rvalue ret={:?}", ret); - ret + PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()) } + #[instrument(level = "debug", skip(self, node), ret)] pub(crate) fn cat_projection<N: HirNode>( &self, node: &N, @@ -464,16 +457,23 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ty: Ty<'tcx>, kind: ProjectionKind, ) -> PlaceWithHirId<'tcx> { + let place_ty = base_place.place.ty(); let mut projections = base_place.place.projections; + + let node_ty = self.typeck_results.node_type(node.hir_id()); + // Opaque types can't have field projections, but we can instead convert + // the current place in-place (heh) to the hidden type, and then apply all + // follow up projections on that. + if node_ty != place_ty && place_ty.has_opaque_types() { + projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty }); + } projections.push(Projection { kind, ty }); - let ret = PlaceWithHirId::new( + PlaceWithHirId::new( node.hir_id(), base_place.place.base_ty, base_place.place.base, projections, - ); - debug!("cat_field ret {:?}", ret); - ret + ) } #[instrument(level = "debug", skip(self))] @@ -497,7 +497,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_deref(expr, base) } - #[instrument(level = "debug", skip(self, node))] + #[instrument(level = "debug", skip(self, node), ret)] fn cat_deref( &self, node: &impl HirNode, @@ -514,14 +514,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { let mut projections = base_place.place.projections; projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty }); - let ret = PlaceWithHirId::new( + Ok(PlaceWithHirId::new( node.hir_id(), base_place.place.base_ty, base_place.place.base, projections, - ); - debug!("cat_deref ret {:?}", ret); - Ok(ret) + )) } pub(crate) fn cat_pattern<F>( @@ -603,6 +601,13 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } + /// Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it + /// is being matched against. + /// + /// In general, the way that this works is that we walk down the pattern, + /// constructing a `PlaceWithHirId` that represents the path that will be taken + /// to reach the value being matched. + #[instrument(skip(self, op), ret, level = "debug")] fn cat_pattern_<F>( &self, mut place_with_id: PlaceWithHirId<'tcx>, @@ -612,15 +617,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { where F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), { - // Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it - // is being matched against. - // - // In general, the way that this works is that we walk down the pattern, - // constructing a `PlaceWithHirId` that represents the path that will be taken - // to reach the value being matched. - - debug!("cat_pattern(pat={:?}, place_with_id={:?})", pat, place_with_id); - // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithHirId` correspondingly. // `PlaceWithHirId`s are constructed differently from patterns. For example, in // @@ -654,11 +650,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. for _ in 0..self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) { - debug!("cat_pattern: applying adjustment to place_with_id={:?}", place_with_id); + debug!("applying adjustment to place_with_id={:?}", place_with_id); place_with_id = self.cat_deref(pat, place_with_id)?; } let place_with_id = place_with_id; // lose mutability - debug!("cat_pattern: applied adjustment derefs to get place_with_id={:?}", place_with_id); + debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id); // Invoke the callback, but only now, after the `place_with_id` has adjusted. // diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 356e7022aea..597696843c4 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -341,15 +341,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Construct an obligation let poly_trait_ref = ty::Binder::dummy(trait_ref); - ( - traits::Obligation::new( - self.tcx, - cause, - self.param_env, - poly_trait_ref.without_const(), - ), - args, - ) + (traits::Obligation::new(self.tcx, cause, self.param_env, poly_trait_ref), args) } /// `lookup_method_in_trait` is used for overloaded operators. diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 05eed1923d1..7164102a30e 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1599,8 +1599,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } } - let predicate = - ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx); + let predicate = ty::Binder::dummy(trait_ref).to_predicate(self.tcx); parent_pred = Some(predicate); let obligation = traits::Obligation::new(self.tcx, cause.clone(), self.param_env, predicate); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index f6c07931023..b9d8c5a7540 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -94,7 +94,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, self.body_id, self.param_env, - poly_trait_ref.without_const(), + poly_trait_ref, ); self.predicate_may_hold(&obligation) }) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index d8eb8c71b5e..8fc236f46b2 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1,3 +1,4 @@ +use crate::gather_locals::DeclOrigin; use crate::{errors, FnCtxt, RawTy}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; @@ -77,6 +78,13 @@ struct TopInfo<'tcx> { span: Option<Span>, } +#[derive(Copy, Clone)] +struct PatInfo<'tcx, 'a> { + binding_mode: BindingMode, + top_info: TopInfo<'tcx>, + decl_origin: Option<DeclOrigin<'a>>, +} + impl<'tcx> FnCtxt<'_, 'tcx> { fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { let code = @@ -135,15 +143,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Otherwise, `Some(span)` represents the span of a type expression /// which originated the `expected` type. - pub fn check_pat_top( + pub(crate) fn check_pat_top( &self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, span: Option<Span>, origin_expr: Option<&'tcx hir::Expr<'tcx>>, + decl_origin: Option<DeclOrigin<'tcx>>, ) { let info = TopInfo { expected, origin_expr, span }; - self.check_pat(pat, expected, INITIAL_BM, info); + let pat_info = PatInfo { binding_mode: INITIAL_BM, top_info: info, decl_origin }; + self.check_pat(pat, expected, pat_info); } /// Type check the given `pat` against the `expected` type @@ -151,14 +161,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Outside of this module, `check_pat_top` should always be used. /// Conversely, inside this module, `check_pat_top` should never be used. - #[instrument(level = "debug", skip(self, ti))] - fn check_pat( - &self, - pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) { + #[instrument(level = "debug", skip(self, pat_info))] + fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) { + let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info; let path_res = match &pat.kind { PatKind::Path(qpath) => { Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span)) @@ -167,38 +172,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); + let pat_info = + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin: pat_info.decl_origin }; let ty = match pat.kind { PatKind::Wild => expected, PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti), PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), PatKind::Binding(ba, var_id, _, sub) => { - self.check_pat_ident(pat, ba, var_id, sub, expected, def_bm, ti) + self.check_pat_ident(pat, ba, var_id, sub, expected, pat_info) } PatKind::TupleStruct(ref qpath, subpats, ddpos) => { - self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti) + self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) } PatKind::Path(ref qpath) => { self.check_pat_path(pat, qpath, path_res.unwrap(), expected, ti) } PatKind::Struct(ref qpath, fields, has_rest_pat) => { - self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, def_bm, ti) + self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info) } PatKind::Or(pats) => { for pat in pats { - self.check_pat(pat, expected, def_bm, ti); + self.check_pat(pat, expected, pat_info); } expected } PatKind::Tuple(elements, ddpos) => { - self.check_pat_tuple(pat.span, elements, ddpos, expected, def_bm, ti) - } - PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, def_bm, ti), - PatKind::Ref(inner, mutbl) => { - self.check_pat_ref(pat, inner, mutbl, expected, def_bm, ti) + self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info) } + PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info), + PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info), PatKind::Slice(before, slice, after) => { - self.check_pat_slice(pat.span, before, slice, after, expected, def_bm, ti) + self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) } }; @@ -580,9 +585,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { var_id: HirId, sub: Option<&'tcx Pat<'tcx>>, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { + let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info; + // Determine the binding mode... let bm = match ba { hir::BindingAnnotation::NONE => def_bm, @@ -620,7 +626,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(p) = sub { - self.check_pat(p, expected, def_bm, ti); + self.check_pat(p, expected, pat_info); } local_ty @@ -843,8 +849,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &'tcx [hir::PatField<'tcx>], has_rest_pat: bool, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { // Resolve the path and check the definition for errors. let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) { @@ -852,18 +857,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(guar) => { let err = Ty::new_error(self.tcx, guar); for field in fields { - let ti = ti; - self.check_pat(field.pat, err, def_bm, ti); + self.check_pat(field.pat, err, pat_info); } return err; } }; // Type-check the path. - self.demand_eqtype_pat(pat.span, expected, pat_ty, ti); + self.demand_eqtype_pat(pat.span, expected, pat_ty, pat_info.top_info); // Type-check subpatterns. - if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, has_rest_pat, def_bm, ti) { + if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, has_rest_pat, pat_info) { pat_ty } else { Ty::new_misc_error(self.tcx) @@ -1029,13 +1033,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { subpats: &'tcx [Pat<'tcx>], ddpos: hir::DotDotPos, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { + let PatInfo { binding_mode: def_bm, top_info: ti, decl_origin } = pat_info; let tcx = self.tcx; let on_error = |e| { for pat in subpats { - self.check_pat(pat, Ty::new_error(tcx, e), def_bm, ti); + self.check_pat( + pat, + Ty::new_error(tcx, e), + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, + ); } }; let report_unexpected_res = |res: Res| { @@ -1101,7 +1109,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { let field = &variant.fields[FieldIdx::from_usize(i)]; let field_ty = self.field_ty(subpat.span, field, args); - self.check_pat(subpat, field_ty, def_bm, ti); + self.check_pat( + subpat, + field_ty, + PatInfo { binding_mode: def_bm, top_info: ti, decl_origin }, + ); self.tcx.check_stability( variant.fields[FieldIdx::from_usize(i)].did, @@ -1285,8 +1297,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { elements: &'tcx [Pat<'tcx>], ddpos: hir::DotDotPos, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { let tcx = self.tcx; let mut expected_len = elements.len(); @@ -1307,18 +1318,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let element_tys = tcx.mk_type_list_from_iter(element_tys_iter); let pat_ty = Ty::new_tup(tcx, element_tys); - if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) { + if let Some(mut err) = + self.demand_eqtype_pat_diag(span, expected, pat_ty, pat_info.top_info) + { let reported = err.emit(); // Walk subpatterns with an expected type of `err` in this case to silence // further errors being emitted when using the bindings. #50333 let element_tys_iter = (0..max_len).map(|_| Ty::new_error(tcx, reported)); for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, Ty::new_error(tcx, reported), def_bm, ti); + self.check_pat(elem, Ty::new_error(tcx, reported), pat_info); } Ty::new_tup_from_iter(tcx, element_tys_iter) } else { for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, element_tys[i], def_bm, ti); + self.check_pat(elem, element_tys[i], pat_info); } pat_ty } @@ -1331,8 +1344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant: &'tcx ty::VariantDef, fields: &'tcx [hir::PatField<'tcx>], has_rest_pat: bool, - def_bm: BindingMode, - ti: TopInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> bool { let tcx = self.tcx; @@ -1379,7 +1391,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - self.check_pat(field.pat, field_ty, def_bm, ti); + self.check_pat(field.pat, field_ty, pat_info); } let mut unmentioned_fields = variant @@ -1937,8 +1949,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, inner: &'tcx Pat<'tcx>, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { let tcx = self.tcx; let (box_ty, inner_ty) = match self.check_dereferenceable(span, expected, inner) { @@ -1950,7 +1961,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: inner.span, }); let box_ty = Ty::new_box(tcx, inner_ty); - self.demand_eqtype_pat(span, expected, box_ty, ti); + self.demand_eqtype_pat(span, expected, box_ty, pat_info.top_info); (box_ty, inner_ty) } Err(guar) => { @@ -1958,7 +1969,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (err, err) } }; - self.check_pat(inner, inner_ty, def_bm, ti); + self.check_pat(inner, inner_ty, pat_info); box_ty } @@ -1969,8 +1980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { inner: &'tcx Pat<'tcx>, mutbl: hir::Mutability, expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { let tcx = self.tcx; let expected = self.shallow_resolve(expected); @@ -1992,7 +2002,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); - let err = self.demand_eqtype_pat_diag(pat.span, expected, ref_ty, ti); + let err = self.demand_eqtype_pat_diag( + pat.span, + expected, + ref_ty, + pat_info.top_info, + ); // Look for a case like `fn foo(&foo: u32)` and suggest // `fn foo(foo: &u32)` @@ -2009,7 +2024,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (err, err) } }; - self.check_pat(inner, inner_ty, def_bm, ti); + self.check_pat(inner, inner_ty, pat_info); ref_ty } @@ -2020,6 +2035,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_ref(self.tcx, region, mt) } + fn try_resolve_slice_ty_to_array_ty( + &self, + before: &'tcx [Pat<'tcx>], + slice: Option<&'tcx Pat<'tcx>>, + span: Span, + ) -> Option<Ty<'tcx>> { + if !slice.is_none() { + return None; + } + + let tcx = self.tcx; + let len = before.len(); + let ty_var_origin = + TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }; + let inner_ty = self.next_ty_var(ty_var_origin); + + Some(Ty::new_array(tcx, inner_ty, len.try_into().unwrap())) + } + + /// Used to determines whether we can infer the expected type in the slice pattern to be of type array. + /// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable + /// patterns we wouldn't e.g. report ambiguity in the following situation: + /// + /// ```ignore(rust) + /// struct Zeroes; + /// const ARR: [usize; 2] = [0; 2]; + /// const ARR2: [usize; 2] = [2; 2]; + /// + /// impl Into<&'static [usize; 2]> for Zeroes { + /// fn into(self) -> &'static [usize; 2] { + /// &ARR + /// } + /// } + /// + /// impl Into<&'static [usize]> for Zeroes { + /// fn into(self) -> &'static [usize] { + /// &ARR2 + /// } + /// } + /// + /// fn main() { + /// let &[a, b]: &[usize] = Zeroes.into() else { + /// .. + /// }; + /// } + /// ``` + /// + /// If we're in an irrefutable pattern we prefer the array impl candidate given that + /// the slice impl candidate would be be rejected anyway (if no ambiguity existed). + fn pat_is_irrefutable(&self, decl_origin: Option<DeclOrigin<'_>>) -> bool { + match decl_origin { + Some(DeclOrigin::LocalDecl { els: None }) => true, + Some(DeclOrigin::LocalDecl { els: Some(_) } | DeclOrigin::LetExpr) | None => false, + } + } + /// Type check a slice pattern. /// /// Syntactically, these look like `[pat_0, ..., pat_n]`. @@ -2037,10 +2108,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { slice: Option<&'tcx Pat<'tcx>>, after: &'tcx [Pat<'tcx>], expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, + pat_info: PatInfo<'tcx, '_>, ) -> Ty<'tcx> { + let expected = self.try_structurally_resolve_type(span, expected); + + // If the pattern is irrefutable and `expected` is an infer ty, we try to equate it + // to an array if the given pattern allows it. See issue #76342 + if self.pat_is_irrefutable(pat_info.decl_origin) && expected.is_ty_var() { + if let Some(resolved_arr_ty) = + self.try_resolve_slice_ty_to_array_ty(before, slice, span) + { + debug!(?resolved_arr_ty); + self.demand_eqtype(span, expected, resolved_arr_ty); + } + } + let expected = self.structurally_resolve_type(span, expected); + debug!(?expected); + let (element_ty, opt_slice_ty, inferred) = match *expected.kind() { // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. ty::Array(element_ty, len) => { @@ -2055,10 +2140,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Slice(element_ty) => (element_ty, Some(expected), expected), // The expected type must be an array or slice, but was neither, so error. _ => { - let guar = expected - .error_reported() - .err() - .unwrap_or_else(|| self.error_expected_array_or_slice(span, expected, ti)); + let guar = expected.error_reported().err().unwrap_or_else(|| { + self.error_expected_array_or_slice(span, expected, pat_info.top_info) + }); let err = Ty::new_error(self.tcx, guar); (err, Some(err), err) } @@ -2066,15 +2150,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type check all the patterns before `slice`. for elt in before { - self.check_pat(elt, element_ty, def_bm, ti); + self.check_pat(elt, element_ty, pat_info); } // Type check the `slice`, if present, against its expected type. if let Some(slice) = slice { - self.check_pat(slice, opt_slice_ty.unwrap(), def_bm, ti); + self.check_pat(slice, opt_slice_ty.unwrap(), pat_info); } // Type check the elements after `slice`, if present. for elt in after { - self.check_pat(elt, element_ty, def_bm, ti); + self.check_pat(elt, element_ty, pat_info); } inferred } diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 1eb84eb1637..406434e09e6 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -284,7 +284,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut exprs = vec![expr]; while let hir::ExprKind::Field(ref expr, _) - | hir::ExprKind::Index(ref expr, _) + | hir::ExprKind::Index(ref expr, _, _) | hir::ExprKind::Unary(hir::UnOp::Deref, ref expr) = exprs.last().unwrap().kind { exprs.push(expr); diff --git a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs index 091e88abe97..04d84102336 100644 --- a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs +++ b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs @@ -40,7 +40,7 @@ fn record_rvalue_scope_rec( hir::ExprKind::AddrOf(_, _, subexpr) | hir::ExprKind::Unary(hir::UnOp::Deref, subexpr) | hir::ExprKind::Field(subexpr, _) - | hir::ExprKind::Index(subexpr, _) => { + | hir::ExprKind::Index(subexpr, _, _) => { expr = subexpr; } _ => { diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index be939560c45..1a41786d251 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -264,12 +264,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype(span, closure_kind.to_ty(self.tcx), closure_kind_ty); // If we have an origin, store it. - if let Some(origin) = origin { - let origin = if enable_precise_capture(span) { - (origin.0, origin.1) - } else { - (origin.0, Place { projections: vec![], ..origin.1 }) - }; + if let Some(mut origin) = origin { + if !enable_precise_capture(span) { + // Without precise captures, we just capture the base and ignore + // the projections. + origin.1.projections.clear() + } self.typeck_results .borrow_mut() @@ -294,10 +294,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Equate the type variables for the upvars with the actual types. let final_upvar_tys = self.final_upvar_tys(closure_def_id); - debug!( - "analyze_closure: id={:?} args={:?} final_upvar_tys={:?}", - closure_hir_id, args, final_upvar_tys - ); + debug!(?closure_hir_id, ?args, ?final_upvar_tys); // Build a tuple (U0..Un) of the final upvar types U0..Un // and unify the upvar tuple type in the closure with it: @@ -338,10 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let upvar_ty = captured_place.place.ty(); let capture = captured_place.info.capture_kind; - debug!( - "final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}", - captured_place.place, upvar_ty, capture, captured_place.mutability, - ); + debug!(?captured_place.place, ?upvar_ty, ?capture, ?captured_place.mutability); apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region) }) @@ -679,6 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (p1.kind, p2.kind) { // Paths are the same, continue to next loop. (ProjectionKind::Deref, ProjectionKind::Deref) => {} + (ProjectionKind::OpaqueCast, ProjectionKind::OpaqueCast) => {} (ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _)) if i1 == i2 => {} @@ -701,10 +696,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { l @ (ProjectionKind::Index | ProjectionKind::Subslice | ProjectionKind::Deref + | ProjectionKind::OpaqueCast | ProjectionKind::Field(..)), r @ (ProjectionKind::Index | ProjectionKind::Subslice | ProjectionKind::Deref + | ProjectionKind::OpaqueCast | ProjectionKind::Field(..)), ) => bug!( "ProjectionKinds Index or Subslice were unexpected: ({:?}, {:?})", @@ -1890,6 +1887,7 @@ fn restrict_capture_precision( return (place, curr_mode); } ProjectionKind::Deref => {} + ProjectionKind::OpaqueCast => {} ProjectionKind::Field(..) => {} // ignore } } @@ -1946,6 +1944,7 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String ProjectionKind::Deref => String::from("Deref"), ProjectionKind::Index => String::from("Index"), ProjectionKind::Subslice => String::from("Subslice"), + ProjectionKind::OpaqueCast => String::from("OpaqueCast"), }; if i != 0 { projections_str.push(','); diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 6f47623ec43..603681bbc99 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -210,7 +210,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // to use builtin indexing because the index type is known to be // usize-ish fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) { - if let hir::ExprKind::Index(ref base, ref index) = e.kind { + if let hir::ExprKind::Index(ref base, ref index, _) = e.kind { // All valid indexing looks like this; might encounter non-valid indexes at this point. let base_ty = self.typeck_results.expr_ty_adjusted_opt(base); if base_ty.is_none() { diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 0111a6d302d..8e22ab4083e 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -6,6 +6,7 @@ //! //! ``` //! # #![feature(rustc_attrs)] +//! # #![allow(internal_features)] //! #![rustc_partition_reused(module="spike", cfg="rpass2")] //! #![rustc_partition_codegened(module="spike-x", cfg="rpass2")] //! ``` diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index 6fd9f34b29e..9942c70c4ae 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -12,6 +12,7 @@ test ) )] +#![cfg_attr(all(not(bootstrap), feature = "nightly"), allow(internal_features))] #[cfg(feature = "nightly")] pub mod bit_set; diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 433735e827b..6d5db3336cf 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -481,3 +481,31 @@ impl<'tcx> ToTrace<'tcx> for ty::FnSig<'tcx> { TypeTrace { cause: cause.clone(), values: Sigs(ExpectedFound::new(a_is_expected, a, b)) } } } + +impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialTraitRef<'tcx> { + fn to_trace( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: ExistentialTraitRef(ExpectedFound::new(a_is_expected, a, b)), + } + } +} + +impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> { + fn to_trace( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: ExistentialProjection(ExpectedFound::new(a_is_expected, a, b)), + } + } +} diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 54d901f20da..9d7a9fefd08 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -562,15 +562,9 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { V: TypeFoldable<TyCtxt<'tcx>>, { let needs_canonical_flags = if canonicalize_region_mode.any() { - TypeFlags::HAS_INFER | - TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS` - TypeFlags::HAS_TY_PLACEHOLDER | - TypeFlags::HAS_CT_PLACEHOLDER + TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS } else { - TypeFlags::HAS_INFER - | TypeFlags::HAS_RE_PLACEHOLDER - | TypeFlags::HAS_TY_PLACEHOLDER - | TypeFlags::HAS_CT_PLACEHOLDER + TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER }; // Fast path: nothing that needs to be canonicalized. diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 5a78790ec6e..75cca973306 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -351,6 +351,15 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( ) } } + ty::RePlaceholder(_) => { + explain_free_region( + tcx, + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + ); + } ty::ReError(_) => { err.delay_as_bug(); } @@ -764,7 +773,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { Some(ty) if expected == ty => { let source_map = self.tcx.sess.source_map(); err.span_suggestion( - source_map.end_point(cause.span), + source_map.end_point(cause.span()), "try removing this `?`", "", Applicability::MachineApplicable, @@ -1635,6 +1644,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (false, Mismatch::Fixed(self.tcx.def_descr(expected.def_id))) } ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")), + ValuePairs::ExistentialTraitRef(_) => { + (false, Mismatch::Fixed("existential trait ref")) + } + ValuePairs::ExistentialProjection(_) => { + (false, Mismatch::Fixed("existential projection")) + } }; let Some(vals) = self.values_str(values) else { // Derived error. Cancel the emitter. @@ -2139,6 +2154,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer::Regions(exp_found) => self.expected_found_str(exp_found), infer::Terms(exp_found) => self.expected_found_str_term(exp_found), infer::Aliases(exp_found) => self.expected_found_str(exp_found), + infer::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), + infer::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), infer::TraitRefs(exp_found) => { let pretty_exp_found = ty::error::ExpectedFound { expected: exp_found.expected.print_only_trait_path(), @@ -2567,15 +2584,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { None, ); if let Some(infer::RelateParamBound(_, t, _)) = origin { - let return_impl_trait = - self.tcx.return_type_impl_trait(generic_param_scope).is_some(); let t = self.resolve_vars_if_possible(t); match t.kind() { // We've got: // fn get_later<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ // suggest: // fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a - ty::Closure(..) | ty::Alias(ty::Opaque, ..) if return_impl_trait => { + ty::Closure(..) | ty::Alias(ty::Opaque, ..) => { new_binding_suggestion(&mut err, type_param_span); } _ => { diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index be424424f3a..60d9d6578f5 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -942,6 +942,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { generic_ty: Ty<'tcx>, min: ty::Region<'tcx>, ) -> bool { + if let ty::ReError(_) = *min { + return true; + } + match bound { VerifyBound::IfEq(verify_if_eq_b) => { let verify_if_eq_b = var_values.normalize(self.region_rels.tcx, *verify_if_eq_b); diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a73e84f94c4..aaabf1482e2 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -374,6 +374,8 @@ pub enum ValuePairs<'tcx> { TraitRefs(ExpectedFound<ty::TraitRef<'tcx>>), PolyTraitRefs(ExpectedFound<ty::PolyTraitRef<'tcx>>), Sigs(ExpectedFound<ty::FnSig<'tcx>>), + ExistentialTraitRef(ExpectedFound<ty::PolyExistentialTraitRef<'tcx>>), + ExistentialProjection(ExpectedFound<ty::PolyExistentialProjection<'tcx>>), } impl<'tcx> ValuePairs<'tcx> { diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 9c90b704586..1c3a5c36076 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -64,7 +64,7 @@ impl<'tcx> InferCtxt<'tcx> { ct_op: |ct| ct, ty_op: |ty| match *ty.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) - if replace_opaque_type(def_id) => + if replace_opaque_type(def_id) && !ty.has_escaping_bound_vars() => { let def_span = self.tcx.def_span(def_id); let span = if span.contains(def_span) { def_span } else { span }; @@ -448,7 +448,9 @@ where ty::Closure(_, ref args) => { // Skip lifetime parameters of the enclosing item(s) - args.as_closure().tupled_upvars_ty().visit_with(self); + for upvar in args.as_closure().upvar_tys() { + upvar.visit_with(self); + } args.as_closure().sig_as_fn_ptr_ty().visit_with(self); } @@ -456,7 +458,9 @@ where // Skip lifetime parameters of the enclosing item(s) // Also skip the witness type, because that has no free regions. - args.as_generator().tupled_upvars_ty().visit_with(self); + for upvar in args.as_generator().upvar_tys() { + upvar.visit_with(self); + } args.as_generator().return_ty().visit_with(self); args.as_generator().yield_ty().visit_with(self); args.as_generator().resume_ty().visit_with(self); diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 11f43469400..64b9714c7c0 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -25,7 +25,7 @@ pub trait TraitEngine<'tcx>: 'tcx { cause, recursion_depth: 0, param_env, - predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(infcx.tcx), + predicate: ty::Binder::dummy(trait_ref).to_predicate(infcx.tcx), }, ); } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index a9da6104b38..dc41630196b 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -77,13 +77,6 @@ impl<'tcx> PredicateObligation<'tcx> { recursion_depth: self.recursion_depth, }) } - - pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> PredicateObligation<'tcx> { - if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = self.predicate.kind().skip_binder() && trait_pred.is_const_if_const() { - self.predicate = tcx.mk_predicate(self.predicate.kind().map_bound(|_| ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred.without_const())))); - } - self - } } impl<'tcx> PolyTraitObligation<'tcx> { diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 87ba6b3ec50..93dfbe63bcd 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -264,11 +264,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { }; let obligations = - predicates.predicates.iter().enumerate().map(|(index, &(mut clause, span))| { - // when parent predicate is non-const, elaborate it to non-const predicates. - if data.constness == ty::BoundConstness::NotConst { - clause = clause.without_const(tcx); - } + predicates.predicates.iter().enumerate().map(|(index, &(clause, span))| { elaboratable.child_with_derived_cause( clause.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)), span, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 6b3facd041c..a34fdf4ecc9 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -846,10 +846,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { }, { sess.time("lint_checking", || { - rustc_lint::check_crate(tcx, || { - rustc_lint::BuiltinCombinedLateLintPass::new() - }); + rustc_lint::check_crate(tcx); }); + }, + { + tcx.ensure().clashing_extern_declarations(()); } ); }, diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 16e17fc9d6a..c4a7f717840 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -72,6 +72,9 @@ lint_builtin_incomplete_features = the feature `{$name}` is incomplete and may n .note = see issue #{$n} <https://github.com/rust-lang/rust/issues/{$n}> for more information .help = consider using `min_{$name}` instead, which is more stable and complete +lint_builtin_internal_features = the feature `{$name}` is internal to the compiler or standard library + .note = using it is strongly discouraged + lint_builtin_keyword_idents = `{$kw}` is a keyword in the {$next} edition .suggestion = you can use a raw identifier to stay compatible @@ -127,8 +130,6 @@ lint_builtin_unexpected_cli_config_name = unexpected `{$name}` as condition name lint_builtin_unexpected_cli_config_value = unexpected condition value `{$value}` for condition name `{$name}` .help = was set with `--cfg` but isn't in the `--check-cfg` expected values -lint_builtin_unnameable_test_items = cannot test inner items - lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done @@ -166,8 +167,9 @@ lint_check_name_warning = {$msg} lint_command_line_source = `forbid` lint level was set on command line -lint_confusable_identifier_pair = identifier pair considered confusable between `{$existing_sym}` and `{$sym}` - .label = this is where the previous identifier occurred +lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as identifiers, which look alike + .current_use = this identifier can be confused with `{$existing_sym}` + .other_use = other identifier used here lint_cstring_ptr = getting the inner pointer of a temporary `CString` .as_ptr_label = this pointer will be invalid @@ -213,9 +215,6 @@ lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message .rationale = {$rationale} -lint_fn_null_check = function pointers are not nullable, so checking them for null will always return false - .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value - lint_for_loops_over_fallibles = for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement .suggestion = consider using `if let` to clear intent @@ -318,7 +317,11 @@ lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be dir lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable -lint_invalid_reference_casting = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` +lint_invalid_reference_casting_assign_to_ref = assigning to `&T` is undefined behavior, consider using an `UnsafeCell` + .label = casting happend here + +lint_invalid_reference_casting_borrow_as_mut = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` + .label = casting happend here lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead @@ -450,6 +453,13 @@ lint_path_statement_drop = path statement drops value lint_path_statement_no_effect = path statement with no effect +lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking them for null will always return false + .help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value + .label = expression has type `{$orig_ty}` + +lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false + .label = expression has type `{$orig_ty}` + lint_query_instability = using `{$query}` can result in unstable query results .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index e6917f4b2d3..2c9d212a6a6 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -24,24 +24,22 @@ use crate::fluent_generated as fluent; use crate::{ errors::BuiltinEllipsisInclusiveRangePatterns, lints::{ - BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinClashingExtern, - BuiltinClashingExternSub, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink, - BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr, - BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives, - BuiltinExplicitOutlivesSuggestion, BuiltinIncompleteFeatures, - BuiltinIncompleteFeaturesHelp, BuiltinIncompleteFeaturesNote, BuiltinKeywordIdents, + BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinConstNoMangle, + BuiltinDeprecatedAttrLink, BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, + BuiltinDerefNullptr, BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives, + BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures, + BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue, - BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit, + BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, SuggestChangingAssocTypes, }, - types::{transparent_newtype_field, CItemKind}, - EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext, + EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, }; use hir::IsAsync; use rustc_ast::attr; @@ -49,29 +47,29 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust::{self, expr_to_string}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, DecorateLint, MultiSpan}; use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; +use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::intravisit::FnKind as HirFnKind; -use rustc_hir::{Body, FnDecl, ForeignItemKind, GenericParamKind, Node, PatKind, PredicateOrigin}; +use rustc_hir::{Body, FnDecl, GenericParamKind, Node, PatKind, PredicateOrigin}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::layout::{LayoutError, LayoutOf}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::GenericArgKind; +use rustc_middle::ty::ToPredicate; use rustc_middle::ty::TypeVisitableExt; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef}; +use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef}; use rustc_session::config::ExpectedValues; use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason}; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, InnerSpan, Span}; -use rustc_target::abi::{Abi, FIRST_VARIANT}; +use rustc_target::abi::Abi; use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy}; use crate::nonstandard_style::{method_context, MethodLateContext}; @@ -461,10 +459,7 @@ declare_lint! { report_in_external_macro } -pub struct MissingDoc { - /// Stack of whether `#[doc(hidden)]` is set at each level which has lint attributes. - doc_hidden_stack: Vec<bool>, -} +pub struct MissingDoc; impl_lint_pass!(MissingDoc => [MISSING_DOCS]); @@ -493,14 +488,6 @@ fn has_doc(attr: &ast::Attribute) -> bool { } impl MissingDoc { - pub fn new() -> MissingDoc { - MissingDoc { doc_hidden_stack: vec![false] } - } - - fn doc_hidden(&self) -> bool { - *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") - } - fn check_missing_docs_attrs( &self, cx: &LateContext<'_>, @@ -514,11 +501,6 @@ impl MissingDoc { return; } - // `#[doc(hidden)]` disables missing_docs check. - if self.doc_hidden() { - return; - } - // Only check publicly-visible items, using the result from the privacy pass. // It's an option so the crate root can also use this function (it doesn't // have a `NodeId`). @@ -541,23 +523,6 @@ impl MissingDoc { } impl<'tcx> LateLintPass<'tcx> for MissingDoc { - #[inline] - fn enter_lint_attrs(&mut self, _cx: &LateContext<'_>, attrs: &[ast::Attribute]) { - let doc_hidden = self.doc_hidden() - || attrs.iter().any(|attr| { - attr.has_name(sym::doc) - && match attr.meta_item_list() { - None => false, - Some(l) => attr::list_contains_name(&l, sym::hidden), - } - }); - self.doc_hidden_stack.push(doc_hidden); - } - - fn exit_lint_attrs(&mut self, _: &LateContext<'_>, _attrs: &[ast::Attribute]) { - self.doc_hidden_stack.pop().expect("empty doc_hidden_stack"); - } - fn check_crate(&mut self, cx: &LateContext<'_>) { self.check_missing_docs_attrs(cx, CRATE_DEF_ID, "the", "crate"); } @@ -710,6 +675,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { if ty.is_copy_modulo_regions(cx.tcx, param_env) { return; } + if type_implements_negative_copy_modulo_regions(cx.tcx, ty, param_env) { + return; + } // We shouldn't recommend implementing `Copy` on stateful things, // such as iterators. @@ -745,6 +713,24 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { } } +/// Check whether a `ty` has a negative `Copy` implementation, ignoring outlives constraints. +fn type_implements_negative_copy_modulo_regions<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> bool { + let trait_ref = ty::TraitRef::new(tcx, tcx.require_lang_item(hir::LangItem::Copy, None), [ty]); + let pred = ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Negative }; + let obligation = traits::Obligation { + cause: traits::ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: ty::Binder::dummy(pred).to_predicate(tcx), + }; + + tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation) +} + declare_lint! { /// The `missing_debug_implementations` lint detects missing /// implementations of [`fmt::Debug`] for public types. @@ -778,9 +764,7 @@ declare_lint! { } #[derive(Default)] -pub struct MissingDebugImplementations { - impling_types: Option<LocalDefIdSet>, -} +pub(crate) struct MissingDebugImplementations; impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]); @@ -795,23 +779,20 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { _ => return, } - let Some(debug) = cx.tcx.get_diagnostic_item(sym::Debug) else { return }; - - if self.impling_types.is_none() { - let mut impls = LocalDefIdSet::default(); - cx.tcx.for_each_impl(debug, |d| { - if let Some(ty_def) = cx.tcx.type_of(d).instantiate_identity().ty_adt_def() { - if let Some(def_id) = ty_def.did().as_local() { - impls.insert(def_id); - } - } - }); - - self.impling_types = Some(impls); - debug!("{:?}", self.impling_types); + // Avoid listing trait impls if the trait is allowed. + let (level, _) = cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()); + if level == Level::Allow { + return; } - if !self.impling_types.as_ref().unwrap().contains(&item.owner_id.def_id) { + let Some(debug) = cx.tcx.get_diagnostic_item(sym::Debug) else { return }; + + let has_impl = cx + .tcx + .non_blanket_impls_for_ty(debug, cx.tcx.type_of(item.owner_id).instantiate_identity()) + .next() + .is_some(); + if !has_impl { cx.emit_spanned_lint( MISSING_DEBUG_IMPLEMENTATIONS, item.span, @@ -1259,8 +1240,8 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { declare_lint! { /// The `ungated_async_fn_track_caller` lint warns when the - /// `#[track_caller]` attribute is used on an async function, method, or - /// closure, without enabling the corresponding unstable feature flag. + /// `#[track_caller]` attribute is used on an async function + /// without enabling the corresponding unstable feature flag. /// /// ### Example /// @@ -1274,13 +1255,13 @@ declare_lint! { /// ### Explanation /// /// The attribute must be used in conjunction with the - /// [`closure_track_caller` feature flag]. Otherwise, the `#[track_caller]` + /// [`async_fn_track_caller` feature flag]. Otherwise, the `#[track_caller]` /// annotation will function as a no-op. /// - /// [`closure_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/closure-track-caller.html + /// [`async_fn_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/async-fn-track-caller.html UNGATED_ASYNC_FN_TRACK_CALLER, Warn, - "enabling track_caller on an async fn is a no-op unless the closure_track_caller feature is enabled" + "enabling track_caller on an async fn is a no-op unless the async_fn_track_caller feature is enabled" } declare_lint_pass!( @@ -1300,7 +1281,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { def_id: LocalDefId, ) { if fn_kind.asyncness() == IsAsync::Async - && !cx.tcx.features().closure_track_caller + && !cx.tcx.features().async_fn_track_caller // Now, check if the function has the `#[track_caller]` attribute && let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller) { @@ -1771,82 +1752,6 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } declare_lint! { - /// The `unnameable_test_items` lint detects [`#[test]`][test] functions - /// that are not able to be run by the test harness because they are in a - /// position where they are not nameable. - /// - /// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute - /// - /// ### Example - /// - /// ```rust,test - /// fn main() { - /// #[test] - /// fn foo() { - /// // This test will not fail because it does not run. - /// assert_eq!(1, 2); - /// } - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// In order for the test harness to run a test, the test function must be - /// located in a position where it can be accessed from the crate root. - /// This generally means it must be defined in a module, and not anywhere - /// else such as inside another function. The compiler previously allowed - /// this without an error, so a lint was added as an alert that a test is - /// not being used. Whether or not this should be allowed has not yet been - /// decided, see [RFC 2471] and [issue #36629]. - /// - /// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443 - /// [issue #36629]: https://github.com/rust-lang/rust/issues/36629 - UNNAMEABLE_TEST_ITEMS, - Warn, - "detects an item that cannot be named being marked as `#[test_case]`", - report_in_external_macro -} - -pub struct UnnameableTestItems { - boundary: Option<hir::OwnerId>, // Id of the item under which things are not nameable - items_nameable: bool, -} - -impl_lint_pass!(UnnameableTestItems => [UNNAMEABLE_TEST_ITEMS]); - -impl UnnameableTestItems { - pub fn new() -> Self { - Self { boundary: None, items_nameable: true } - } -} - -impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems { - fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - if self.items_nameable { - if let hir::ItemKind::Mod(..) = it.kind { - } else { - self.items_nameable = false; - self.boundary = Some(it.owner_id); - } - return; - } - - let attrs = cx.tcx.hir().attrs(it.hir_id()); - if let Some(attr) = attr::find_by_name(attrs, sym::rustc_test_marker) { - cx.emit_spanned_lint(UNNAMEABLE_TEST_ITEMS, attr.span, BuiltinUnnameableTestItems); - } - } - - fn check_item_post(&mut self, _cx: &LateContext<'_>, it: &hir::Item<'_>) { - if !self.items_nameable && self.boundary == Some(it.owner_id) { - self.items_nameable = true; - } - } -} - -declare_lint! { /// The `keyword_idents` lint detects edition keywords being used as an /// identifier. /// @@ -2301,12 +2206,36 @@ declare_lint! { "incomplete features that may function improperly in some or all cases" } +declare_lint! { + /// The `internal_features` lint detects unstable features enabled with + /// the [`feature` attribute] that are internal to the compiler or standard + /// library. + /// + /// [`feature` attribute]: https://doc.rust-lang.org/nightly/unstable-book/ + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![feature(rustc_attrs)] + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// These features are an implementation detail of the compiler and standard + /// library and are not supposed to be used in user code. + pub INTERNAL_FEATURES, + Deny, + "internal features are not supposed to be used" +} + declare_lint_pass!( /// Check for used feature gates in `INCOMPLETE_FEATURES` in `rustc_feature/src/active.rs`. - IncompleteFeatures => [INCOMPLETE_FEATURES] + IncompleteInternalFeatures => [INCOMPLETE_FEATURES, INTERNAL_FEATURES] ); -impl EarlyLintPass for IncompleteFeatures { +impl EarlyLintPass for IncompleteInternalFeatures { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { let features = cx.sess().features_untracked(); features @@ -2314,17 +2243,26 @@ impl EarlyLintPass for IncompleteFeatures { .iter() .map(|(name, span, _)| (name, span)) .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span))) - .filter(|(&name, _)| features.incomplete(name)) + .filter(|(&name, _)| features.incomplete(name) || features.internal(name)) .for_each(|(&name, &span)| { let note = rustc_feature::find_feature_issue(name, GateIssue::Language) - .map(|n| BuiltinIncompleteFeaturesNote { n }); - let help = - HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp); - cx.emit_spanned_lint( - INCOMPLETE_FEATURES, - span, - BuiltinIncompleteFeatures { name, note, help }, - ); + .map(|n| BuiltinFeatureIssueNote { n }); + + if features.incomplete(name) { + let help = + HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp); + cx.emit_spanned_lint( + INCOMPLETE_FEATURES, + span, + BuiltinIncompleteFeatures { name, note, help }, + ); + } else { + cx.emit_spanned_lint( + INTERNAL_FEATURES, + span, + BuiltinInternalFeatures { name, note }, + ); + } }); } } @@ -2656,381 +2594,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { } declare_lint! { - /// The `clashing_extern_declarations` lint detects when an `extern fn` - /// has been declared with the same name but different types. - /// - /// ### Example - /// - /// ```rust - /// mod m { - /// extern "C" { - /// fn foo(); - /// } - /// } - /// - /// extern "C" { - /// fn foo(_: u32); - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Because two symbols of the same name cannot be resolved to two - /// different functions at link time, and one function cannot possibly - /// have two types, a clashing extern declaration is almost certainly a - /// mistake. Check to make sure that the `extern` definitions are correct - /// and equivalent, and possibly consider unifying them in one location. - /// - /// This lint does not run between crates because a project may have - /// dependencies which both rely on the same extern function, but declare - /// it in a different (but valid) way. For example, they may both declare - /// an opaque type for one or more of the arguments (which would end up - /// distinct types), or use types that are valid conversions in the - /// language the `extern fn` is defined in. In these cases, the compiler - /// can't say that the clashing declaration is incorrect. - pub CLASHING_EXTERN_DECLARATIONS, - Warn, - "detects when an extern fn has been declared with the same name but different types" -} - -pub struct ClashingExternDeclarations { - /// Map of function symbol name to the first-seen hir id for that symbol name.. If seen_decls - /// contains an entry for key K, it means a symbol with name K has been seen by this lint and - /// the symbol should be reported as a clashing declaration. - // FIXME: Technically, we could just store a &'tcx str here without issue; however, the - // `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime. - seen_decls: FxHashMap<Symbol, hir::OwnerId>, -} - -/// Differentiate between whether the name for an extern decl came from the link_name attribute or -/// just from declaration itself. This is important because we don't want to report clashes on -/// symbol name if they don't actually clash because one or the other links against a symbol with a -/// different name. -enum SymbolName { - /// The name of the symbol + the span of the annotation which introduced the link name. - Link(Symbol, Span), - /// No link name, so just the name of the symbol. - Normal(Symbol), -} - -impl SymbolName { - fn get_name(&self) -> Symbol { - match self { - SymbolName::Link(s, _) | SymbolName::Normal(s) => *s, - } - } -} - -impl ClashingExternDeclarations { - pub(crate) fn new() -> Self { - ClashingExternDeclarations { seen_decls: FxHashMap::default() } - } - - /// Insert a new foreign item into the seen set. If a symbol with the same name already exists - /// for the item, return its HirId without updating the set. - fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option<hir::OwnerId> { - let did = fi.owner_id.to_def_id(); - let instance = Instance::new(did, ty::List::identity_for_item(tcx, did)); - let name = Symbol::intern(tcx.symbol_name(instance).name); - if let Some(&existing_id) = self.seen_decls.get(&name) { - // Avoid updating the map with the new entry when we do find a collision. We want to - // make sure we're always pointing to the first definition as the previous declaration. - // This lets us avoid emitting "knock-on" diagnostics. - Some(existing_id) - } else { - self.seen_decls.insert(name, fi.owner_id) - } - } - - /// Get the name of the symbol that's linked against for a given extern declaration. That is, - /// the name specified in a #[link_name = ...] attribute if one was specified, else, just the - /// symbol's name. - fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> SymbolName { - if let Some((overridden_link_name, overridden_link_name_span)) = - tcx.codegen_fn_attrs(fi.owner_id).link_name.map(|overridden_link_name| { - // FIXME: Instead of searching through the attributes again to get span - // information, we could have codegen_fn_attrs also give span information back for - // where the attribute was defined. However, until this is found to be a - // bottleneck, this does just fine. - (overridden_link_name, tcx.get_attr(fi.owner_id, sym::link_name).unwrap().span) - }) - { - SymbolName::Link(overridden_link_name, overridden_link_name_span) - } else { - SymbolName::Normal(fi.ident.name) - } - } - - /// Checks whether two types are structurally the same enough that the declarations shouldn't - /// clash. We need this so we don't emit a lint when two modules both declare an extern struct, - /// with the same members (as the declarations shouldn't clash). - fn structurally_same_type<'tcx>( - cx: &LateContext<'tcx>, - a: Ty<'tcx>, - b: Ty<'tcx>, - ckind: CItemKind, - ) -> bool { - fn structurally_same_type_impl<'tcx>( - seen_types: &mut FxHashSet<(Ty<'tcx>, Ty<'tcx>)>, - cx: &LateContext<'tcx>, - a: Ty<'tcx>, - b: Ty<'tcx>, - ckind: CItemKind, - ) -> bool { - debug!("structurally_same_type_impl(cx, a = {:?}, b = {:?})", a, b); - let tcx = cx.tcx; - - // Given a transparent newtype, reach through and grab the inner - // type unless the newtype makes the type non-null. - let non_transparent_ty = |mut ty: Ty<'tcx>| -> Ty<'tcx> { - loop { - if let ty::Adt(def, args) = *ty.kind() { - let is_transparent = def.repr().transparent(); - let is_non_null = crate::types::nonnull_optimization_guaranteed(tcx, def); - debug!( - "non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}", - ty, is_transparent, is_non_null - ); - if is_transparent && !is_non_null { - debug_assert_eq!(def.variants().len(), 1); - let v = &def.variant(FIRST_VARIANT); - // continue with `ty`'s non-ZST field, - // otherwise `ty` is a ZST and we can return - if let Some(field) = transparent_newtype_field(tcx, v) { - ty = field.ty(tcx, args); - continue; - } - } - } - debug!("non_transparent_ty -> {:?}", ty); - return ty; - } - }; - - let a = non_transparent_ty(a); - let b = non_transparent_ty(b); - - if !seen_types.insert((a, b)) { - // We've encountered a cycle. There's no point going any further -- the types are - // structurally the same. - true - } else if a == b { - // All nominally-same types are structurally same, too. - true - } else { - // Do a full, depth-first comparison between the two. - use rustc_type_ir::sty::TyKind::*; - let a_kind = a.kind(); - let b_kind = b.kind(); - - let compare_layouts = |a, b| -> Result<bool, LayoutError<'tcx>> { - debug!("compare_layouts({:?}, {:?})", a, b); - let a_layout = &cx.layout_of(a)?.layout.abi(); - let b_layout = &cx.layout_of(b)?.layout.abi(); - debug!( - "comparing layouts: {:?} == {:?} = {}", - a_layout, - b_layout, - a_layout == b_layout - ); - Ok(a_layout == b_layout) - }; - - #[allow(rustc::usage_of_ty_tykind)] - let is_primitive_or_pointer = |kind: &ty::TyKind<'_>| { - kind.is_primitive() || matches!(kind, RawPtr(..) | Ref(..)) - }; - - ensure_sufficient_stack(|| { - match (a_kind, b_kind) { - (Adt(a_def, _), Adt(b_def, _)) => { - // We can immediately rule out these types as structurally same if - // their layouts differ. - match compare_layouts(a, b) { - Ok(false) => return false, - _ => (), // otherwise, continue onto the full, fields comparison - } - - // Grab a flattened representation of all fields. - let a_fields = a_def.variants().iter().flat_map(|v| v.fields.iter()); - let b_fields = b_def.variants().iter().flat_map(|v| v.fields.iter()); - - // Perform a structural comparison for each field. - a_fields.eq_by( - b_fields, - |&ty::FieldDef { did: a_did, .. }, - &ty::FieldDef { did: b_did, .. }| { - structurally_same_type_impl( - seen_types, - cx, - tcx.type_of(a_did).instantiate_identity(), - tcx.type_of(b_did).instantiate_identity(), - ckind, - ) - }, - ) - } - (Array(a_ty, a_const), Array(b_ty, b_const)) => { - // For arrays, we also check the constness of the type. - a_const.kind() == b_const.kind() - && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind) - } - (Slice(a_ty), Slice(b_ty)) => { - structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind) - } - (RawPtr(a_tymut), RawPtr(b_tymut)) => { - a_tymut.mutbl == b_tymut.mutbl - && structurally_same_type_impl( - seen_types, cx, a_tymut.ty, b_tymut.ty, ckind, - ) - } - (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { - // For structural sameness, we don't need the region to be same. - a_mut == b_mut - && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind) - } - (FnDef(..), FnDef(..)) => { - let a_poly_sig = a.fn_sig(tcx); - let b_poly_sig = b.fn_sig(tcx); - - // We don't compare regions, but leaving bound regions around ICEs, so - // we erase them. - let a_sig = tcx.erase_late_bound_regions(a_poly_sig); - let b_sig = tcx.erase_late_bound_regions(b_poly_sig); - - (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) - == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) - && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { - structurally_same_type_impl(seen_types, cx, *a, *b, ckind) - }) - && structurally_same_type_impl( - seen_types, - cx, - a_sig.output(), - b_sig.output(), - ckind, - ) - } - (Tuple(a_args), Tuple(b_args)) => { - a_args.iter().eq_by(b_args.iter(), |a_ty, b_ty| { - structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind) - }) - } - // For these, it's not quite as easy to define structural-sameness quite so easily. - // For the purposes of this lint, take the conservative approach and mark them as - // not structurally same. - (Dynamic(..), Dynamic(..)) - | (Error(..), Error(..)) - | (Closure(..), Closure(..)) - | (Generator(..), Generator(..)) - | (GeneratorWitness(..), GeneratorWitness(..)) - | (Alias(ty::Projection, ..), Alias(ty::Projection, ..)) - | (Alias(ty::Inherent, ..), Alias(ty::Inherent, ..)) - | (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false, - - // These definitely should have been caught above. - (Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(), - - // An Adt and a primitive or pointer type. This can be FFI-safe if non-null - // enum layout optimisation is being applied. - (Adt(..), other_kind) | (other_kind, Adt(..)) - if is_primitive_or_pointer(other_kind) => - { - let (primitive, adt) = - if is_primitive_or_pointer(a.kind()) { (a, b) } else { (b, a) }; - if let Some(ty) = crate::types::repr_nullable_ptr(cx, adt, ckind) { - ty == primitive - } else { - compare_layouts(a, b).unwrap_or(false) - } - } - // Otherwise, just compare the layouts. This may fail to lint for some - // incompatible types, but at the very least, will stop reads into - // uninitialised memory. - _ => compare_layouts(a, b).unwrap_or(false), - } - }) - } - } - let mut seen_types = FxHashSet::default(); - structurally_same_type_impl(&mut seen_types, cx, a, b, ckind) - } -} - -impl_lint_pass!(ClashingExternDeclarations => [CLASHING_EXTERN_DECLARATIONS]); - -impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations { - #[instrument(level = "trace", skip(self, cx))] - fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, this_fi: &hir::ForeignItem<'_>) { - if let ForeignItemKind::Fn(..) = this_fi.kind { - let tcx = cx.tcx; - if let Some(existing_did) = self.insert(tcx, this_fi) { - let existing_decl_ty = tcx.type_of(existing_did).skip_binder(); - let this_decl_ty = tcx.type_of(this_fi.owner_id).instantiate_identity(); - debug!( - "ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}", - existing_did, existing_decl_ty, this_fi.owner_id, this_decl_ty - ); - // Check that the declarations match. - if !Self::structurally_same_type( - cx, - existing_decl_ty, - this_decl_ty, - CItemKind::Declaration, - ) { - let orig_fi = tcx.hir().expect_foreign_item(existing_did); - let orig = Self::name_of_extern_decl(tcx, orig_fi); - - // We want to ensure that we use spans for both decls that include where the - // name was defined, whether that was from the link_name attribute or not. - let get_relevant_span = - |fi: &hir::ForeignItem<'_>| match Self::name_of_extern_decl(tcx, fi) { - SymbolName::Normal(_) => fi.span, - SymbolName::Link(_, annot_span) => fi.span.to(annot_span), - }; - - // Finally, emit the diagnostic. - let this = this_fi.ident.name; - let orig = orig.get_name(); - let previous_decl_label = get_relevant_span(orig_fi); - let mismatch_label = get_relevant_span(this_fi); - let sub = BuiltinClashingExternSub { - tcx, - expected: existing_decl_ty, - found: this_decl_ty, - }; - let decorator = if orig == this { - BuiltinClashingExtern::SameName { - this, - orig, - previous_decl_label, - mismatch_label, - sub, - } - } else { - BuiltinClashingExtern::DiffName { - this, - orig, - previous_decl_label, - mismatch_label, - sub, - } - }; - tcx.emit_spanned_lint( - CLASHING_EXTERN_DECLARATIONS, - this_fi.hir_id(), - get_relevant_span(this_fi), - decorator, - ); - } - } - } - } -} - -declare_lint! { /// The `deref_nullptr` lint detects when an null pointer is dereferenced, /// which causes [undefined behavior]. /// diff --git a/compiler/rustc_lint/src/fn_null_check.rs b/compiler/rustc_lint/src/fn_null_check.rs deleted file mode 100644 index e3b33463ccf..00000000000 --- a/compiler/rustc_lint/src/fn_null_check.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::{lints::FnNullCheckDiag, LateContext, LateLintPass, LintContext}; -use rustc_ast::LitKind; -use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; -use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::sym; - -declare_lint! { - /// The `incorrect_fn_null_checks` lint checks for expression that checks if a - /// function pointer is null. - /// - /// ### Example - /// - /// ```rust - /// # fn test() {} - /// let fn_ptr: fn() = /* somehow obtained nullable function pointer */ - /// # test; - /// - /// if (fn_ptr as *const ()).is_null() { /* ... */ } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Function pointers are assumed to be non-null, checking them for null will always - /// return false. - INCORRECT_FN_NULL_CHECKS, - Warn, - "incorrect checking of null function pointer" -} - -declare_lint_pass!(IncorrectFnNullChecks => [INCORRECT_FN_NULL_CHECKS]); - -fn is_fn_ptr_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let mut expr = expr.peel_blocks(); - let mut had_at_least_one_cast = false; - while let ExprKind::Cast(cast_expr, cast_ty) = expr.kind - && let TyKind::Ptr(_) = cast_ty.kind { - expr = cast_expr.peel_blocks(); - had_at_least_one_cast = true; - } - had_at_least_one_cast && cx.typeck_results().expr_ty_adjusted(expr).is_fn() -} - -impl<'tcx> LateLintPass<'tcx> for IncorrectFnNullChecks { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - match expr.kind { - // Catching: - // <*<const/mut> <ty>>::is_null(fn_ptr as *<const/mut> <ty>) - ExprKind::Call(path, [arg]) - if let ExprKind::Path(ref qpath) = path.kind - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && matches!( - cx.tcx.get_diagnostic_name(def_id), - Some(sym::ptr_const_is_null | sym::ptr_is_null) - ) - && is_fn_ptr_cast(cx, arg) => - { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) - } - - // Catching: - // (fn_ptr as *<const/mut> <ty>).is_null() - ExprKind::MethodCall(_, receiver, _, _) - if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && matches!( - cx.tcx.get_diagnostic_name(def_id), - Some(sym::ptr_const_is_null | sym::ptr_is_null) - ) - && is_fn_ptr_cast(cx, receiver) => - { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) - } - - ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { - let to_check: &Expr<'_>; - if is_fn_ptr_cast(cx, left) { - to_check = right; - } else if is_fn_ptr_cast(cx, right) { - to_check = left; - } else { - return; - } - - match to_check.kind { - // Catching: - // (fn_ptr as *<const/mut> <ty>) == (0 as <ty>) - ExprKind::Cast(cast_expr, _) - if let ExprKind::Lit(spanned) = cast_expr.kind - && let LitKind::Int(v, _) = spanned.node && v == 0 => - { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) - }, - - // Catching: - // (fn_ptr as *<const/mut> <ty>) == std::ptr::null() - ExprKind::Call(path, []) - if let ExprKind::Path(ref qpath) = path.kind - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) - && (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut) => - { - cx.emit_spanned_lint(INCORRECT_FN_NULL_CHECKS, expr.span, FnNullCheckDiag) - }, - - _ => {}, - } - } - _ => {} - } - } -} diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs new file mode 100644 index 00000000000..7b291d558e0 --- /dev/null +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -0,0 +1,402 @@ +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_middle::query::Providers; +use rustc_middle::ty::layout::LayoutError; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_session::lint::{lint_array, LintArray}; +use rustc_span::{sym, Span, Symbol}; +use rustc_target::abi::FIRST_VARIANT; + +use crate::lints::{BuiltinClashingExtern, BuiltinClashingExternSub}; +use crate::types; + +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { clashing_extern_declarations, ..*providers }; +} + +pub(crate) fn get_lints() -> LintArray { + lint_array!(CLASHING_EXTERN_DECLARATIONS) +} + +fn clashing_extern_declarations(tcx: TyCtxt<'_>, (): ()) { + let mut lint = ClashingExternDeclarations::new(); + for id in tcx.hir_crate_items(()).foreign_items() { + lint.check_foreign_item(tcx, id); + } +} + +declare_lint! { + /// The `clashing_extern_declarations` lint detects when an `extern fn` + /// has been declared with the same name but different types. + /// + /// ### Example + /// + /// ```rust + /// mod m { + /// extern "C" { + /// fn foo(); + /// } + /// } + /// + /// extern "C" { + /// fn foo(_: u32); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Because two symbols of the same name cannot be resolved to two + /// different functions at link time, and one function cannot possibly + /// have two types, a clashing extern declaration is almost certainly a + /// mistake. Check to make sure that the `extern` definitions are correct + /// and equivalent, and possibly consider unifying them in one location. + /// + /// This lint does not run between crates because a project may have + /// dependencies which both rely on the same extern function, but declare + /// it in a different (but valid) way. For example, they may both declare + /// an opaque type for one or more of the arguments (which would end up + /// distinct types), or use types that are valid conversions in the + /// language the `extern fn` is defined in. In these cases, the compiler + /// can't say that the clashing declaration is incorrect. + pub CLASHING_EXTERN_DECLARATIONS, + Warn, + "detects when an extern fn has been declared with the same name but different types" +} + +struct ClashingExternDeclarations { + /// Map of function symbol name to the first-seen hir id for that symbol name.. If seen_decls + /// contains an entry for key K, it means a symbol with name K has been seen by this lint and + /// the symbol should be reported as a clashing declaration. + // FIXME: Technically, we could just store a &'tcx str here without issue; however, the + // `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime. + seen_decls: FxHashMap<Symbol, hir::OwnerId>, +} + +/// Differentiate between whether the name for an extern decl came from the link_name attribute or +/// just from declaration itself. This is important because we don't want to report clashes on +/// symbol name if they don't actually clash because one or the other links against a symbol with a +/// different name. +enum SymbolName { + /// The name of the symbol + the span of the annotation which introduced the link name. + Link(Symbol, Span), + /// No link name, so just the name of the symbol. + Normal(Symbol), +} + +impl SymbolName { + fn get_name(&self) -> Symbol { + match self { + SymbolName::Link(s, _) | SymbolName::Normal(s) => *s, + } + } +} + +impl ClashingExternDeclarations { + pub(crate) fn new() -> Self { + ClashingExternDeclarations { seen_decls: FxHashMap::default() } + } + + /// Insert a new foreign item into the seen set. If a symbol with the same name already exists + /// for the item, return its HirId without updating the set. + fn insert(&mut self, tcx: TyCtxt<'_>, fi: hir::ForeignItemId) -> Option<hir::OwnerId> { + let did = fi.owner_id.to_def_id(); + let instance = Instance::new(did, ty::List::identity_for_item(tcx, did)); + let name = Symbol::intern(tcx.symbol_name(instance).name); + if let Some(&existing_id) = self.seen_decls.get(&name) { + // Avoid updating the map with the new entry when we do find a collision. We want to + // make sure we're always pointing to the first definition as the previous declaration. + // This lets us avoid emitting "knock-on" diagnostics. + Some(existing_id) + } else { + self.seen_decls.insert(name, fi.owner_id) + } + } + + #[instrument(level = "trace", skip(self, tcx))] + fn check_foreign_item<'tcx>(&mut self, tcx: TyCtxt<'tcx>, this_fi: hir::ForeignItemId) { + let DefKind::Fn = tcx.def_kind(this_fi.owner_id) else { return }; + let Some(existing_did) = self.insert(tcx, this_fi) else { return }; + + let existing_decl_ty = tcx.type_of(existing_did).skip_binder(); + let this_decl_ty = tcx.type_of(this_fi.owner_id).instantiate_identity(); + debug!( + "ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}", + existing_did, existing_decl_ty, this_fi.owner_id, this_decl_ty + ); + + // Check that the declarations match. + if !structurally_same_type( + tcx, + tcx.param_env(this_fi.owner_id), + existing_decl_ty, + this_decl_ty, + types::CItemKind::Declaration, + ) { + let orig = name_of_extern_decl(tcx, existing_did); + + // Finally, emit the diagnostic. + let this = tcx.item_name(this_fi.owner_id.to_def_id()); + let orig = orig.get_name(); + let previous_decl_label = get_relevant_span(tcx, existing_did); + let mismatch_label = get_relevant_span(tcx, this_fi.owner_id); + let sub = + BuiltinClashingExternSub { tcx, expected: existing_decl_ty, found: this_decl_ty }; + let decorator = if orig == this { + BuiltinClashingExtern::SameName { + this, + orig, + previous_decl_label, + mismatch_label, + sub, + } + } else { + BuiltinClashingExtern::DiffName { + this, + orig, + previous_decl_label, + mismatch_label, + sub, + } + }; + tcx.emit_spanned_lint( + CLASHING_EXTERN_DECLARATIONS, + this_fi.hir_id(), + mismatch_label, + decorator, + ); + } + } +} + +/// Get the name of the symbol that's linked against for a given extern declaration. That is, +/// the name specified in a #[link_name = ...] attribute if one was specified, else, just the +/// symbol's name. +fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName { + if let Some((overridden_link_name, overridden_link_name_span)) = + tcx.codegen_fn_attrs(fi).link_name.map(|overridden_link_name| { + // FIXME: Instead of searching through the attributes again to get span + // information, we could have codegen_fn_attrs also give span information back for + // where the attribute was defined. However, until this is found to be a + // bottleneck, this does just fine. + (overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span) + }) + { + SymbolName::Link(overridden_link_name, overridden_link_name_span) + } else { + SymbolName::Normal(tcx.item_name(fi.to_def_id())) + } +} + +/// We want to ensure that we use spans for both decls that include where the +/// name was defined, whether that was from the link_name attribute or not. +fn get_relevant_span(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> Span { + match name_of_extern_decl(tcx, fi) { + SymbolName::Normal(_) => tcx.def_span(fi), + SymbolName::Link(_, annot_span) => annot_span, + } +} + +/// Checks whether two types are structurally the same enough that the declarations shouldn't +/// clash. We need this so we don't emit a lint when two modules both declare an extern struct, +/// with the same members (as the declarations shouldn't clash). +fn structurally_same_type<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + a: Ty<'tcx>, + b: Ty<'tcx>, + ckind: types::CItemKind, +) -> bool { + let mut seen_types = FxHashSet::default(); + structurally_same_type_impl(&mut seen_types, tcx, param_env, a, b, ckind) +} + +fn structurally_same_type_impl<'tcx>( + seen_types: &mut FxHashSet<(Ty<'tcx>, Ty<'tcx>)>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + a: Ty<'tcx>, + b: Ty<'tcx>, + ckind: types::CItemKind, +) -> bool { + debug!("structurally_same_type_impl(tcx, a = {:?}, b = {:?})", a, b); + + // Given a transparent newtype, reach through and grab the inner + // type unless the newtype makes the type non-null. + let non_transparent_ty = |mut ty: Ty<'tcx>| -> Ty<'tcx> { + loop { + if let ty::Adt(def, args) = *ty.kind() { + let is_transparent = def.repr().transparent(); + let is_non_null = types::nonnull_optimization_guaranteed(tcx, def); + debug!( + "non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}", + ty, is_transparent, is_non_null + ); + if is_transparent && !is_non_null { + debug_assert_eq!(def.variants().len(), 1); + let v = &def.variant(FIRST_VARIANT); + // continue with `ty`'s non-ZST field, + // otherwise `ty` is a ZST and we can return + if let Some(field) = types::transparent_newtype_field(tcx, v) { + ty = field.ty(tcx, args); + continue; + } + } + } + debug!("non_transparent_ty -> {:?}", ty); + return ty; + } + }; + + let a = non_transparent_ty(a); + let b = non_transparent_ty(b); + + if !seen_types.insert((a, b)) { + // We've encountered a cycle. There's no point going any further -- the types are + // structurally the same. + true + } else if a == b { + // All nominally-same types are structurally same, too. + true + } else { + // Do a full, depth-first comparison between the two. + use rustc_type_ir::sty::TyKind::*; + let a_kind = a.kind(); + let b_kind = b.kind(); + + let compare_layouts = |a, b| -> Result<bool, &'tcx LayoutError<'tcx>> { + debug!("compare_layouts({:?}, {:?})", a, b); + let a_layout = &tcx.layout_of(param_env.and(a))?.layout.abi(); + let b_layout = &tcx.layout_of(param_env.and(b))?.layout.abi(); + debug!( + "comparing layouts: {:?} == {:?} = {}", + a_layout, + b_layout, + a_layout == b_layout + ); + Ok(a_layout == b_layout) + }; + + #[allow(rustc::usage_of_ty_tykind)] + let is_primitive_or_pointer = + |kind: &ty::TyKind<'_>| kind.is_primitive() || matches!(kind, RawPtr(..) | Ref(..)); + + ensure_sufficient_stack(|| { + match (a_kind, b_kind) { + (Adt(a_def, _), Adt(b_def, _)) => { + // We can immediately rule out these types as structurally same if + // their layouts differ. + match compare_layouts(a, b) { + Ok(false) => return false, + _ => (), // otherwise, continue onto the full, fields comparison + } + + // Grab a flattened representation of all fields. + let a_fields = a_def.variants().iter().flat_map(|v| v.fields.iter()); + let b_fields = b_def.variants().iter().flat_map(|v| v.fields.iter()); + + // Perform a structural comparison for each field. + a_fields.eq_by( + b_fields, + |&ty::FieldDef { did: a_did, .. }, &ty::FieldDef { did: b_did, .. }| { + structurally_same_type_impl( + seen_types, + tcx, + param_env, + tcx.type_of(a_did).instantiate_identity(), + tcx.type_of(b_did).instantiate_identity(), + ckind, + ) + }, + ) + } + (Array(a_ty, a_const), Array(b_ty, b_const)) => { + // For arrays, we also check the constness of the type. + a_const.kind() == b_const.kind() + && structurally_same_type_impl( + seen_types, tcx, param_env, *a_ty, *b_ty, ckind, + ) + } + (Slice(a_ty), Slice(b_ty)) => { + structurally_same_type_impl(seen_types, tcx, param_env, *a_ty, *b_ty, ckind) + } + (RawPtr(a_tymut), RawPtr(b_tymut)) => { + a_tymut.mutbl == b_tymut.mutbl + && structurally_same_type_impl( + seen_types, tcx, param_env, a_tymut.ty, b_tymut.ty, ckind, + ) + } + (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { + // For structural sameness, we don't need the region to be same. + a_mut == b_mut + && structurally_same_type_impl( + seen_types, tcx, param_env, *a_ty, *b_ty, ckind, + ) + } + (FnDef(..), FnDef(..)) => { + let a_poly_sig = a.fn_sig(tcx); + let b_poly_sig = b.fn_sig(tcx); + + // We don't compare regions, but leaving bound regions around ICEs, so + // we erase them. + let a_sig = tcx.erase_late_bound_regions(a_poly_sig); + let b_sig = tcx.erase_late_bound_regions(b_poly_sig); + + (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) + == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) + && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { + structurally_same_type_impl(seen_types, tcx, param_env, *a, *b, ckind) + }) + && structurally_same_type_impl( + seen_types, + tcx, + param_env, + a_sig.output(), + b_sig.output(), + ckind, + ) + } + (Tuple(a_args), Tuple(b_args)) => { + a_args.iter().eq_by(b_args.iter(), |a_ty, b_ty| { + structurally_same_type_impl(seen_types, tcx, param_env, a_ty, b_ty, ckind) + }) + } + // For these, it's not quite as easy to define structural-sameness quite so easily. + // For the purposes of this lint, take the conservative approach and mark them as + // not structurally same. + (Dynamic(..), Dynamic(..)) + | (Error(..), Error(..)) + | (Closure(..), Closure(..)) + | (Generator(..), Generator(..)) + | (GeneratorWitness(..), GeneratorWitness(..)) + | (Alias(ty::Projection, ..), Alias(ty::Projection, ..)) + | (Alias(ty::Inherent, ..), Alias(ty::Inherent, ..)) + | (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false, + + // These definitely should have been caught above. + (Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(), + + // An Adt and a primitive or pointer type. This can be FFI-safe if non-null + // enum layout optimisation is being applied. + (Adt(..), other_kind) | (other_kind, Adt(..)) + if is_primitive_or_pointer(other_kind) => + { + let (primitive, adt) = + if is_primitive_or_pointer(a.kind()) { (a, b) } else { (b, a) }; + if let Some(ty) = types::repr_nullable_ptr(tcx, param_env, adt, ckind) { + ty == primitive + } else { + compare_layouts(a, b).unwrap_or(false) + } + } + // Otherwise, just compare the layouts. This may fail to lint for some + // incompatible types, but at the very least, will stop reads into + // uninitialised memory. + _ => compare_layouts(a, b).unwrap_or(false), + } + }) + } +} diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index fb12ded71d6..3331dbad4a9 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -17,7 +17,7 @@ use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore}; use rustc_ast as ast; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::{join, DynSend}; +use rustc_data_structures::sync::join; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit as hir_visit; @@ -336,7 +336,7 @@ macro_rules! impl_late_lint_pass { crate::late_lint_methods!(impl_late_lint_pass, []); -pub(super) fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( +pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( tcx: TyCtxt<'tcx>, module_def_id: LocalDefId, builtin_lints: T, @@ -376,6 +376,12 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( let mut cx = LateContextAndPass { context, pass }; let (module, _span, hir_id) = tcx.hir().get_module(module_def_id); + + // There is no module lint that will have the crate itself as an item, so check it here. + if hir_id == hir::CRATE_HIR_ID { + lint_callback!(cx, check_crate,); + } + cx.process_mod(module, hir_id); // Visit the crate attributes @@ -383,10 +389,19 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( for attr in tcx.hir().attrs(hir::CRATE_HIR_ID).iter() { cx.visit_attribute(attr) } + lint_callback!(cx, check_crate_post,); } } -fn late_lint_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(tcx: TyCtxt<'tcx>, builtin_lints: T) { +fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { + // Note: `passes` is often empty. + let mut passes: Vec<_> = + unerased_lint_store(tcx).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); + + if passes.is_empty() { + return; + } + let context = LateContext { tcx, enclosing_body: None, @@ -399,18 +414,8 @@ fn late_lint_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(tcx: TyCtxt<'tcx>, builti only_module: false, }; - // Note: `passes` is often empty. In that case, it's faster to run - // `builtin_lints` directly rather than bundling it up into the - // `RuntimeCombinedLateLintPass`. - let mut passes: Vec<_> = - unerased_lint_store(tcx).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); - if passes.is_empty() { - late_lint_crate_inner(tcx, context, builtin_lints); - } else { - passes.push(Box::new(builtin_lints)); - let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] }; - late_lint_crate_inner(tcx, context, pass); - } + let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] }; + late_lint_crate_inner(tcx, context, pass); } fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>( @@ -432,15 +437,12 @@ fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>( } /// Performs lint checking on a crate. -pub fn check_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>( - tcx: TyCtxt<'tcx>, - builtin_lints: impl FnOnce() -> T + Send + DynSend, -) { +pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>) { join( || { tcx.sess.time("crate_lints", || { // Run whole crate non-incremental lints - late_lint_crate(tcx, builtin_lints()); + late_lint_crate(tcx); }); }, || { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index fb407be1f02..18b178d8882 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,4 +1,5 @@ use crate::{ + builtin::MISSING_DOCS, context::{CheckLintNameResult, LintStore}, fluent_generated as fluent, late::unerased_lint_store, @@ -667,6 +668,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { continue; } + // `#[doc(hidden)]` disables missing_docs check. + if attr.has_name(sym::doc) + && attr + .meta_item_list() + .map_or(false, |l| ast::attr::list_contains_name(&l, sym::hidden)) + { + self.insert(LintId::of(MISSING_DOCS), (Level::Allow, LintLevelSource::Default)); + continue; + } + let level = match Level::from_attr(attr) { None => continue, // This is the only lint level with a `LintExpectationId` that can be created from an attribute diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 53089294fe2..42378951af3 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -40,6 +40,7 @@ #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate rustc_middle; @@ -57,8 +58,8 @@ mod early; mod enum_intrinsics_non_enums; mod errors; mod expect; -mod fn_null_check; mod for_loops_over_fallibles; +mod foreign_modules; pub mod hidden_unicode_codepoints; mod internal; mod invalid_from_utf8; @@ -76,6 +77,7 @@ mod noop_method_call; mod opaque_hidden_inferred_bound; mod pass_by_value; mod passes; +mod ptr_nulls; mod redundant_semicolon; mod reference_casting; mod traits; @@ -102,7 +104,6 @@ use builtin::*; use deref_into_dyn_supertrait::*; use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; -use fn_null_check::*; use for_loops_over_fallibles::*; use hidden_unicode_codepoints::*; use internal::*; @@ -117,6 +118,7 @@ use nonstandard_style::*; use noop_method_call::*; use opaque_hidden_inferred_bound::*; use pass_by_value::*; +use ptr_nulls::*; use redundant_semicolon::*; use reference_casting::*; use traits::*; @@ -124,11 +126,11 @@ use types::*; use unused::*; /// Useful for other parts of the compiler / Clippy. -pub use builtin::SoftLints; +pub use builtin::{MissingDoc, SoftLints}; pub use context::{CheckLintNameResult, FindLintError, LintStore}; pub use context::{EarlyContext, LateContext, LintContext}; pub use early::{check_ast_node, EarlyCheckNode}; -pub use late::{check_crate, unerased_lint_store}; +pub use late::{check_crate, late_lint_mod, unerased_lint_store}; pub use passes::{EarlyLintPass, LateLintPass}; pub use rustc_session::lint::Level::{self, *}; pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId}; @@ -139,11 +141,12 @@ fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { levels::provide(providers); expect::provide(providers); + foreign_modules::provide(providers); *providers = Providers { lint_mod, ..*providers }; } fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new()); + late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new()); } early_lint_methods!( @@ -173,7 +176,7 @@ early_lint_methods!( WhileTrue: WhileTrue, NonAsciiIdents: NonAsciiIdents, HiddenUnicodeCodepoints: HiddenUnicodeCodepoints, - IncompleteFeatures: IncompleteFeatures, + IncompleteInternalFeatures: IncompleteInternalFeatures, RedundantSemicolons: RedundantSemicolons, UnusedDocComment: UnusedDocComment, UnexpectedCfgs: UnexpectedCfgs, @@ -181,27 +184,6 @@ early_lint_methods!( ] ); -// FIXME: Make a separate lint type which does not require typeck tables. - -late_lint_methods!( - declare_combined_late_lint_pass, - [ - pub BuiltinCombinedLateLintPass, - [ - // Tracks state across modules - UnnameableTestItems: UnnameableTestItems::new(), - // Tracks attributes of parents - MissingDoc: MissingDoc::new(), - // Builds a global list of all impls of `Debug`. - // FIXME: Turn the computation of types which implement Debug into a query - // and change this to a module lint pass - MissingDebugImplementations: MissingDebugImplementations::default(), - // Keeps a global list of foreign declarations. - ClashingExternDeclarations: ClashingExternDeclarations::new(), - ] - ] -); - late_lint_methods!( declare_combined_late_lint_pass, [ @@ -218,7 +200,7 @@ late_lint_methods!( BoxPointers: BoxPointers, PathStatements: PathStatements, LetUnderscore: LetUnderscore, - InvalidReferenceCasting: InvalidReferenceCasting, + InvalidReferenceCasting: InvalidReferenceCasting::default(), // Depends on referenced function signatures in expressions UnusedResults: UnusedResults, NonUpperCaseGlobals: NonUpperCaseGlobals, @@ -227,7 +209,7 @@ late_lint_methods!( // Depends on types used in type definitions MissingCopyImplementations: MissingCopyImplementations, // Depends on referenced function signatures in expressions - IncorrectFnNullChecks: IncorrectFnNullChecks, + PtrNullChecks: PtrNullChecks, MutableTransmutes: MutableTransmutes, TypeAliasBounds: TypeAliasBounds, TrivialConstraints: TrivialConstraints, @@ -254,6 +236,8 @@ late_lint_methods!( OpaqueHiddenInferredBound: OpaqueHiddenInferredBound, MultipleSupertraitUpcastable: MultipleSupertraitUpcastable, MapUnitFn: MapUnitFn, + MissingDebugImplementations: MissingDebugImplementations, + MissingDoc: MissingDoc, ] ] ); @@ -282,7 +266,7 @@ fn register_builtins(store: &mut LintStore) { store.register_lints(&BuiltinCombinedPreExpansionLintPass::get_lints()); store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints()); store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints()); - store.register_lints(&BuiltinCombinedLateLintPass::get_lints()); + store.register_lints(&foreign_modules::get_lints()); add_lint_group!( "nonstandard_style", @@ -522,20 +506,20 @@ fn register_internals(store: &mut LintStore) { store.register_lints(&LintPassImpl::get_lints()); store.register_early_pass(|| Box::new(LintPassImpl)); store.register_lints(&DefaultHashTypes::get_lints()); - store.register_late_pass(|_| Box::new(DefaultHashTypes)); + store.register_late_mod_pass(|_| Box::new(DefaultHashTypes)); store.register_lints(&QueryStability::get_lints()); - store.register_late_pass(|_| Box::new(QueryStability)); + store.register_late_mod_pass(|_| Box::new(QueryStability)); store.register_lints(&ExistingDocKeyword::get_lints()); - store.register_late_pass(|_| Box::new(ExistingDocKeyword)); + store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword)); store.register_lints(&TyTyKind::get_lints()); - store.register_late_pass(|_| Box::new(TyTyKind)); + store.register_late_mod_pass(|_| Box::new(TyTyKind)); store.register_lints(&Diagnostics::get_lints()); store.register_early_pass(|| Box::new(Diagnostics)); - store.register_late_pass(|_| Box::new(Diagnostics)); + store.register_late_mod_pass(|_| Box::new(Diagnostics)); store.register_lints(&BadOptAccess::get_lints()); - store.register_late_pass(|_| Box::new(BadOptAccess)); + store.register_late_mod_pass(|_| Box::new(BadOptAccess)); store.register_lints(&PassByValue::get_lints()); - store.register_late_pass(|_| Box::new(PassByValue)); + store.register_late_mod_pass(|_| Box::new(PassByValue)); // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and // these lints will trigger all of the time - change this once migration to diagnostic structs diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 968172693a9..25982a45853 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -250,7 +250,7 @@ impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { rustc_session::parse::add_feature_diagnostics( diag, &self.parse_sess, - sym::closure_track_caller, + sym::async_fn_track_caller, ); diag } @@ -371,10 +371,6 @@ pub enum BuiltinEllipsisInclusiveRangePatternsLint { } #[derive(LintDiagnostic)] -#[diag(lint_builtin_unnameable_test_items)] -pub struct BuiltinUnnameableTestItems; - -#[derive(LintDiagnostic)] #[diag(lint_builtin_keyword_idents)] pub struct BuiltinKeywordIdents { pub kw: Ident, @@ -405,18 +401,27 @@ pub struct BuiltinExplicitOutlivesSuggestion { pub struct BuiltinIncompleteFeatures { pub name: Symbol, #[subdiagnostic] - pub note: Option<BuiltinIncompleteFeaturesNote>, + pub note: Option<BuiltinFeatureIssueNote>, #[subdiagnostic] pub help: Option<BuiltinIncompleteFeaturesHelp>, } +#[derive(LintDiagnostic)] +#[diag(lint_builtin_internal_features)] +#[note] +pub struct BuiltinInternalFeatures { + pub name: Symbol, + #[subdiagnostic] + pub note: Option<BuiltinFeatureIssueNote>, +} + #[derive(Subdiagnostic)] #[help(lint_help)] pub struct BuiltinIncompleteFeaturesHelp; #[derive(Subdiagnostic)] #[note(lint_note)] -pub struct BuiltinIncompleteFeaturesNote { +pub struct BuiltinFeatureIssueNote { pub n: NonZeroU32, } @@ -613,11 +618,23 @@ pub struct ExpectationNote { pub rationale: Symbol, } -// fn_null_check.rs +// ptr_nulls.rs #[derive(LintDiagnostic)] -#[diag(lint_fn_null_check)] -#[help] -pub struct FnNullCheckDiag; +pub enum PtrNullChecksDiag<'a> { + #[diag(lint_ptr_null_checks_fn_ptr)] + #[help(lint_help)] + FnPtr { + orig_ty: Ty<'a>, + #[label] + label: Span, + }, + #[diag(lint_ptr_null_checks_ref)] + Ref { + orig_ty: Ty<'a>, + #[label] + label: Span, + }, +} // for_loops_over_fallibles.rs #[derive(LintDiagnostic)] @@ -745,8 +762,18 @@ pub enum InvalidFromUtf8Diag { // reference_casting.rs #[derive(LintDiagnostic)] -#[diag(lint_invalid_reference_casting)] -pub struct InvalidReferenceCastingDiag; +pub enum InvalidReferenceCastingDiag { + #[diag(lint_invalid_reference_casting_borrow_as_mut)] + BorrowAsMut { + #[label] + orig_cast: Option<Span>, + }, + #[diag(lint_invalid_reference_casting_assign_to_ref)] + AssignToRef { + #[label] + orig_cast: Option<Span>, + }, +} // hidden_unicode_codepoints.rs #[derive(LintDiagnostic)] @@ -1056,8 +1083,10 @@ pub struct IdentifierUncommonCodepoints; pub struct ConfusableIdentifierPair { pub existing_sym: Symbol, pub sym: Symbol, - #[label] + #[label(lint_other_use)] pub label: Span, + #[label(lint_current_use)] + pub main_label: Span, } #[derive(LintDiagnostic)] @@ -1467,7 +1496,7 @@ pub enum InvalidNanComparisons { #[diag(lint_invalid_nan_comparisons_eq_ne)] EqNe { #[subdiagnostic] - suggestion: InvalidNanComparisonsSuggestion, + suggestion: Option<InvalidNanComparisonsSuggestion>, }, #[diag(lint_invalid_nan_comparisons_lt_le_gt_ge)] LtLeGtGe, diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 4af879b4e91..62bb8c2c67d 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -222,6 +222,7 @@ impl EarlyLintPass for NonAsciiIdents { existing_sym: *existing_symbol, sym: symbol, label: *existing_span, + main_label: sp, }, ); } diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs new file mode 100644 index 00000000000..02aff91032f --- /dev/null +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -0,0 +1,146 @@ +use crate::{lints::PtrNullChecksDiag, LateContext, LateLintPass, LintContext}; +use rustc_ast::LitKind; +use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::sym; + +declare_lint! { + /// The `useless_ptr_null_checks` lint checks for useless null checks against pointers + /// obtained from non-null types. + /// + /// ### Example + /// + /// ```rust + /// # fn test() {} + /// let fn_ptr: fn() = /* somehow obtained nullable function pointer */ + /// # test; + /// + /// if (fn_ptr as *const ()).is_null() { /* ... */ } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Function pointers and references are assumed to be non-null, checking them for null + /// will always return false. + USELESS_PTR_NULL_CHECKS, + Warn, + "useless checking of non-null-typed pointer" +} + +declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]); + +/// This function detects and returns the original expression from a series of consecutive casts, +/// ie. `(my_fn as *const _ as *mut _).cast_mut()` would return the expression for `my_fn`. +fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'a Expr<'a>> { + let mut had_at_least_one_cast = false; + loop { + e = e.peel_blocks(); + e = if let ExprKind::Cast(expr, t) = e.kind + && let TyKind::Ptr(_) = t.kind { + had_at_least_one_cast = true; + expr + } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut)) { + had_at_least_one_cast = true; + expr + } else if let ExprKind::Call(path, [arg]) = e.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_from_ref | sym::ptr_from_mut)) { + had_at_least_one_cast = true; + arg + } else if had_at_least_one_cast { + return Some(e); + } else { + return None; + }; + } +} + +fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option<PtrNullChecksDiag<'a>> { + let expr = ptr_cast_chain(cx, expr)?; + + let orig_ty = cx.typeck_results().expr_ty(expr); + if orig_ty.is_fn() { + Some(PtrNullChecksDiag::FnPtr { orig_ty, label: expr.span }) + } else if orig_ty.is_ref() { + Some(PtrNullChecksDiag::Ref { orig_ty, label: expr.span }) + } else { + None + } +} + +impl<'tcx> LateLintPass<'tcx> for PtrNullChecks { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + match expr.kind { + // Catching: + // <*<const/mut> <ty>>::is_null(fn_ptr as *<const/mut> <ty>) + ExprKind::Call(path, [arg]) + if let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_const_is_null | sym::ptr_is_null) + ) + && let Some(diag) = incorrect_check(cx, arg) => + { + cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) + } + + // Catching: + // (fn_ptr as *<const/mut> <ty>).is_null() + ExprKind::MethodCall(_, receiver, _, _) + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_const_is_null | sym::ptr_is_null) + ) + && let Some(diag) = incorrect_check(cx, receiver) => + { + cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) + } + + ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Eq) => { + let to_check: &Expr<'_>; + let diag: PtrNullChecksDiag<'_>; + if let Some(ddiag) = incorrect_check(cx, left) { + to_check = right; + diag = ddiag; + } else if let Some(ddiag) = incorrect_check(cx, right) { + to_check = left; + diag = ddiag; + } else { + return; + } + + match to_check.kind { + // Catching: + // (fn_ptr as *<const/mut> <ty>) == (0 as <ty>) + ExprKind::Cast(cast_expr, _) + if let ExprKind::Lit(spanned) = cast_expr.kind + && let LitKind::Int(v, _) = spanned.node && v == 0 => + { + cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) + }, + + // Catching: + // (fn_ptr as *<const/mut> <ty>) == std::ptr::null() + ExprKind::Call(path, []) + if let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) + && (diag_item == sym::ptr_null || diag_item == sym::ptr_null_mut) => + { + cx.emit_spanned_lint(USELESS_PTR_NULL_CHECKS, expr.span, diag) + }, + + _ => {}, + } + } + _ => {} + } + } +} diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs index d343aaf35d5..ed3d4721049 100644 --- a/compiler/rustc_lint/src/reference_casting.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -1,7 +1,8 @@ use rustc_ast::Mutability; -use rustc_hir::{Expr, ExprKind, MutTy, TyKind, UnOp}; -use rustc_middle::ty; -use rustc_span::sym; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, QPath, StmtKind, UnOp}; +use rustc_middle::ty::{self, TypeAndMut}; +use rustc_span::{sym, Span}; use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext}; @@ -12,7 +13,6 @@ declare_lint! { /// ### Example /// /// ```rust,compile_fail - /// # #![deny(invalid_reference_casting)] /// fn x(r: &i32) { /// unsafe { /// *(r as *const i32 as *mut i32) += 1; @@ -30,46 +30,103 @@ declare_lint! { /// `UnsafeCell` is the only way to obtain aliasable data that is considered /// mutable. INVALID_REFERENCE_CASTING, - Allow, + Deny, "casts of `&T` to `&mut T` without interior mutability" } -declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]); +#[derive(Default)] +pub struct InvalidReferenceCasting { + casted: FxHashMap<HirId, Span>, +} + +impl_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]); impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let ExprKind::Unary(UnOp::Deref, e) = &expr.kind else { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx rustc_hir::Stmt<'tcx>) { + let StmtKind::Local(local) = stmt.kind else { + return; + }; + let Local { init: Some(init), els: None, .. } = local else { return; }; - let e = e.peel_blocks(); - let e = if let ExprKind::Cast(e, t) = e.kind - && let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind { - e - } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind - && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) { + if is_cast_from_const_to_mut(cx, init) { + self.casted.insert(local.pat.hir_id, init.span); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + // &mut <expr> + let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind { + expr + // <expr> = ... + } else if let ExprKind::Assign(expr, _, _) = expr.kind { + expr + // <expr> += ... + } else if let ExprKind::AssignOp(_, expr, _) = expr.kind { expr } else { return; }; - let e = e.peel_blocks(); - let e = if let ExprKind::Cast(e, t) = e.kind - && let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind { - e - } else if let ExprKind::Call(path, [arg]) = e.kind - && let ExprKind::Path(ref qpath) = path.kind - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) { - arg + let ExprKind::Unary(UnOp::Deref, e) = &inner.kind else { + return; + }; + + let orig_cast = if is_cast_from_const_to_mut(cx, e) { + None + } else if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind + && let Res::Local(hir_id) = &path.res + && let Some(orig_cast) = self.casted.get(hir_id) { + Some(*orig_cast) } else { return; }; - let e = e.peel_blocks(); - if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind() { - cx.emit_spanned_lint(INVALID_REFERENCE_CASTING, expr.span, InvalidReferenceCastingDiag); - } + cx.emit_spanned_lint( + INVALID_REFERENCE_CASTING, + expr.span, + if matches!(expr.kind, ExprKind::AddrOf(..)) { + InvalidReferenceCastingDiag::BorrowAsMut { orig_cast } + } else { + InvalidReferenceCastingDiag::AssignToRef { orig_cast } + }, + ); } } + +fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { + let e = e.peel_blocks(); + + // <expr> as *mut ... + let e = if let ExprKind::Cast(e, t) = e.kind + && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(t.hir_id).kind() { + e + // <expr>.cast_mut() + } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) { + expr + } else { + return false; + }; + + let e = e.peel_blocks(); + + // <expr> as *const ... + let e = if let ExprKind::Cast(e, t) = e.kind + && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = cx.typeck_results().node_type(t.hir_id).kind() { + e + // ptr::from_ref(<expr>) + } else if let ExprKind::Call(path, [arg]) = e.kind + && let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) { + arg + } else { + return false; + }; + + let e = e.peel_blocks(); + matches!(cx.typeck_results().node_type(e.hir_id).kind(), ty::Ref(_, _, Mutability::Not)) +} diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 226d01b79a8..1ba746eddeb 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -572,32 +572,36 @@ fn lint_nan<'tcx>( } fn eq_ne( + cx: &LateContext<'_>, e: &hir::Expr<'_>, l: &hir::Expr<'_>, r: &hir::Expr<'_>, f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion, ) -> InvalidNanComparisons { - let suggestion = + // FIXME(#72505): This suggestion can be restored if `f{32,64}::is_nan` is made const. + let suggestion = (!cx.tcx.hir().is_inside_const_context(e.hir_id)).then(|| { if let Some(l_span) = l.span.find_ancestor_inside(e.span) && - let Some(r_span) = r.span.find_ancestor_inside(e.span) { + let Some(r_span) = r.span.find_ancestor_inside(e.span) + { f(l_span, r_span) } else { InvalidNanComparisonsSuggestion::Spanless - }; + } + }); InvalidNanComparisons::EqNe { suggestion } } let lint = match binop.node { hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => { - eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { + eq_ne(cx, e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { nan_plus_binop: l_span.until(r_span), float: r_span.shrink_to_hi(), neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()), }) } hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => { - eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { + eq_ne(cx, e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { nan_plus_binop: l_span.shrink_to_hi().to(r_span), float: l_span.shrink_to_hi(), neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()), @@ -815,8 +819,7 @@ pub fn transparent_newtype_field<'a, 'tcx>( } /// Is type known to be non-null? -fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool { - let tcx = cx.tcx; +fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool { match ty.kind() { ty::FnPtr(_) => true, ty::Ref(..) => true, @@ -835,8 +838,8 @@ fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKi def.variants() .iter() - .filter_map(|variant| transparent_newtype_field(cx.tcx, variant)) - .any(|field| ty_is_known_nonnull(cx, field.ty(tcx, args), mode)) + .filter_map(|variant| transparent_newtype_field(tcx, variant)) + .any(|field| ty_is_known_nonnull(tcx, field.ty(tcx, args), mode)) } _ => false, } @@ -844,15 +847,12 @@ fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKi /// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type. /// If the type passed in was not scalar, returns None. -fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { - let tcx = cx.tcx; +fn get_nullable_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { Some(match *ty.kind() { ty::Adt(field_def, field_args) => { let inner_field_ty = { - let mut first_non_zst_ty = field_def - .variants() - .iter() - .filter_map(|v| transparent_newtype_field(cx.tcx, v)); + let mut first_non_zst_ty = + field_def.variants().iter().filter_map(|v| transparent_newtype_field(tcx, v)); debug_assert_eq!( first_non_zst_ty.clone().count(), 1, @@ -863,7 +863,7 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t .expect("No non-zst fields in transparent type.") .ty(tcx, field_args) }; - return get_nullable_type(cx, inner_field_ty); + return get_nullable_type(tcx, inner_field_ty); } ty::Int(ty) => Ty::new_int(tcx, ty), ty::Uint(ty) => Ty::new_uint(tcx, ty), @@ -895,43 +895,44 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. /// FIXME: This duplicates code in codegen. pub(crate) fn repr_nullable_ptr<'tcx>( - cx: &LateContext<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, ckind: CItemKind, ) -> Option<Ty<'tcx>> { - debug!("is_repr_nullable_ptr(cx, ty = {:?})", ty); + debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty); if let ty::Adt(ty_def, args) = ty.kind() { let field_ty = match &ty_def.variants().raw[..] { [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) { - ([], [field]) | ([field], []) => field.ty(cx.tcx, args), + ([], [field]) | ([field], []) => field.ty(tcx, args), _ => return None, }, _ => return None, }; - if !ty_is_known_nonnull(cx, field_ty, ckind) { + if !ty_is_known_nonnull(tcx, field_ty, ckind) { return None; } // At this point, the field's type is known to be nonnull and the parent enum is Option-like. // If the computed size for the field and the enum are different, the nonnull optimization isn't // being applied (and we've got a problem somewhere). - let compute_size_skeleton = |t| SizeSkeleton::compute(t, cx.tcx, cx.param_env).unwrap(); + let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, param_env).unwrap(); if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) { bug!("improper_ctypes: Option nonnull optimization not applied?"); } // Return the nullable type this Option-like enum can be safely represented with. - let field_ty_abi = &cx.layout_of(field_ty).unwrap().abi; + let field_ty_abi = &tcx.layout_of(param_env.and(field_ty)).unwrap().abi; if let Abi::Scalar(field_ty_scalar) = field_ty_abi { - match field_ty_scalar.valid_range(cx) { + match field_ty_scalar.valid_range(&tcx) { WrappingRange { start: 0, end } - if end == field_ty_scalar.size(&cx.tcx).unsigned_int_max() - 1 => + if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 => { - return Some(get_nullable_type(cx, field_ty).unwrap()); + return Some(get_nullable_type(tcx, field_ty).unwrap()); } WrappingRange { start: 1, .. } => { - return Some(get_nullable_type(cx, field_ty).unwrap()); + return Some(get_nullable_type(tcx, field_ty).unwrap()); } WrappingRange { start, end } => { unreachable!("Unhandled start and end range: ({}, {})", start, end) @@ -1116,7 +1117,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() { // Special-case types like `Option<extern fn()>`. - if repr_nullable_ptr(self.cx, ty, self.mode).is_none() { + if repr_nullable_ptr(self.cx.tcx, self.cx.param_env, ty, self.mode) + .is_none() + { return FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_enum_repr_reason, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index a3c5ca4cda1..3db6b302790 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -653,7 +653,7 @@ trait UnusedDelimLint { ExprKind::Call(fn_, _params) => fn_, ExprKind::Cast(expr, _ty) => expr, ExprKind::Type(expr, _ty) => expr, - ExprKind::Index(base, _subscript) => base, + ExprKind::Index(base, _subscript, _) => base, _ => break, }; if !classify::expr_requires_semi_to_be_stmt(innermost) { @@ -830,7 +830,7 @@ trait UnusedDelimLint { (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true) } - Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false), + Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false), Assign(_, ref value, _) | AssignOp(.., ref value) => { (value, UnusedDelimsCtx::AssignedValue, false, None, None, false) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 7e3b6e9e218..3747e562cec 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2847,6 +2847,45 @@ declare_lint! { } declare_lint! { + /// The `unnameable_test_items` lint detects [`#[test]`][test] functions + /// that are not able to be run by the test harness because they are in a + /// position where they are not nameable. + /// + /// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute + /// + /// ### Example + /// + /// ```rust,test + /// fn main() { + /// #[test] + /// fn foo() { + /// // This test will not fail because it does not run. + /// assert_eq!(1, 2); + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In order for the test harness to run a test, the test function must be + /// located in a position where it can be accessed from the crate root. + /// This generally means it must be defined in a module, and not anywhere + /// else such as inside another function. The compiler previously allowed + /// this without an error, so a lint was added as an alert that a test is + /// not being used. Whether or not this should be allowed has not yet been + /// decided, see [RFC 2471] and [issue #36629]. + /// + /// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443 + /// [issue #36629]: https://github.com/rust-lang/rust/issues/36629 + pub UNNAMEABLE_TEST_ITEMS, + Warn, + "detects an item that cannot be named being marked as `#[test_case]`", + report_in_external_macro +} + +declare_lint! { /// The `useless_deprecated` lint detects deprecation attributes with no effect. /// /// ### Example @@ -3403,6 +3442,7 @@ declare_lint_pass! { UNKNOWN_CRATE_TYPES, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, UNKNOWN_LINTS, + UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, @@ -3925,7 +3965,6 @@ declare_lint! { /// /// // in crate B /// #![feature(non_exhaustive_omitted_patterns_lint)] - /// /// match Bar::A { /// Bar::A => {}, /// #[warn(non_exhaustive_omitted_patterns)] diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 904f8eb5731..f4593d0fe73 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -7,6 +7,7 @@ #![allow(rustc::default_hash_types)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #![recursion_limit = "128"] use synstructure::decl_derive; diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 13b3dac85d1..cc58d51befd 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -25,6 +25,9 @@ metadata_conflicting_alloc_error_handler = metadata_conflicting_global_alloc = the `#[global_allocator]` in {$other_crate_name} conflicts with global allocator in: {$crate_name} +metadata_consider_adding_std = + consider adding the standard library to the sysroot with `x build library --target {$locator_triple}` + metadata_consider_building_std = consider building the standard library from source with `cargo build -Zbuild-std` diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index fca06c0f47c..91220629fb6 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -623,6 +623,7 @@ pub struct CannotFindCrate { pub is_nightly_build: bool, pub profiler_runtime: Symbol, pub locator_triple: TargetTriple, + pub is_ui_testing: bool, } impl IntoDiagnostic<'_> for CannotFindCrate { @@ -646,12 +647,19 @@ impl IntoDiagnostic<'_> for CannotFindCrate { } else { diag.note(fluent::metadata_target_no_std_support); } - // NOTE: this suggests using rustup, even though the user may not have it installed. - // That's because they could choose to install it; or this may give them a hint which - // target they need to install from their distro. + if self.missing_core { - diag.help(fluent::metadata_consider_downloading_target); + if env!("CFG_RELEASE_CHANNEL") == "dev" && !self.is_ui_testing { + // Note: Emits the nicer suggestion only for the dev channel. + diag.help(fluent::metadata_consider_adding_std); + } else { + // NOTE: this suggests using rustup, even though the user may not have it installed. + // That's because they could choose to install it; or this may give them a hint which + // target they need to install from their distro. + diag.help(fluent::metadata_consider_downloading_target); + } } + // Suggest using #![no_std]. #[no_core] is unstable and not really supported anyway. // NOTE: this is a dummy span if `extern crate std` was injected by the compiler. // If it's not a dummy, that means someone added `extern crate std` explicitly and diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index fb9dd660d2f..a0c552f5fd6 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -1130,6 +1130,7 @@ impl CrateError { is_nightly_build: sess.is_nightly_build(), profiler_runtime: Symbol::intern(&sess.opts.unstable_opts.profiler_runtime), locator_triple: locator.triple, + is_ui_testing: sess.opts.unstable_opts.ui_testing, }); } } @@ -1146,6 +1147,7 @@ impl CrateError { is_nightly_build: sess.is_nightly_build(), profiler_runtime: Symbol::intern(&sess.opts.unstable_opts.profiler_runtime), locator_triple: sess.opts.target_triple.clone(), + is_ui_testing: sess.opts.unstable_opts.ui_testing, }); } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index d72053ca985..f12094a271f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -30,6 +30,7 @@ use rustc_middle::query::Providers; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AssocItemContainer, SymbolName, Ty, TyCtxt}; use rustc_middle::util::common::to_readable_str; use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; @@ -1034,7 +1035,7 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) { } } -fn should_encode_variances(def_kind: DefKind) -> bool { +fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: DefKind) -> bool { match def_kind { DefKind::Struct | DefKind::Union @@ -1053,7 +1054,6 @@ fn should_encode_variances(def_kind: DefKind) -> bool { | DefKind::Static(..) | DefKind::Const | DefKind::ForeignMod - | DefKind::TyAlias | DefKind::Impl { .. } | DefKind::Trait | DefKind::TraitAlias @@ -1067,6 +1067,10 @@ fn should_encode_variances(def_kind: DefKind) -> bool { | DefKind::Closure | DefKind::Generator | DefKind::ExternCrate => false, + DefKind::TyAlias => { + tcx.features().lazy_type_alias + || tcx.type_of(def_id).instantiate_identity().has_opaque_types() + } } } @@ -1349,7 +1353,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.encode_default_body_stability(def_id); self.encode_deprecation(def_id); } - if should_encode_variances(def_kind) { + if should_encode_variances(tcx, def_id, def_kind) { let v = self.tcx.variances_of(def_id); record_array!(self.tables.variances_of[def_id] <- v); } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 0256e09e4b5..2aebb8f541f 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -735,17 +735,6 @@ impl<'hir> Map<'hir> { } } - /// Returns the `OwnerId` of `id`'s nearest module parent, or `id` itself if no - /// module parent is in this map. - pub(super) fn get_module_parent_node(self, hir_id: HirId) -> OwnerId { - for (def_id, node) in self.parent_owner_iter(hir_id) { - if let OwnerNode::Item(&Item { kind: ItemKind::Mod(_), .. }) = node { - return def_id; - } - } - CRATE_OWNER_ID - } - /// When on an if expression, a match arm tail expression or a match arm, give back /// the enclosing `if` or `match` expression. /// diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 45a07fdd293..06b25556c82 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -102,7 +102,21 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn parent_module(self, id: HirId) -> LocalDefId { - self.parent_module_from_def_id(id.owner.def_id) + if !id.is_owner() && self.def_kind(id.owner) == DefKind::Mod { + id.owner.def_id + } else { + self.parent_module_from_def_id(id.owner.def_id) + } + } + + pub fn parent_module_from_def_id(self, mut id: LocalDefId) -> LocalDefId { + while let Some(parent) = self.opt_local_parent(id) { + id = parent; + if self.def_kind(id) == DefKind::Mod { + break; + } + } + id } pub fn impl_subject(self, def_id: DefId) -> EarlyBinder<ImplSubject<'tcx>> { @@ -120,10 +134,6 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn provide(providers: &mut Providers) { - providers.parent_module_from_def_id = |tcx, id| { - let hir = tcx.hir(); - hir.get_module_parent_node(hir.local_def_id_to_hir_id(id)).def_id - }; providers.hir_crate_items = map::hir_crate_items; providers.crate_hash = map::crate_hash; providers.hir_module_items = map::hir_module_items; diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index 8a22de931c3..32f3a177508 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -36,6 +36,10 @@ pub enum ProjectionKind { /// A subslice covering a range of values like `B[x..y]`. Subslice, + + /// A conversion from an opaque type to its hidden type so we can + /// do further projections on it. + OpaqueCast, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 1b125e8e26d..f5576b59571 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -64,6 +64,7 @@ #![feature(macro_metavar_expr)] #![recursion_limit = "512"] #![allow(rustc::potential_query_instability)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate bitflags; diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 9ecc7580f5c..f62e406692a 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -449,7 +449,11 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool { match expn_data.kind { ExpnKind::Root | ExpnKind::Desugaring( - DesugaringKind::ForLoop | DesugaringKind::WhileLoop | DesugaringKind::OpaqueTy, + DesugaringKind::ForLoop + | DesugaringKind::WhileLoop + | DesugaringKind::OpaqueTy + | DesugaringKind::Async + | DesugaringKind::Await, ) => false, ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external" ExpnKind::Macro(MacroKind::Bang, _) => { @@ -459,3 +463,12 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool { ExpnKind::Macro { .. } => true, // definitely a plugin } } + +/// Return whether `span` is generated by `async` or `await`. +pub fn is_from_async_await(span: Span) -> bool { + let expn_data = span.ctxt().outer_expn_data(); + match expn_data.kind { + ExpnKind::Desugaring(DesugaringKind::Async | DesugaringKind::Await) => true, + _ => false, + } +} diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index c1cb2f2e497..2567170f39a 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -18,9 +18,9 @@ use rustc_span::DUMMY_SP; use rustc_target::abi::{Align, HasDataLayout, Size}; use super::{ - read_target_uint, write_target_uint, AllocId, InterpError, InterpResult, Pointer, Provenance, - ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess, - UnsupportedOpInfo, + read_target_uint, write_target_uint, AllocId, BadBytesAccess, InterpError, InterpResult, + Pointer, PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, + UndefinedBehaviorInfo, UnsupportedOpInfo, }; use crate::ty; use init_mask::*; @@ -173,13 +173,13 @@ pub enum AllocError { /// A scalar had the wrong size. ScalarSizeMismatch(ScalarSizeMismatch), /// Encountered a pointer where we needed raw bytes. - ReadPointerAsBytes, + ReadPointerAsInt(Option<BadBytesAccess>), /// Partially overwriting a pointer. - PartialPointerOverwrite(Size), + OverwritePartialPointer(Size), /// Partially copying a pointer. - PartialPointerCopy(Size), + ReadPartialPointer(Size), /// Using uninitialized data where it is not allowed. - InvalidUninitBytes(Option<UninitBytesAccess>), + InvalidUninitBytes(Option<BadBytesAccess>), } pub type AllocResult<T = ()> = Result<T, AllocError>; @@ -196,12 +196,14 @@ impl AllocError { ScalarSizeMismatch(s) => { InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s)) } - ReadPointerAsBytes => InterpError::Unsupported(UnsupportedOpInfo::ReadPointerAsBytes), - PartialPointerOverwrite(offset) => InterpError::Unsupported( - UnsupportedOpInfo::PartialPointerOverwrite(Pointer::new(alloc_id, offset)), + ReadPointerAsInt(info) => InterpError::Unsupported( + UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))), ), - PartialPointerCopy(offset) => InterpError::Unsupported( - UnsupportedOpInfo::PartialPointerCopy(Pointer::new(alloc_id, offset)), + OverwritePartialPointer(offset) => InterpError::Unsupported( + UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)), + ), + ReadPartialPointer(offset) => InterpError::Unsupported( + UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)), ), InvalidUninitBytes(info) => InterpError::UndefinedBehavior( UndefinedBehaviorInfo::InvalidUninitBytes(info.map(|b| (alloc_id, b))), @@ -433,14 +435,26 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> range: AllocRange, ) -> AllocResult<&[u8]> { self.init_mask.is_range_initialized(range).map_err(|uninit_range| { - AllocError::InvalidUninitBytes(Some(UninitBytesAccess { + AllocError::InvalidUninitBytes(Some(BadBytesAccess { access: range, - uninit: uninit_range, + bad: uninit_range, })) })?; if !Prov::OFFSET_IS_ADDR { if !self.provenance.range_empty(range, cx) { - return Err(AllocError::ReadPointerAsBytes); + // Find the provenance. + let (offset, _prov) = self + .provenance + .range_get_ptrs(range, cx) + .first() + .copied() + .expect("there must be provenance somewhere here"); + let start = offset.max(range.start); // the pointer might begin before `range`! + let end = (offset + cx.pointer_size()).min(range.end()); // the pointer might end after `range`! + return Err(AllocError::ReadPointerAsInt(Some(BadBytesAccess { + access: range, + bad: AllocRange::from(start..end), + }))); } } Ok(self.get_bytes_unchecked(range)) @@ -536,23 +550,25 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> // Now use this provenance. let ptr = Pointer::new(prov, Size::from_bytes(bits)); return Ok(Scalar::from_maybe_pointer(ptr, cx)); + } else { + // Without OFFSET_IS_ADDR, the only remaining case we can handle is total absence of + // provenance. + if self.provenance.range_empty(range, cx) { + return Ok(Scalar::from_uint(bits, range.size)); + } + // Else we have mixed provenance, that doesn't work. + return Err(AllocError::ReadPartialPointer(range.start)); } } else { // We are *not* reading a pointer. - // If we can just ignore provenance, do exactly that. - if Prov::OFFSET_IS_ADDR { + // If we can just ignore provenance or there is none, that's easy. + if Prov::OFFSET_IS_ADDR || self.provenance.range_empty(range, cx) { // We just strip provenance. return Ok(Scalar::from_uint(bits, range.size)); } + // There is some provenance and we don't have OFFSET_IS_ADDR. This doesn't work. + return Err(AllocError::ReadPointerAsInt(None)); } - - // Fallback path for when we cannot treat provenance bytewise or ignore it. - assert!(!Prov::OFFSET_IS_ADDR); - if !self.provenance.range_empty(range, cx) { - return Err(AllocError::ReadPointerAsBytes); - } - // There is no provenance, we can just return the bits. - Ok(Scalar::from_uint(bits, range.size)) } /// Writes a *non-ZST* scalar. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 318f93e12b5..0243fc4513a 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -66,7 +66,11 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { /// Returns all ptr-sized provenance in the given range. /// If the range has length 0, returns provenance that crosses the edge between `start-1` and /// `start`. - fn range_get_ptrs(&self, range: AllocRange, cx: &impl HasDataLayout) -> &[(Size, Prov)] { + pub(super) fn range_get_ptrs( + &self, + range: AllocRange, + cx: &impl HasDataLayout, + ) -> &[(Size, Prov)] { // We have to go back `pointer_size - 1` bytes, as that one would still overlap with // the beginning of this range. let adjusted_start = Size::from_bytes( @@ -158,7 +162,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { if first < start { if !Prov::OFFSET_IS_ADDR { // We can't split up the provenance into less than a pointer. - return Err(AllocError::PartialPointerOverwrite(first)); + return Err(AllocError::OverwritePartialPointer(first)); } // Insert the remaining part in the bytewise provenance. let prov = self.ptrs[&first]; @@ -171,7 +175,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { let begin_of_last = last - cx.data_layout().pointer_size; if !Prov::OFFSET_IS_ADDR { // We can't split up the provenance into less than a pointer. - return Err(AllocError::PartialPointerOverwrite(begin_of_last)); + return Err(AllocError::OverwritePartialPointer(begin_of_last)); } // Insert the remaining part in the bytewise provenance. let prov = self.ptrs[&begin_of_last]; @@ -246,10 +250,10 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { if !Prov::OFFSET_IS_ADDR { // There can't be any bytewise provenance, and we cannot split up the begin/end overlap. if let Some(entry) = begin_overlap { - return Err(AllocError::PartialPointerCopy(entry.0)); + return Err(AllocError::ReadPartialPointer(entry.0)); } if let Some(entry) = end_overlap { - return Err(AllocError::PartialPointerCopy(entry.0)); + return Err(AllocError::ReadPartialPointer(entry.0)); } debug_assert!(self.bytes.is_none()); } else { diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index d44dfa2172a..2e46bfc1a38 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -134,10 +134,6 @@ impl InterpErrorBacktrace { } impl<'tcx> InterpErrorInfo<'tcx> { - pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self { - Self(Box::new(InterpErrorInfoInner { kind, backtrace })) - } - pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) { let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self; (kind, backtrace) @@ -155,7 +151,7 @@ impl<'tcx> InterpErrorInfo<'tcx> { } fn print_backtrace(backtrace: &Backtrace) { - eprintln!("\n\nAn error occurred in miri:\n{backtrace}"); + eprintln!("\n\nAn error occurred in the MIR interpreter:\n{backtrace}"); } impl From<ErrorGuaranteed> for InterpErrorInfo<'_> { @@ -226,13 +222,13 @@ impl IntoDiagnosticArg for InvalidMetaKind { } } -/// Details of an access to uninitialized bytes where it is not allowed. +/// Details of an access to uninitialized bytes / bad pointer bytes where it is not allowed. #[derive(Debug, Clone, Copy)] -pub struct UninitBytesAccess { +pub struct BadBytesAccess { /// Range of the original memory access. pub access: AllocRange, - /// Range of the uninit memory that was encountered. (Might not be maximal.) - pub uninit: AllocRange, + /// Range of the bad memory that was encountered. (Might not be maximal.) + pub bad: AllocRange, } /// Information about a size mismatch. @@ -282,8 +278,8 @@ pub enum UndefinedBehaviorInfo<'a> { InvalidMeta(InvalidMetaKind), /// Reading a C string that does not end within its allocation. UnterminatedCString(Pointer), - /// Dereferencing a dangling pointer after it got freed. - PointerUseAfterFree(AllocId), + /// Using a pointer after it got freed. + PointerUseAfterFree(AllocId, CheckInAllocMsg), /// Used a pointer outside the bounds it is valid for. /// (If `ptr_size > 0`, determines the size of the memory range that was expected to be in-bounds.) PointerOutOfBounds { @@ -316,7 +312,7 @@ pub enum UndefinedBehaviorInfo<'a> { /// Using a string that is not valid UTF-8, InvalidStr(std::str::Utf8Error), /// Using uninitialized data where it is not allowed. - InvalidUninitBytes(Option<(AllocId, UninitBytesAccess)>), + InvalidUninitBytes(Option<(AllocId, BadBytesAccess)>), /// Working with a local that is not currently live. DeadLocal, /// Data size is not equal to target size. @@ -326,7 +322,7 @@ pub enum UndefinedBehaviorInfo<'a> { /// An uninhabited enum variant is projected. UninhabitedEnumVariantRead(VariantIdx), /// Validation error. - Validation(ValidationErrorInfo<'a>), + ValidationError(ValidationErrorInfo<'a>), // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically // dispatched /// A custom (free-form) error, created by `err_ub_custom!`. @@ -368,6 +364,8 @@ pub enum ExpectedKind { Float, Int, FnPtr, + EnumTag, + Str, } impl From<PointerKind> for ExpectedKind { @@ -381,10 +379,11 @@ impl From<PointerKind> for ExpectedKind { #[derive(Debug)] pub enum ValidationErrorKind<'tcx> { + PointerAsInt { expected: ExpectedKind }, + PartialPointer, PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> }, PtrToStatic { ptr_kind: PointerKind }, PtrToMut { ptr_kind: PointerKind }, - ExpectedNonPtr { value: String }, MutableRefInConst, NullFnPtr, NeverVal, @@ -394,11 +393,8 @@ pub enum ValidationErrorKind<'tcx> { UnsafeCell, UninhabitedVal { ty: Ty<'tcx> }, InvalidEnumTag { value: String }, - UninhabitedEnumTag, - UninitEnumTag, - UninitStr, + UninhabitedEnumVariant, Uninit { expected: ExpectedKind }, - UninitVal, InvalidVTablePtr { value: String }, InvalidMetaSliceTooLarge { ptr_kind: PointerKind }, InvalidMetaTooLarge { ptr_kind: PointerKind }, @@ -426,12 +422,12 @@ pub enum UnsupportedOpInfo { // /// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state /// cannot be represented by the CTFE interpreter. - PartialPointerOverwrite(Pointer<AllocId>), - /// Attempting to `copy` parts of a pointer to somewhere else; without knowing absolute + OverwritePartialPointer(Pointer<AllocId>), + /// Attempting to read or copy parts of a pointer to somewhere else; without knowing absolute /// addresses, the resulting state cannot be represented by the CTFE interpreter. - PartialPointerCopy(Pointer<AllocId>), - /// Encountered a pointer where we needed raw bytes. - ReadPointerAsBytes, + ReadPartialPointer(Pointer<AllocId>), + /// Encountered a pointer where we needed an integer. + ReadPointerAsInt(Option<(AllocId, BadBytesAccess)>), /// Accessing thread local statics ThreadLocalStatic(DefId), /// Accessing an unsupported extern static. @@ -497,7 +493,7 @@ impl InterpError<'_> { matches!( self, InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) - | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Validation { .. }) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. }) | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) ) } diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 8b5a8d17301..3543158bf82 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -142,11 +142,11 @@ use crate::ty::GenericArgKind; use crate::ty::{self, Instance, Ty, TyCtxt}; pub use self::error::{ - struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult, - EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, - InvalidProgramInfo, MachineStopType, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo, - ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo, - ValidationErrorInfo, ValidationErrorKind, + struct_error, BadBytesAccess, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, + EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, + InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, PointerKind, + ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo, + UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind, }; pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar}; @@ -609,7 +609,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Panics in case the `AllocId` is dangling. Since that is impossible for `AllocId`s in /// constants (as all constants must pass interning and validation that check for dangling /// ids), this function is frequently used throughout rustc, but should not be used within - /// the miri engine. + /// the interpreter. pub fn global_alloc(self, id: AllocId) -> GlobalAlloc<'tcx> { match self.try_get_global_alloc(id) { Some(alloc) => alloc, diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 20861d5ffa4..5345a658803 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -378,15 +378,16 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> { #[inline] pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); - self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsBytes))?.to_bits(target_size).map_err( - |size| { + self.try_to_int() + .map_err(|_| err_unsup!(ReadPointerAsInt(None)))? + .to_bits(target_size) + .map_err(|size| { err_ub!(ScalarSizeMismatch(ScalarSizeMismatch { target_size: target_size.bytes(), data_size: size.bytes(), })) .into() - }, - ) + }) } #[inline(always)] diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index c1f87d79b83..ddb5e248cdc 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1592,14 +1592,13 @@ impl<'tcx> Place<'tcx> { self.projection.iter().any(|elem| elem.is_indirect()) } - /// If MirPhase >= Derefered and if projection contains Deref, - /// It's guaranteed to be in the first place - pub fn has_deref(&self) -> bool { - // To make sure this is not accidentally used in wrong mir phase - debug_assert!( - self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref) - ); - self.projection.first() == Some(&PlaceElem::Deref) + /// Returns `true` if this `Place`'s first projection is `Deref`. + /// + /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later, + /// `Deref` projections can only occur as the first projection. In that case this method + /// is equivalent to `is_indirect`, but faster. + pub fn is_indirect_first_projection(&self) -> bool { + self.as_ref().is_indirect_first_projection() } /// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or @@ -1672,9 +1671,16 @@ impl<'tcx> PlaceRef<'tcx> { self.projection.iter().any(|elem| elem.is_indirect()) } - /// If MirPhase >= Derefered and if projection contains Deref, - /// It's guaranteed to be in the first place - pub fn has_deref(&self) -> bool { + /// Returns `true` if this `Place`'s first projection is `Deref`. + /// + /// This is useful because for MIR phases `AnalysisPhase::PostCleanup` and later, + /// `Deref` projections can only occur as the first projection. In that case this method + /// is equivalent to `is_indirect`, but faster. + pub fn is_indirect_first_projection(&self) -> bool { + // To make sure this is not accidentally used in wrong mir phase + debug_assert!( + self.projection.is_empty() || !self.projection[1..].contains(&PlaceElem::Deref) + ); self.projection.first() == Some(&PlaceElem::Deref) } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 63df2830e0a..533c3b66cab 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -398,11 +398,6 @@ rustc_queries! { desc { "computing `#[expect]`ed lints in this crate" } } - query parent_module_from_def_id(key: LocalDefId) -> LocalDefId { - eval_always - desc { |tcx| "getting the parent module of `{}`", tcx.def_path_str(key) } - } - query expn_that_defined(key: DefId) -> rustc_span::ExpnId { desc { |tcx| "getting the expansion that defined `{}`", tcx.def_path_str(key) } separate_provide_extern @@ -706,7 +701,7 @@ rustc_queries! { separate_provide_extern } - query adt_sized_constraint(key: DefId) -> &'tcx [Ty<'tcx>] { + query adt_sized_constraint(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> { desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) } } @@ -749,7 +744,7 @@ rustc_queries! { separate_provide_extern } - /// Gets a map with the variance of every item; use `item_variance` instead. + /// Gets a map with the variance of every item; use `variances_of` instead. query crate_variances(_: ()) -> &'tcx ty::CrateVariancesMap<'tcx> { arena_cache desc { "computing the variances for items in this crate" } @@ -1596,6 +1591,11 @@ rustc_queries! { separate_provide_extern } + /// Lint against `extern fn` declarations having incompatible types. + query clashing_extern_declarations(_: ()) { + desc { "checking `extern fn` declarations are compatible" } + } + /// Identifies the entry-point (e.g., the `main` function) for a given /// crate, returning `None` if there is no entry point (such as for library crates). query entry_fn(_: ()) -> Option<(DefId, EntryFnType)> { diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index b21a00e4122..9d63d291854 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -1,7 +1,6 @@ use std::ops::ControlFlow; use rustc_data_structures::intern::Interned; -use rustc_query_system::cache::Cache; use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints}; use crate::traits::query::NoSolution; @@ -11,9 +10,10 @@ use crate::ty::{ TypeVisitor, }; +mod cache; pub mod inspect; -pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>; +pub use cache::{CacheData, EvaluationCache}; /// A goal is a statement, i.e. `predicate`, we want to prove /// given some assumptions, i.e. `param_env`. @@ -147,7 +147,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> { } /// Additional constraints returned on success. -#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)] +#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)] pub struct ExternalConstraintsData<'tcx> { // FIXME: implement this. pub region_constraints: QueryRegionConstraints<'tcx>, diff --git a/compiler/rustc_middle/src/traits/solve/cache.rs b/compiler/rustc_middle/src/traits/solve/cache.rs new file mode 100644 index 00000000000..9898b0019bb --- /dev/null +++ b/compiler/rustc_middle/src/traits/solve/cache.rs @@ -0,0 +1,100 @@ +use super::{CanonicalInput, QueryResult}; +use crate::ty::TyCtxt; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::sync::Lock; +use rustc_query_system::cache::WithDepNode; +use rustc_query_system::dep_graph::DepNodeIndex; +use rustc_session::Limit; +/// The trait solver cache used by `-Ztrait-solver=next`. +/// +/// FIXME(@lcnr): link to some official documentation of how +/// this works. +#[derive(Default)] +pub struct EvaluationCache<'tcx> { + map: Lock<FxHashMap<CanonicalInput<'tcx>, CacheEntry<'tcx>>>, +} + +pub struct CacheData<'tcx> { + pub result: QueryResult<'tcx>, + pub reached_depth: usize, + pub encountered_overflow: bool, +} + +impl<'tcx> EvaluationCache<'tcx> { + /// Insert a final result into the global cache. + pub fn insert( + &self, + key: CanonicalInput<'tcx>, + reached_depth: usize, + did_overflow: bool, + cycle_participants: FxHashSet<CanonicalInput<'tcx>>, + dep_node: DepNodeIndex, + result: QueryResult<'tcx>, + ) { + let mut map = self.map.borrow_mut(); + let entry = map.entry(key).or_default(); + let data = WithDepNode::new(dep_node, result); + entry.cycle_participants.extend(cycle_participants); + if did_overflow { + entry.with_overflow.insert(reached_depth, data); + } else { + entry.success = Some(Success { data, reached_depth }); + } + } + + /// Try to fetch a cached result, checking the recursion limit + /// and handling root goals of coinductive cycles. + /// + /// If this returns `Some` the cache result can be used. + pub fn get( + &self, + tcx: TyCtxt<'tcx>, + key: CanonicalInput<'tcx>, + cycle_participant_in_stack: impl FnOnce(&FxHashSet<CanonicalInput<'tcx>>) -> bool, + available_depth: Limit, + ) -> Option<CacheData<'tcx>> { + let map = self.map.borrow(); + let entry = map.get(&key)?; + + if cycle_participant_in_stack(&entry.cycle_participants) { + return None; + } + + if let Some(ref success) = entry.success { + if available_depth.value_within_limit(success.reached_depth) { + return Some(CacheData { + result: success.data.get(tcx), + reached_depth: success.reached_depth, + encountered_overflow: false, + }); + } + } + + entry.with_overflow.get(&available_depth.0).map(|e| CacheData { + result: e.get(tcx), + reached_depth: available_depth.0, + encountered_overflow: true, + }) + } +} + +struct Success<'tcx> { + data: WithDepNode<QueryResult<'tcx>>, + reached_depth: usize, +} + +/// The cache entry for a goal `CanonicalInput`. +/// +/// This contains results whose computation never hit the +/// recursion limit in `success`, and all results which hit +/// the recursion limit in `with_overflow`. +#[derive(Default)] +struct CacheEntry<'tcx> { + success: Option<Success<'tcx>>, + /// We have to be careful when caching roots of cycles. + /// + /// See the doc comment of `StackEntry::cycle_participants` for more + /// details. + cycle_participants: FxHashSet<CanonicalInput<'tcx>>, + with_overflow: FxHashMap<usize, WithDepNode<QueryResult<'tcx>>>, +} diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index e793f481995..4e2af3816ac 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -73,12 +73,15 @@ pub struct GoalCandidate<'tcx> { pub enum CandidateKind<'tcx> { /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, - DynUpcastingAssembly, /// A normal candidate for proving a goal - Candidate { - name: String, - result: QueryResult<'tcx>, - }, + Candidate { name: String, result: QueryResult<'tcx> }, + /// Used in the probe that wraps normalizing the non-self type for the unsize + /// trait, which is also structurally matched on. + UnsizeAssembly, + /// During upcasting from some source object to target object type, used to + /// do a probe to find out what projection type(s) may be used to prove that + /// the source type upholds all of the target type's object bounds. + UpcastProbe, } impl Debug for GoalCandidate<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index f1e567e9bf8..8759fecb05a 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -100,8 +100,11 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { CandidateKind::NormalizedSelfTyAssembly => { writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") } - CandidateKind::DynUpcastingAssembly => { - writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:") + CandidateKind::UnsizeAssembly => { + writeln!(self.f, "ASSEMBLING CANDIDATES FOR UNSIZING:") + } + CandidateKind::UpcastProbe => { + writeln!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:") } CandidateKind::Candidate { name, result } => { writeln!(self.f, "CANDIDATE {name}: {result:?}") diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index e067d2a984f..6ba13a76563 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -562,18 +562,10 @@ impl<'tcx> AdtDef<'tcx> { tcx.adt_destructor(self.did()) } - /// Returns a list of types such that `Self: Sized` if and only - /// if that type is `Sized`, or `TyErr` if this type is recursive. - /// - /// Oddly enough, checking that the sized-constraint is `Sized` is - /// actually more expressive than checking all members: - /// the `Sized` trait is inductive, so an associated type that references - /// `Self` would prevent its containing ADT from being `Sized`. - /// - /// Due to normalization being eager, this applies even if - /// the associated type is behind a pointer (e.g., issue #31299). - pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<&'tcx [Ty<'tcx>]> { - ty::EarlyBinder::bind(tcx.adt_sized_constraint(self.did())) + /// Returns a list of types such that `Self: Sized` if and only if that + /// type is `Sized`, or `ty::Error` if this type has a recursive layout. + pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> { + tcx.adt_sized_constraint(self.did()) } } diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 42f22604975..2e5c6a44579 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -174,6 +174,8 @@ impl<'tcx> CapturedPlace<'tcx> { // Ignore derefs for now, as they are likely caused by // autoderefs that don't appear in the original code. HirProjectionKind::Deref => {} + // Just change the type to the hidden type, so we can actually project. + HirProjectionKind::OpaqueCast => {} proj => bug!("Unexpected projection {:?} in captured place", proj), } ty = proj.ty; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index aa1f8e48b1b..5eef3e45f32 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -50,9 +50,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{ - Constness, ExprKind, HirId, ImplItemKind, ItemKind, Node, TraitCandidate, TraitItemKind, -}; +use rustc_hir::{Constness, HirId, Node, TraitCandidate}; use rustc_index::IndexVec; use rustc_macros::HashStable; use rustc_query_system::dep_graph::DepNodeIndex; @@ -1077,31 +1075,6 @@ impl<'tcx> TyCtxt<'tcx> { return None; } - pub fn return_type_impl_trait(self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> { - // `type_of()` will fail on these (#55796, #86483), so only allow `fn`s or closures. - match self.hir().get_by_def_id(scope_def_id) { - Node::Item(&hir::Item { kind: ItemKind::Fn(..), .. }) => {} - Node::TraitItem(&hir::TraitItem { kind: TraitItemKind::Fn(..), .. }) => {} - Node::ImplItem(&hir::ImplItem { kind: ImplItemKind::Fn(..), .. }) => {} - Node::Expr(&hir::Expr { kind: ExprKind::Closure { .. }, .. }) => {} - _ => return None, - } - - let ret_ty = self.type_of(scope_def_id).instantiate_identity(); - match ret_ty.kind() { - ty::FnDef(_, _) => { - let sig = ret_ty.fn_sig(self); - let output = self.erase_late_bound_regions(sig.output()); - output.is_impl_trait().then(|| { - let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id); - let fn_decl = self.hir().fn_decl_by_hir_id(hir_id).unwrap(); - (output, fn_decl.output.span()) - }) - } - _ => None, - } - } - /// Checks if the bound region is in Impl Item. pub fn is_bound_region_in_impl_item(self, suitable_region_binding_scope: LocalDefId) -> bool { let container_id = self.parent(suitable_region_binding_scope.to_def_id()); diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 0f7bed0845c..df39103bc19 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -911,7 +911,7 @@ where if i == tag_field { return TyMaybeWithLayout::TyAndLayout(tag_layout(tag)); } - TyMaybeWithLayout::Ty(args.as_generator().prefix_tys().nth(i).unwrap()) + TyMaybeWithLayout::Ty(args.as_generator().prefix_tys()[i]) } }, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2b4c834f766..48aa25dba6d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -498,11 +498,9 @@ impl<'tcx> Predicate<'tcx> { .map_bound(|kind| match kind { PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { trait_ref, - constness, polarity, })) => Some(PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { trait_ref, - constness, polarity: polarity.flip()?, }))), @@ -513,19 +511,6 @@ impl<'tcx> Predicate<'tcx> { Some(tcx.mk_predicate(kind)) } - pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> Self { - if let PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { trait_ref, constness, polarity })) = self.kind().skip_binder() - && constness != BoundConstness::NotConst - { - self = tcx.mk_predicate(self.kind().rebind(PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { - trait_ref, - constness: BoundConstness::NotConst, - polarity, - })))); - } - self - } - #[instrument(level = "debug", skip(tcx), ret)] pub fn is_coinductive(self, tcx: TyCtxt<'tcx>) -> bool { match self.kind().skip_binder() { @@ -629,10 +614,6 @@ impl<'tcx> Clause<'tcx> { None } } - - pub fn without_const(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> { - self.as_predicate().without_const(tcx).expect_clause() - } } #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] @@ -852,8 +833,6 @@ impl<'tcx> Clause<'tcx> { pub struct TraitPredicate<'tcx> { pub trait_ref: TraitRef<'tcx>, - pub constness: BoundConstness, - /// If polarity is Positive: we are proving that the trait is implemented. /// /// If polarity is Negative: we are proving that a negative impl of this trait @@ -878,24 +857,6 @@ impl<'tcx> TraitPredicate<'tcx> { pub fn self_ty(self) -> Ty<'tcx> { self.trait_ref.self_ty() } - - #[inline] - pub fn is_const_if_const(self) -> bool { - self.constness == BoundConstness::ConstIfConst - } - - pub fn is_constness_satisfied_by(self, constness: hir::Constness) -> bool { - match (self.constness, constness) { - (BoundConstness::NotConst, _) - | (BoundConstness::ConstIfConst, hir::Constness::Const) => true, - (BoundConstness::ConstIfConst, hir::Constness::NotConst) => false, - } - } - - pub fn without_const(mut self) -> Self { - self.constness = BoundConstness::NotConst; - self - } } impl<'tcx> PolyTraitPredicate<'tcx> { @@ -909,11 +870,6 @@ impl<'tcx> PolyTraitPredicate<'tcx> { } #[inline] - pub fn is_const_if_const(self) -> bool { - self.skip_binder().is_const_if_const() - } - - #[inline] pub fn polarity(self) -> ImplPolarity { self.skip_binder().polarity } @@ -1287,7 +1243,7 @@ impl<'tcx> ToPredicate<'tcx> for TraitRef<'tcx> { impl<'tcx> ToPredicate<'tcx, TraitPredicate<'tcx>> for TraitRef<'tcx> { #[inline(always)] fn to_predicate(self, _tcx: TyCtxt<'tcx>) -> TraitPredicate<'tcx> { - self.without_const() + TraitPredicate { trait_ref: self, polarity: ImplPolarity::Positive } } } @@ -1328,7 +1284,6 @@ impl<'tcx> ToPredicate<'tcx, PolyTraitPredicate<'tcx>> for Binder<'tcx, TraitRef fn to_predicate(self, _: TyCtxt<'tcx>) -> PolyTraitPredicate<'tcx> { self.map_bound(|trait_ref| TraitPredicate { trait_ref, - constness: ty::BoundConstness::NotConst, polarity: ty::ImplPolarity::Positive, }) } @@ -1383,12 +1338,25 @@ impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { } } +impl<'tcx> ToPredicate<'tcx> for ProjectionPredicate<'tcx> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::Binder::dummy(PredicateKind::Clause(ClauseKind::Projection(self))).to_predicate(tcx) + } +} + impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { self.map_bound(|p| PredicateKind::Clause(ClauseKind::Projection(p))).to_predicate(tcx) } } +impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for ProjectionPredicate<'tcx> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> { + let p: Predicate<'tcx> = self.to_predicate(tcx); + p.expect_clause() + } +} + impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyProjectionPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> { let p: Predicate<'tcx> = self.to_predicate(tcx); @@ -1826,24 +1794,6 @@ impl<'tcx> ParamEnv<'tcx> { } } -// FIXME(ecstaticmorse): Audit all occurrences of `without_const().to_predicate(tcx)` to ensure that -// the constness of trait bounds is being propagated correctly. -impl<'tcx> PolyTraitRef<'tcx> { - #[inline] - pub fn with_constness(self, constness: BoundConstness) -> PolyTraitPredicate<'tcx> { - self.map_bound(|trait_ref| ty::TraitPredicate { - trait_ref, - constness, - polarity: ty::ImplPolarity::Positive, - }) - } - - #[inline] - pub fn without_const(self) -> PolyTraitPredicate<'tcx> { - self.with_constness(BoundConstness::NotConst) - } -} - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)] #[derive(HashStable, Lift)] pub struct ParamEnvAnd<'tcx, T> { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f146f8aa8b4..d3fd49150ba 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -827,7 +827,7 @@ pub trait PrettyPrinter<'tcx>: if !args.as_generator().is_valid() { p!("unavailable"); } else { - self = self.comma_sep(args.as_generator().upvar_tys())?; + self = self.comma_sep(args.as_generator().upvar_tys().iter())?; } p!(")"); @@ -900,7 +900,7 @@ pub trait PrettyPrinter<'tcx>: print(args.as_closure().sig_as_fn_ptr_ty()) ); p!(" upvar_tys=("); - self = self.comma_sep(args.as_closure().upvar_tys())?; + self = self.comma_sep(args.as_closure().upvar_tys().iter())?; p!(")"); } } @@ -2734,8 +2734,9 @@ forward_display_to_print! { // HACK(eddyb) these are exhaustive instead of generic, // because `for<'tcx>` isn't possible yet. ty::PolyExistentialPredicate<'tcx>, + ty::PolyExistentialProjection<'tcx>, + ty::PolyExistentialTraitRef<'tcx>, ty::Binder<'tcx, ty::TraitRef<'tcx>>, - ty::Binder<'tcx, ty::ExistentialTraitRef<'tcx>>, ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, ty::Binder<'tcx, TraitRefPrintOnlyTraitName<'tcx>>, ty::Binder<'tcx, ty::FnSig<'tcx>>, @@ -2806,10 +2807,7 @@ define_print_and_forward_display! { } TraitPredPrintModifiersAndPath<'tcx> { - if let ty::BoundConstness::ConstIfConst = self.0.constness { - p!("~const ") - } - + // FIXME(effects) print `~const` here if let ty::ImplPolarity::Negative = self.0.polarity { p!("!") } @@ -2843,9 +2841,7 @@ define_print_and_forward_display! { ty::TraitPredicate<'tcx> { p!(print(self.trait_ref.self_ty()), ": "); - if let ty::BoundConstness::ConstIfConst = self.constness && cx.tcx().features().const_trait_impl { - p!("~const "); - } + // FIXME(effects) print `~const` here if let ty::ImplPolarity::Negative = self.polarity { p!("!"); } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index bf9adabdab1..47512d350e5 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -826,7 +826,6 @@ impl<'tcx> Relate<'tcx> for ty::TraitPredicate<'tcx> { ) -> RelateResult<'tcx, ty::TraitPredicate<'tcx>> { Ok(ty::TraitPredicate { trait_ref: relation.relate(a.trait_ref, b.trait_ref)?, - constness: relation.relate(a.constness, b.constness)?, polarity: relation.relate(a.polarity, b.polarity)?, }) } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index c6b6f0e8990..1347b35556d 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -172,9 +172,7 @@ impl fmt::Debug for ty::ParamConst { impl<'tcx> fmt::Debug for ty::TraitPredicate<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let ty::BoundConstness::ConstIfConst = self.constness { - write!(f, "~const ")?; - } + // FIXME(effects) printing? write!(f, "TraitPredicate({:?}, polarity:{:?})", self.trait_ref, self.polarity) } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 3e023ccdead..a695febf087 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -296,15 +296,13 @@ impl<'tcx> ClosureArgs<'tcx> { /// In case there was a type error in figuring out the types of the captured path, an /// empty iterator is returned. #[inline] - pub fn upvar_tys(self) -> impl Iterator<Item = Ty<'tcx>> + 'tcx { + pub fn upvar_tys(self) -> &'tcx List<Ty<'tcx>> { match self.tupled_upvars_ty().kind() { - TyKind::Error(_) => None, - TyKind::Tuple(..) => Some(self.tupled_upvars_ty().tuple_fields()), + TyKind::Error(_) => ty::List::empty(), + TyKind::Tuple(..) => self.tupled_upvars_ty().tuple_fields(), TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), } - .into_iter() - .flatten() } /// Returns the tuple type representing the upvars for this closure. @@ -436,15 +434,13 @@ impl<'tcx> GeneratorArgs<'tcx> { /// In case there was a type error in figuring out the types of the captured path, an /// empty iterator is returned. #[inline] - pub fn upvar_tys(self) -> impl Iterator<Item = Ty<'tcx>> + 'tcx { + pub fn upvar_tys(self) -> &'tcx List<Ty<'tcx>> { match self.tupled_upvars_ty().kind() { - TyKind::Error(_) => None, - TyKind::Tuple(..) => Some(self.tupled_upvars_ty().tuple_fields()), + TyKind::Error(_) => ty::List::empty(), + TyKind::Tuple(..) => self.tupled_upvars_ty().tuple_fields(), TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), } - .into_iter() - .flatten() } /// Returns the tuple type representing the upvars for this generator. @@ -576,7 +572,7 @@ impl<'tcx> GeneratorArgs<'tcx> { /// This is the types of the fields of a generator which are not stored in a /// variant. #[inline] - pub fn prefix_tys(self) -> impl Iterator<Item = Ty<'tcx>> { + pub fn prefix_tys(self) -> &'tcx List<Ty<'tcx>> { self.upvar_tys() } } @@ -592,20 +588,18 @@ impl<'tcx> UpvarArgs<'tcx> { /// In case there was a type error in figuring out the types of the captured path, an /// empty iterator is returned. #[inline] - pub fn upvar_tys(self) -> impl Iterator<Item = Ty<'tcx>> + 'tcx { + pub fn upvar_tys(self) -> &'tcx List<Ty<'tcx>> { let tupled_tys = match self { UpvarArgs::Closure(args) => args.as_closure().tupled_upvars_ty(), UpvarArgs::Generator(args) => args.as_generator().tupled_upvars_ty(), }; match tupled_tys.kind() { - TyKind::Error(_) => None, - TyKind::Tuple(..) => Some(self.tupled_upvars_ty().tuple_fields()), + TyKind::Error(_) => ty::List::empty(), + TyKind::Tuple(..) => self.tupled_upvars_ty().tuple_fields(), TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), } - .into_iter() - .flatten() } #[inline] @@ -728,7 +722,7 @@ impl<'tcx> PolyExistentialPredicate<'tcx> { use crate::ty::ToPredicate; match self.skip_binder() { ExistentialPredicate::Trait(tr) => { - self.rebind(tr).with_self_ty(tcx, self_ty).without_const().to_predicate(tcx) + self.rebind(tr).with_self_ty(tcx, self_ty).to_predicate(tcx) } ExistentialPredicate::Projection(p) => { self.rebind(p.with_self_ty(tcx, self_ty)).to_predicate(tcx) @@ -743,7 +737,7 @@ impl<'tcx> PolyExistentialPredicate<'tcx> { let err_args = ty::GenericArgs::extend_with_error(tcx, did, &[self_ty.into()]); ty::TraitRef::new(tcx, did, err_args) }; - self.rebind(trait_ref).without_const().to_predicate(tcx) + self.rebind(trait_ref).to_predicate(tcx) } } } @@ -875,18 +869,6 @@ impl<'tcx> TraitRef<'tcx> { ) } - /// Converts this trait ref to a trait predicate with a given `constness` and a positive polarity. - #[inline] - pub fn with_constness(self, constness: ty::BoundConstness) -> ty::TraitPredicate<'tcx> { - ty::TraitPredicate { trait_ref: self, constness, polarity: ty::ImplPolarity::Positive } - } - - /// Converts this trait ref to a trait predicate without `const` and a positive polarity. - #[inline] - pub fn without_const(self) -> ty::TraitPredicate<'tcx> { - self.with_constness(ty::BoundConstness::NotConst) - } - #[inline] pub fn self_ty(&self) -> Ty<'tcx> { self.args.type_at(0) diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 520bb55e031..156eda477ad 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -88,14 +88,10 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> { self.has_type_flags(TypeFlags::HAS_INFER) } fn has_placeholders(&self) -> bool { - self.has_type_flags( - TypeFlags::HAS_RE_PLACEHOLDER - | TypeFlags::HAS_TY_PLACEHOLDER - | TypeFlags::HAS_CT_PLACEHOLDER, - ) + self.has_type_flags(TypeFlags::HAS_PLACEHOLDER) } fn has_non_region_placeholders(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER) + self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER) } fn has_param(&self) -> bool { self.has_type_flags(TypeFlags::HAS_PARAM) diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 7756d5d4879..2e7ef265a93 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -236,6 +236,9 @@ fn strip_prefix<'a, 'tcx>( } assert_matches!(iter.next(), Some(ProjectionElem::Field(..))); } + HirProjectionKind::OpaqueCast => { + assert_matches!(iter.next(), Some(ProjectionElem::OpaqueCast(..))); + } HirProjectionKind::Index | HirProjectionKind::Subslice => { bug!("unexpected projection kind: {:?}", projection); } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index ff4620948fa..994ac8a3286 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -469,11 +469,17 @@ impl<'tcx> Cx<'tcx> { } } - hir::ExprKind::Index(ref lhs, ref index) => { + hir::ExprKind::Index(ref lhs, ref index, brackets_span) => { if self.typeck_results().is_method_call(expr) { let lhs = self.mirror_expr(lhs); let index = self.mirror_expr(index); - self.overloaded_place(expr, expr_ty, None, Box::new([lhs, index]), expr.span) + self.overloaded_place( + expr, + expr_ty, + None, + Box::new([lhs, index]), + brackets_span, + ) } else { ExprKind::Index { lhs: self.mirror_expr(lhs), index: self.mirror_expr(index) } } @@ -1072,6 +1078,9 @@ impl<'tcx> Cx<'tcx> { variant_index, name: field, }, + HirProjectionKind::OpaqueCast => { + ExprKind::Use { source: self.thir.exprs.push(captured_place_expr) } + } HirProjectionKind::Index | HirProjectionKind::Subslice => { // We don't capture these projections, so we can ignore them here continue; diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 1d7d905c937..9e02b027182 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -860,20 +860,14 @@ where fn open_drop(&mut self) -> BasicBlock { let ty = self.place_ty(self.place); match ty.kind() { - ty::Closure(_, args) => { - let tys: Vec<_> = args.as_closure().upvar_tys().collect(); - self.open_drop_for_tuple(&tys) - } + ty::Closure(_, args) => self.open_drop_for_tuple(&args.as_closure().upvar_tys()), // Note that `elaborate_drops` only drops the upvars of a generator, // and this is ok because `open_drop` here can only be reached // within that own generator's resume function. // This should only happen for the self argument on the resume function. // It effectively only contains upvars until the generator transformation runs. // See librustc_body/transform/generator.rs for more details. - ty::Generator(_, args, _) => { - let tys: Vec<_> = args.as_generator().upvar_tys().collect(); - self.open_drop_for_tuple(&tys) - } + ty::Generator(_, args, _) => self.open_drop_for_tuple(&args.as_generator().upvar_tys()), ty::Tuple(fields) => self.open_drop_for_tuple(fields), ty::Adt(def, args) => self.open_drop_for_adt(*def, args), ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind), diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 8d78ec04821..17bb8fc37ad 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -839,7 +839,7 @@ impl Map { tail_elem: Option<TrackElem>, f: &mut impl FnMut(ValueIndex), ) { - if place.has_deref() { + if place.is_indirect_first_projection() { // We do not track indirect places. return; } diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index d9e7339f1b2..75473ca53fb 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -60,7 +60,7 @@ impl<'tcx> MirPass<'tcx> for AddRetag { let basic_blocks = body.basic_blocks.as_mut(); let local_decls = &body.local_decls; let needs_retag = |place: &Place<'tcx>| { - !place.has_deref() // we're not really interested in stores to "outside" locations, they are hard to keep track of anyway + !place.is_indirect_first_projection() // we're not really interested in stores to "outside" locations, they are hard to keep track of anyway && may_contain_reference(place.ty(&*local_decls, tcx).ty, /*depth*/ 3, tcx) && !local_decls[place.local].is_deref_temp() }; diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs index 47d9f52bfb5..9a3798eea3b 100644 --- a/compiler/rustc_mir_transform/src/copy_prop.rs +++ b/compiler/rustc_mir_transform/src/copy_prop.rs @@ -154,7 +154,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) { if let Operand::Move(place) = *operand // A move out of a projection of a copy is equivalent to a copy of the original projection. - && !place.has_deref() + && !place.is_indirect_first_projection() && !self.fully_moved.contains(place.local) { *operand = Operand::Copy(place); diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 669135f80bc..797a1a86846 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -856,7 +856,7 @@ fn sanitize_witness<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, witness: Ty<'tcx>, - upvars: Vec<Ty<'tcx>>, + upvars: &'tcx ty::List<Ty<'tcx>>, layout: &GeneratorLayout<'tcx>, ) { let did = body.source.def_id(); @@ -1471,7 +1471,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { let args = args.as_generator(); ( args.discr_ty(tcx), - args.upvar_tys().collect::<Vec<_>>(), + args.upvar_tys(), args.witness(), movability == hir::Movability::Movable, ) diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index f13c8214af1..fc9e18378d5 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -223,19 +223,29 @@ impl<'tcx> Inliner<'tcx> { return Err("failed to normalize return type"); } if callsite.fn_sig.abi() == Abi::RustCall { - let (arg_tuple, skipped_args) = match &args[..] { - [arg_tuple] => (arg_tuple, 0), - [_, arg_tuple] => (arg_tuple, 1), + // FIXME: Don't inline user-written `extern "rust-call"` functions, + // since this is generally perf-negative on rustc, and we hope that + // LLVM will inline these functions instead. + if callee_body.spread_arg.is_some() { + return Err("do not inline user-written rust-call functions"); + } + + let (self_arg, arg_tuple) = match &args[..] { + [arg_tuple] => (None, arg_tuple), + [self_arg, arg_tuple] => (Some(self_arg), arg_tuple), _ => bug!("Expected `rust-call` to have 1 or 2 args"), }; + let self_arg_ty = + self_arg.map(|self_arg| self_arg.ty(&caller_body.local_decls, self.tcx)); + let arg_tuple_ty = arg_tuple.ty(&caller_body.local_decls, self.tcx); - let ty::Tuple(arg_tuple_tys) = arg_tuple_ty.kind() else { + let ty::Tuple(arg_tuple_tys) = *arg_tuple_ty.kind() else { bug!("Closure arguments are not passed as a tuple"); }; for (arg_ty, input) in - arg_tuple_tys.iter().zip(callee_body.args_iter().skip(skipped_args)) + self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter()) { let input_type = callee_body.local_decls[input].ty; if !util::is_subtype(self.tcx, self.param_env, input_type, arg_ty) { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 734321e97d8..ccb67248822 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -338,38 +338,16 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> { return shim::build_adt_ctor(tcx, def.to_def_id()); } - let context = tcx - .hir() - .body_const_context(def) - .expect("mir_for_ctfe should not be used for runtime functions"); - - let body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone(); + let body = tcx.mir_drops_elaborated_and_const_checked(def); + let body = match tcx.hir().body_const_context(def) { + // consts and statics do not have `optimized_mir`, so we can steal the body instead of + // cloning it. + Some(hir::ConstContext::Const | hir::ConstContext::Static(_)) => body.steal(), + Some(hir::ConstContext::ConstFn) => body.borrow().clone(), + None => bug!("`mir_for_ctfe` called on non-const {def:?}"), + }; let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const); - - match context { - // Do not const prop functions, either they get executed at runtime or exported to metadata, - // so we run const prop on them, or they don't, in which case we const evaluate some control - // flow paths of the function and any errors in those paths will get emitted as const eval - // errors. - hir::ConstContext::ConstFn => {} - // Static items always get evaluated, so we can just let const eval see if any erroneous - // control flow paths get executed. - hir::ConstContext::Static(_) => {} - // Associated constants get const prop run so we detect common failure situations in the - // crate that defined the constant. - // Technically we want to not run on regular const items, but oli-obk doesn't know how to - // conveniently detect that at this point without looking at the HIR. - hir::ConstContext::Const => { - pm::run_passes( - tcx, - &mut body, - &[&const_prop::ConstProp], - Some(MirPhase::Runtime(RuntimePhase::Optimized)), - ); - } - } - pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None); body diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index d1ecfe9f851..fc36c6e4124 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -305,7 +305,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { terminator.kind = TerminatorKind::Unreachable; } } - _ if intrinsic_name.as_str().starts_with("simd_shuffle") => { + sym::simd_shuffle => { validate_simd_shuffle(tcx, args, terminator.source_info.span); } _ => {} diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 5b678d4ba78..55b14ce1c3e 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -384,7 +384,7 @@ fn collect_items_rec<'tcx>( if let Ok(alloc) = tcx.eval_static_initializer(def_id) { for &id in alloc.inner().provenance().ptrs().values() { - collect_miri(tcx, id, &mut used_items); + collect_alloc(tcx, id, &mut used_items); } } @@ -1331,8 +1331,8 @@ fn create_mono_items_for_default_impls<'tcx>( } } -/// Scans the miri alloc in order to find function calls, closures, and drop-glue. -fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoItems<'tcx>) { +/// Scans the CTFE alloc in order to find function calls, closures, and drop-glue. +fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoItems<'tcx>) { match tcx.global_alloc(alloc_id) { GlobalAlloc::Static(def_id) => { assert!(!tcx.is_thread_local_static(def_id)); @@ -1346,7 +1346,7 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte trace!("collecting {:?} with {:#?}", alloc_id, alloc); for &inner in alloc.inner().provenance().ptrs().values() { rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_miri(tcx, inner, output); + collect_alloc(tcx, inner, output); }); } } @@ -1358,7 +1358,7 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte } GlobalAlloc::VTable(ty, trait_ref) => { let alloc_id = tcx.vtable_allocation((ty, trait_ref)); - collect_miri(tcx, alloc_id, output) + collect_alloc(tcx, alloc_id, output) } } } @@ -1381,10 +1381,10 @@ fn collect_const_value<'tcx>( output: &mut MonoItems<'tcx>, ) { match value { - ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => collect_miri(tcx, ptr.provenance, output), + ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => collect_alloc(tcx, ptr.provenance, output), ConstValue::Slice { data: alloc, start: _, end: _ } | ConstValue::ByRef { alloc, .. } => { for &id in alloc.inner().provenance().ptrs().values() { - collect_miri(tcx, id, output); + collect_alloc(tcx, id, output); } } _ => {} diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 83d96ad8e76..6888127f36c 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -23,6 +23,8 @@ parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or late parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015 .label = to use `async fn`, switch to Rust 2018 or later +parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later + parse_async_move_order_incorrect = the order of `move` and `async` is incorrect .suggestion = try switching the order @@ -270,6 +272,8 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}` parse_function_body_equals_expr = function body cannot be `= expression;` .suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;` +parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax + parse_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets .suggestion = surround the type parameters with angle brackets @@ -457,6 +461,12 @@ parse_loop_else = `{$loop_kind}...else` loops are not supported .note = consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run .loop_keyword = `else` is attached to this loop +parse_macro_expands_to_adt_field = macros cannot expand to {$adt_ty} fields + +parse_macro_expands_to_enum_variant = macros cannot expand to enum variants + +parse_macro_expands_to_match_arm = macros cannot expand to match arms + parse_macro_invocation_visibility = can't qualify macro invocation with `pub` .suggestion = remove the visibility .help = try adjusting the macro to put `{$vis}` inside the invocation diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 06c09960727..26f38c9156a 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1435,6 +1435,13 @@ pub(crate) struct AsyncBlockIn2015 { } #[derive(Diagnostic)] +#[diag(parse_async_move_block_in_2015)] +pub(crate) struct AsyncMoveBlockIn2015 { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(parse_self_argument_pointer)] pub(crate) struct SelfArgumentPointer { #[primary_span] @@ -1809,6 +1816,12 @@ pub struct UnknownPrefix<'a> { } #[derive(Subdiagnostic)] +#[note(parse_macro_expands_to_adt_field)] +pub struct MacroExpandsToAdtField<'a> { + pub adt_ty: &'a str, +} + +#[derive(Subdiagnostic)] pub enum UnknownPrefixSugg { #[suggestion( parse_suggestion_br, @@ -2731,3 +2744,17 @@ pub(crate) struct WhereClauseBeforeConstBodySugg { #[suggestion_part(code = "")] pub right: Span, } + +#[derive(Diagnostic)] +#[diag(parse_generic_args_in_pat_require_turbofish_syntax)] +pub(crate) struct GenericArgsInPatRequireTurbofishSyntax { + #[primary_span] + pub span: Span, + #[suggestion( + parse_sugg_turbofish_syntax, + style = "verbose", + code = "::", + applicability = "maybe-incorrect" + )] + pub suggest_turbofish: Span, +} diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 6712db26693..892be36aae7 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -8,6 +8,7 @@ #![feature(never_type)] #![feature(rustc_attrs)] #![recursion_limit = "256"] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate tracing; diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index ee0abba1c17..104de47b97d 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -36,7 +36,7 @@ impl<'a> Parser<'a> { pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, AttrWrapper> { let mut outer_attrs = ast::AttrVec::new(); let mut just_parsed_doc_comment = false; - let start_pos = self.token_cursor.num_next_calls; + let start_pos = self.num_bump_calls; loop { let attr = if self.check(&token::Pound) { let prev_outer_attr_sp = outer_attrs.last().map(|attr| attr.span); @@ -277,7 +277,7 @@ impl<'a> Parser<'a> { pub(crate) fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> { let mut attrs = ast::AttrVec::new(); loop { - let start_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap(); + let start_pos: u32 = self.num_bump_calls.try_into().unwrap(); // Only try to parse if it is an inner attribute (has `!`). let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) { Some(self.parse_attribute(InnerAttrPolicy::Permitted)?) @@ -298,7 +298,7 @@ impl<'a> Parser<'a> { None }; if let Some(attr) = attr { - let end_pos: u32 = self.token_cursor.num_next_calls.try_into().unwrap(); + let end_pos: u32 = self.num_bump_calls.try_into().unwrap(); // If we are currently capturing tokens, mark the location of this inner attribute. // If capturing ends up creating a `LazyAttrTokenStream`, we will include // this replace range with it, removing the inner attribute from the final diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 158ab2a2956..5d6c574baa6 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -213,6 +213,7 @@ impl<'a> Parser<'a> { let start_token = (self.token.clone(), self.token_spacing); let cursor_snapshot = self.token_cursor.clone(); + let start_pos = self.num_bump_calls; let has_outer_attrs = !attrs.attrs.is_empty(); let prev_capturing = std::mem::replace(&mut self.capture_state.capturing, Capturing::Yes); @@ -273,8 +274,7 @@ impl<'a> Parser<'a> { let replace_ranges_end = self.capture_state.replace_ranges.len(); - let cursor_snapshot_next_calls = cursor_snapshot.num_next_calls; - let mut end_pos = self.token_cursor.num_next_calls; + let mut end_pos = self.num_bump_calls; let mut captured_trailing = false; @@ -301,12 +301,12 @@ impl<'a> Parser<'a> { // then extend the range of captured tokens to include it, since the parser // was not actually bumped past it. When the `LazyAttrTokenStream` gets converted // into an `AttrTokenStream`, we will create the proper token. - if self.token_cursor.break_last_token { + if self.break_last_token { assert!(!captured_trailing, "Cannot set break_last_token and have trailing token"); end_pos += 1; } - let num_calls = end_pos - cursor_snapshot_next_calls; + let num_calls = end_pos - start_pos; // If we have no attributes, then we will never need to // use any replace ranges. @@ -316,7 +316,7 @@ impl<'a> Parser<'a> { // Grab any replace ranges that occur *inside* the current AST node. // We will perform the actual replacement when we convert the `LazyAttrTokenStream` // to an `AttrTokenStream`. - let start_calls: u32 = cursor_snapshot_next_calls.try_into().unwrap(); + let start_calls: u32 = start_pos.try_into().unwrap(); self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end] .iter() .cloned() @@ -331,7 +331,7 @@ impl<'a> Parser<'a> { start_token, num_calls, cursor_snapshot, - break_last_token: self.token_cursor.break_last_token, + break_last_token: self.break_last_token, replace_ranges, }); @@ -359,14 +359,10 @@ impl<'a> Parser<'a> { // with a `FlatToken::AttrTarget`. If this AST node is inside an item // that has `#[derive]`, then this will allow us to cfg-expand this // AST node. - let start_pos = - if has_outer_attrs { attrs.start_pos } else { cursor_snapshot_next_calls }; + let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos }; let new_tokens = vec![(FlatToken::AttrTarget(attr_data), Spacing::Alone)]; - assert!( - !self.token_cursor.break_last_token, - "Should not have unglued last token with cfg attr" - ); + assert!(!self.break_last_token, "Should not have unglued last token with cfg attr"); let range: Range<u32> = (start_pos.try_into().unwrap())..(end_pos.try_into().unwrap()); self.capture_state.replace_ranges.push((range, new_tokens)); self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); @@ -464,6 +460,6 @@ mod size_asserts { use rustc_data_structures::static_assert_size; // tidy-alphabetical-start static_assert_size!(AttrWrapper, 16); - static_assert_size!(LazyAttrTokenStreamImpl, 120); + static_assert_size!(LazyAttrTokenStreamImpl, 104); // tidy-alphabetical-end } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index e6de51a673c..4e639a54cf7 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -4,10 +4,11 @@ use super::{ TokenExpectType, TokenType, }; use crate::errors::{ - AmbiguousPlus, AttributeOnParamType, BadQPathStage2, BadTypePlus, BadTypePlusSub, ColonAsSemi, - ComparisonOperatorsCannotBeChained, ComparisonOperatorsCannotBeChainedSugg, - ConstGenericWithoutBraces, ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything, - DocCommentOnParamType, DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, + AmbiguousPlus, AsyncMoveBlockIn2015, AttributeOnParamType, BadQPathStage2, BadTypePlus, + BadTypePlusSub, ColonAsSemi, ComparisonOperatorsCannotBeChained, + ComparisonOperatorsCannotBeChainedSugg, ConstGenericWithoutBraces, + ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything, DocCommentOnParamType, + DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg, GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg, HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg, @@ -573,6 +574,12 @@ impl<'a> Parser<'a> { return Err(self.sess.create_err(UseEqInstead { span: self.token.span })); } + if self.token.is_keyword(kw::Move) && self.prev_token.is_keyword(kw::Async) { + // The 2015 edition is in use because parsing of `async move` has failed. + let span = self.prev_token.span.to(self.token.span); + return Err(self.sess.create_err(AsyncMoveBlockIn2015 { span })); + } + let expect = tokens_to_string(&expected); let actual = super::token_descr(&self.token); let (msg_exp, (label_sp, label_exp)) = if expected.len() > 1 { @@ -2100,7 +2107,7 @@ impl<'a> Parser<'a> { } pub(super) fn recover_arg_parse(&mut self) -> PResult<'a, (P<ast::Pat>, P<ast::Ty>)> { - let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName))?; + let pat = self.parse_pat_no_top_alt(Some(Expected::ArgumentName), None)?; self.expect(&token::Colon)?; let ty = self.parse_ty()?; @@ -2508,7 +2515,7 @@ impl<'a> Parser<'a> { // Skip the `:`. snapshot_pat.bump(); snapshot_type.bump(); - match snapshot_pat.parse_pat_no_top_alt(expected) { + match snapshot_pat.parse_pat_no_top_alt(expected, None) { Err(inner_err) => { inner_err.cancel(); } @@ -2634,6 +2641,7 @@ impl<'a> Parser<'a> { pub(crate) fn maybe_recover_unexpected_comma( &mut self, lo: Span, + is_mac_invoc: bool, rt: CommaRecoveryMode, ) -> PResult<'a, ()> { if self.token != token::Comma { @@ -2654,24 +2662,28 @@ impl<'a> Parser<'a> { let seq_span = lo.to(self.prev_token.span); let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern"); if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { - err.multipart_suggestion( - format!( - "try adding parentheses to match on a tuple{}", - if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." }, - ), - vec![ - (seq_span.shrink_to_lo(), "(".to_string()), - (seq_span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - if let CommaRecoveryMode::EitherTupleOrPipe = rt { - err.span_suggestion( - seq_span, - "...or a vertical bar to match on multiple alternatives", - seq_snippet.replace(',', " |"), + if is_mac_invoc { + err.note(fluent::parse_macro_expands_to_match_arm); + } else { + err.multipart_suggestion( + format!( + "try adding parentheses to match on a tuple{}", + if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." }, + ), + vec![ + (seq_span.shrink_to_lo(), "(".to_string()), + (seq_span.shrink_to_hi(), ")".to_string()), + ], Applicability::MachineApplicable, ); + if let CommaRecoveryMode::EitherTupleOrPipe = rt { + err.span_suggestion( + seq_span, + "...or a vertical bar to match on multiple alternatives", + seq_snippet.replace(',', " |"), + Applicability::MachineApplicable, + ); + } } } Err(err) @@ -2772,7 +2784,7 @@ impl<'a> Parser<'a> { /// sequence of patterns until `)` is reached. fn skip_pat_list(&mut self) -> PResult<'a, ()> { while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) { - self.parse_pat_no_top_alt(None)?; + self.parse_pat_no_top_alt(None, None)?; if !self.eat(&token::Comma) { return Ok(()); } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 55f857aa31c..e409c7c6781 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -857,7 +857,7 @@ impl<'a> Parser<'a> { let msg = format!( "cast cannot be followed by {}", match with_postfix.kind { - ExprKind::Index(_, _) => "indexing", + ExprKind::Index(..) => "indexing", ExprKind::Try(_) => "`?`", ExprKind::Field(_, _) => "a field access", ExprKind::MethodCall(_) => "a method call", @@ -1167,7 +1167,7 @@ impl<'a> Parser<'a> { DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => { assert!(suffix.is_none()); // Analogous to `Self::break_and_eat` - self.token_cursor.break_last_token = true; + self.break_last_token = true; // This might work, in cases like `1. 2`, and might not, // in cases like `offset_of!(Ty, 1.)`. It depends on what comes // after the float-like token, and therefore we have to make @@ -1304,7 +1304,10 @@ impl<'a> Parser<'a> { let index = self.parse_expr()?; self.suggest_missing_semicolon_before_array(prev_span, open_delim_span)?; self.expect(&token::CloseDelim(Delimiter::Bracket))?; - Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index))) + Ok(self.mk_expr( + lo.to(self.prev_token.span), + self.mk_index(base, index, open_delim_span.to(self.prev_token.span)), + )) } /// Assuming we have just parsed `.`, continue parsing into an expression. @@ -2338,7 +2341,7 @@ impl<'a> Parser<'a> { let lo = self.token.span; let attrs = self.parse_outer_attributes()?; self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { - let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName))?; + let pat = this.parse_pat_no_top_alt(Some(Expected::ParameterName), None)?; let ty = if this.eat(&token::Colon) { this.parse_ty()? } else { @@ -2599,7 +2602,7 @@ impl<'a> Parser<'a> { // Recover from missing expression in `for` loop if matches!(expr.kind, ExprKind::Block(..)) - && !matches!(self.token.kind, token::OpenDelim(token::Delimiter::Brace)) + && !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace)) && self.may_recover() { self.sess @@ -2781,7 +2784,7 @@ impl<'a> Parser<'a> { return None; } let pre_pat_snapshot = self.create_snapshot_for_diagnostic(); - match self.parse_pat_no_top_alt(None) { + match self.parse_pat_no_top_alt(None, None) { Ok(_pat) => { if self.token.kind == token::FatArrow { // Reached arm end. @@ -3366,8 +3369,8 @@ impl<'a> Parser<'a> { ExprKind::Binary(binop, lhs, rhs) } - fn mk_index(&self, expr: P<Expr>, idx: P<Expr>) -> ExprKind { - ExprKind::Index(expr, idx) + fn mk_index(&self, expr: P<Expr>, idx: P<Expr>, brackets_span: Span) -> ExprKind { + ExprKind::Index(expr, idx, brackets_span) } fn mk_call(&self, f: P<Expr>, args: ThinVec<P<Expr>>) -> ExprKind { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1301ed3e388..24c65d061f9 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1,20 +1,20 @@ -use crate::errors; - use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; +use crate::errors::{self, MacroExpandsToAdtField}; +use crate::fluent_generated as fluent; use ast::StaticItem; use rustc_ast::ast::*; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::util::case::Case; +use rustc_ast::MacCall; use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind}; use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, VariantData}; use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind}; -use rustc_ast::{MacCall, MacDelimiter}; use rustc_ast_pretty::pprust; use rustc_errors::{ struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult, @@ -1450,6 +1450,17 @@ impl<'a> Parser<'a> { } let ident = this.parse_field_ident("enum", vlo)?; + if this.token == token::Not { + if let Err(mut err) = this.unexpected::<()>() { + err.note(fluent::parse_macro_expands_to_enum_variant).emit(); + } + + this.bump(); + this.parse_delim_args()?; + + return Ok((None, TrailingToken::MaybeComma)); + } + let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) { // Parse a struct variant. let (fields, recovered) = @@ -1477,7 +1488,7 @@ impl<'a> Parser<'a> { Ok((Some(vr), TrailingToken::MaybeComma)) }, - ).map_err(|mut err|{ + ).map_err(|mut err| { err.help("enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`"); err }) @@ -1687,7 +1698,8 @@ impl<'a> Parser<'a> { self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; let vis = this.parse_visibility(FollowedByType::No)?; - Ok((this.parse_single_struct_field(adt_ty, lo, vis, attrs)?, TrailingToken::None)) + this.parse_single_struct_field(adt_ty, lo, vis, attrs) + .map(|field| (field, TrailingToken::None)) }) } @@ -1821,8 +1833,8 @@ impl<'a> Parser<'a> { "field names and their types are separated with `:`", ":", Applicability::MachineApplicable, - ); - err.emit(); + ) + .emit(); } else { return Err(err); } @@ -1839,6 +1851,23 @@ impl<'a> Parser<'a> { attrs: AttrVec, ) -> PResult<'a, FieldDef> { let name = self.parse_field_ident(adt_ty, lo)?; + // Parse the macro invocation and recover + if self.token.kind == token::Not { + if let Err(mut err) = self.unexpected::<FieldDef>() { + err.subdiagnostic(MacroExpandsToAdtField { adt_ty }).emit(); + self.bump(); + self.parse_delim_args()?; + return Ok(FieldDef { + span: DUMMY_SP, + ident: None, + vis, + id: DUMMY_NODE_ID, + ty: self.mk_ty(DUMMY_SP, TyKind::Err), + attrs, + is_placeholder: false, + }); + } + } self.expect_field_ty_separator()?; let ty = self.parse_ty()?; if self.token.kind == token::Colon && self.look_ahead(1, |tok| tok.kind != token::Colon) { @@ -1968,7 +1997,7 @@ impl<'a> Parser<'a> { let arrow = TokenTree::token_alone(token::FatArrow, pspan.between(bspan)); // `=>` let tokens = TokenStream::new(vec![params, arrow, body]); let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi()); - P(DelimArgs { dspan, delim: MacDelimiter::Brace, tokens }) + P(DelimArgs { dspan, delim: Delimiter::Brace, tokens }) } else { return self.unexpected(); }; diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 57778d67098..c5b46b809b1 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -25,7 +25,7 @@ use rustc_ast::util::case::Case; use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern}; -use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, MacDelimiter, Mutability, StrLit}; +use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit}; use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -135,9 +135,24 @@ pub struct Parser<'a> { pub capture_cfg: bool, restrictions: Restrictions, expected_tokens: Vec<TokenType>, - // Important: This must only be advanced from `bump` to ensure that - // `token_cursor.num_next_calls` is updated properly. token_cursor: TokenCursor, + // The number of calls to `bump`, i.e. the position in the token stream. + num_bump_calls: usize, + // During parsing we may sometimes need to 'unglue' a glued token into two + // component tokens (e.g. '>>' into '>' and '>), so the parser can consume + // them one at a time. This process bypasses the normal capturing mechanism + // (e.g. `num_bump_calls` will not be incremented), since the 'unglued' + // tokens due not exist in the original `TokenStream`. + // + // If we end up consuming both unglued tokens, this is not an issue. We'll + // end up capturing the single 'glued' token. + // + // However, sometimes we may want to capture just the first 'unglued' + // token. For example, capturing the `Vec<u8>` in `Option<Vec<u8>>` + // requires us to unglue the trailing `>>` token. The `break_last_token` + // field is used to track this token. It gets appended to the captured + // stream when we evaluate a `LazyAttrTokenStream`. + break_last_token: bool, /// This field is used to keep track of how many left angle brackets we have seen. This is /// required in order to detect extra leading left angle brackets (`<` characters) and error /// appropriately. @@ -161,7 +176,7 @@ pub struct Parser<'a> { // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure // it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Parser<'_>, 272); +rustc_data_structures::static_assert_size!(Parser<'_>, 264); /// Stores span information about a closure. #[derive(Clone)] @@ -223,32 +238,6 @@ struct TokenCursor { // tokens are in `stack[n-1]`. `stack[0]` (when present) has no delimiters // because it's the outermost token stream which never has delimiters. stack: Vec<(TokenTreeCursor, Delimiter, DelimSpan)>, - - // Counts the number of calls to `{,inlined_}next`. - num_next_calls: usize, - - // During parsing, we may sometimes need to 'unglue' a - // glued token into two component tokens - // (e.g. '>>' into '>' and '>), so that the parser - // can consume them one at a time. This process - // bypasses the normal capturing mechanism - // (e.g. `num_next_calls` will not be incremented), - // since the 'unglued' tokens due not exist in - // the original `TokenStream`. - // - // If we end up consuming both unglued tokens, - // then this is not an issue - we'll end up - // capturing the single 'glued' token. - // - // However, in certain circumstances, we may - // want to capture just the first 'unglued' token. - // For example, capturing the `Vec<u8>` - // in `Option<Vec<u8>>` requires us to unglue - // the trailing `>>` token. The `break_last_token` - // field is used to track this token - it gets - // appended to the captured stream when - // we evaluate a `LazyAttrTokenStream`. - break_last_token: bool, } impl TokenCursor { @@ -399,12 +388,9 @@ impl<'a> Parser<'a> { capture_cfg: false, restrictions: Restrictions::empty(), expected_tokens: Vec::new(), - token_cursor: TokenCursor { - tree_cursor: stream.into_trees(), - stack: Vec::new(), - num_next_calls: 0, - break_last_token: false, - }, + token_cursor: TokenCursor { tree_cursor: stream.into_trees(), stack: Vec::new() }, + num_bump_calls: 0, + break_last_token: false, unmatched_angle_bracket_count: 0, max_angle_bracket_count: 0, last_unexpected_token_span: None, @@ -707,7 +693,7 @@ impl<'a> Parser<'a> { // If we consume any additional tokens, then this token // is not needed (we'll capture the entire 'glued' token), // and `bump` will set this field to `None` - self.token_cursor.break_last_token = true; + self.break_last_token = true; // Use the spacing of the glued token as the spacing // of the unglued second token. self.bump_with((Token::new(second, second_span), self.token_spacing)); @@ -1049,11 +1035,11 @@ impl<'a> Parser<'a> { // Note: destructuring here would give nicer code, but it was found in #96210 to be slower // than `.0`/`.1` access. let mut next = self.token_cursor.inlined_next(); - self.token_cursor.num_next_calls += 1; + self.num_bump_calls += 1; // We've retrieved an token from the underlying // cursor, so we no longer need to worry about // an unglued token. See `break_and_eat` for more details - self.token_cursor.break_last_token = false; + self.break_last_token = false; if next.0.span.is_dummy() { // Tweak the location for better diagnostics, but keep syntactic context intact. let fallback_span = self.token.span; @@ -1230,12 +1216,10 @@ impl<'a> Parser<'a> { || self.check(&token::OpenDelim(Delimiter::Brace)); delimited.then(|| { - // We've confirmed above that there is a delimiter so unwrapping is OK. let TokenTree::Delimited(dspan, delim, tokens) = self.parse_token_tree() else { unreachable!() }; - - DelimArgs { dspan, delim: MacDelimiter::from_token(delim).unwrap(), tokens } + DelimArgs { dspan, delim, tokens } }) } @@ -1251,12 +1235,11 @@ impl<'a> Parser<'a> { } /// Parses a single token tree from the input. - pub(crate) fn parse_token_tree(&mut self) -> TokenTree { + pub fn parse_token_tree(&mut self) -> TokenTree { match self.token.kind { token::OpenDelim(..) => { // Grab the tokens within the delimiters. - let tree_cursor = &self.token_cursor.tree_cursor; - let stream = tree_cursor.stream.clone(); + let stream = self.token_cursor.tree_cursor.stream.clone(); let (_, delim, span) = *self.token_cursor.stack.last().unwrap(); // Advance the token cursor through the entire delimited @@ -1287,15 +1270,6 @@ impl<'a> Parser<'a> { } } - /// Parses a stream of tokens into a list of `TokenTree`s, up to EOF. - pub fn parse_all_token_trees(&mut self) -> PResult<'a, Vec<TokenTree>> { - let mut tts = Vec::new(); - while self.token != token::Eof { - tts.push(self.parse_token_tree()); - } - Ok(tts) - } - pub fn parse_tokens(&mut self) -> TokenStream { let mut result = Vec::new(); loop { @@ -1455,7 +1429,7 @@ impl<'a> Parser<'a> { } pub fn approx_token_stream_pos(&self) -> usize { - self.token_cursor.num_next_calls + self.num_bump_calls } } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index adb0d372a40..f5681532b3a 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -131,7 +131,7 @@ impl<'a> Parser<'a> { }, NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => { token::NtPat(self.collect_tokens_no_attrs(|this| match kind { - NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None), + NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None), NonterminalKind::PatWithOr { .. } => this.parse_pat_allow_top_alt( None, RecoverComma::No, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index fed16278db5..4aadb7d7ca5 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -2,10 +2,11 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken}; use crate::errors::{ self, AmbiguousRangePattern, DotDotDotForRemainingFields, DotDotDotRangeToPatternNotAllowed, DotDotDotRestPattern, EnumPatternInsteadOfIdentifier, ExpectedBindingLeftOfAt, - ExpectedCommaAfterPatternField, InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, - InclusiveRangeNoEnd, InvalidMutInPattern, PatternOnWrongSideOfAt, RefMutOrderIncorrect, - RemoveLet, RepeatedMutInPattern, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, - TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, + ExpectedCommaAfterPatternField, GenericArgsInPatRequireTurbofishSyntax, + InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, + PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern, + TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, TrailingVertNotAllowed, + UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, }; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; @@ -80,7 +81,8 @@ enum EatOrResult { } /// The syntax location of a given pattern. Used for diagnostics. -pub(super) enum PatternLocation { +#[derive(Clone, Copy)] +pub enum PatternLocation { LetBinding, FunctionParameter, } @@ -91,8 +93,12 @@ impl<'a> Parser<'a> { /// Corresponds to `pat<no_top_alt>` in RFC 2535 and does not admit or-patterns /// at the top level. Used when parsing the parameters of lambda expressions, /// functions, function pointers, and `pat` macro fragments. - pub fn parse_pat_no_top_alt(&mut self, expected: Option<Expected>) -> PResult<'a, P<Pat>> { - self.parse_pat_with_range_pat(true, expected) + pub fn parse_pat_no_top_alt( + &mut self, + expected: Option<Expected>, + syntax_loc: Option<PatternLocation>, + ) -> PResult<'a, P<Pat>> { + self.parse_pat_with_range_pat(true, expected, syntax_loc) } /// Parses a pattern. @@ -110,7 +116,7 @@ impl<'a> Parser<'a> { ra: RecoverColon, rt: CommaRecoveryMode, ) -> PResult<'a, P<Pat>> { - self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt).map(|(pat, _)| pat) + self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) } /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = @@ -121,6 +127,7 @@ impl<'a> Parser<'a> { rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, + syntax_loc: Option<PatternLocation>, ) -> PResult<'a, (P<Pat>, bool)> { // Keep track of whether we recovered from a trailing vert so that we can avoid duplicated // suggestions (which bothers rustfix). @@ -133,9 +140,13 @@ impl<'a> Parser<'a> { }; // Parse the first pattern (`p_0`). - let mut first_pat = self.parse_pat_no_top_alt(expected)?; + let mut first_pat = self.parse_pat_no_top_alt(expected, syntax_loc)?; if rc == RecoverComma::Yes { - self.maybe_recover_unexpected_comma(first_pat.span, rt)?; + self.maybe_recover_unexpected_comma( + first_pat.span, + matches!(first_pat.kind, PatKind::MacCall(_)), + rt, + )?; } // If the next token is not a `|`, @@ -172,12 +183,12 @@ impl<'a> Parser<'a> { break; } } - let pat = self.parse_pat_no_top_alt(expected).map_err(|mut err| { + let pat = self.parse_pat_no_top_alt(expected, syntax_loc).map_err(|mut err| { err.span_label(lo, WHILE_PARSING_OR_MSG); err })?; if rc == RecoverComma::Yes { - self.maybe_recover_unexpected_comma(pat.span, rt)?; + self.maybe_recover_unexpected_comma(pat.span, false, rt)?; } pats.push(pat); } @@ -208,6 +219,7 @@ impl<'a> Parser<'a> { rc, RecoverColon::No, CommaRecoveryMode::LikelyTuple, + Some(syntax_loc), )?; let colon = self.eat(&token::Colon); @@ -319,6 +331,7 @@ impl<'a> Parser<'a> { &mut self, allow_range_pat: bool, expected: Option<Expected>, + syntax_loc: Option<PatternLocation>, ) -> PResult<'a, P<Pat>> { maybe_recover_from_interpolated_ty_qpath!(self, true); maybe_whole!(self, NtPat, |x| x); @@ -358,11 +371,11 @@ impl<'a> Parser<'a> { // Parse _ PatKind::Wild } else if self.eat_keyword(kw::Mut) { - self.parse_pat_ident_mut()? + self.parse_pat_ident_mut(syntax_loc)? } else if self.eat_keyword(kw::Ref) { // Parse ref ident @ pat / ref mut ident @ pat let mutbl = self.parse_mutability(); - self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl))? + self.parse_pat_ident(BindingAnnotation(ByRef::Yes, mutbl), syntax_loc)? } else if self.eat_keyword(kw::Box) { self.parse_pat_box()? } else if self.check_inline_const(0) { @@ -384,7 +397,7 @@ impl<'a> Parser<'a> { // Parse `ident @ pat` // This can give false positives and parse nullary enums, // they are dealt with later in resolve. - self.parse_pat_ident(BindingAnnotation::NONE)? + self.parse_pat_ident(BindingAnnotation::NONE, syntax_loc)? } else if self.is_start_of_pat_with_path() { // Parse pattern starting with a path let (qself, path) = if self.eat_lt() { @@ -485,7 +498,7 @@ impl<'a> Parser<'a> { // At this point we attempt to parse `@ $pat_rhs` and emit an error. self.bump(); // `@` - let mut rhs = self.parse_pat_no_top_alt(None)?; + let mut rhs = self.parse_pat_no_top_alt(None, None)?; let whole_span = lhs.span.to(rhs.span); if let PatKind::Ident(_, _, sub @ None) = &mut rhs.kind { @@ -541,7 +554,7 @@ impl<'a> Parser<'a> { } let mutbl = self.parse_mutability(); - let subpat = self.parse_pat_with_range_pat(false, expected)?; + let subpat = self.parse_pat_with_range_pat(false, expected, None)?; Ok(PatKind::Ref(subpat, mutbl)) } @@ -566,12 +579,12 @@ impl<'a> Parser<'a> { } /// Parse a mutable binding with the `mut` token already eaten. - fn parse_pat_ident_mut(&mut self) -> PResult<'a, PatKind> { + fn parse_pat_ident_mut(&mut self, syntax_loc: Option<PatternLocation>) -> PResult<'a, PatKind> { let mut_span = self.prev_token.span; if self.eat_keyword(kw::Ref) { self.sess.emit_err(RefMutOrderIncorrect { span: mut_span.to(self.prev_token.span) }); - return self.parse_pat_ident(BindingAnnotation::REF_MUT); + return self.parse_pat_ident(BindingAnnotation::REF_MUT, syntax_loc); } self.recover_additional_muts(); @@ -584,7 +597,7 @@ impl<'a> Parser<'a> { } // Parse the pattern we hope to be an identifier. - let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier))?; + let mut pat = self.parse_pat_no_top_alt(Some(Expected::Identifier), None)?; // If we don't have `mut $ident (@ pat)?`, error. if let PatKind::Ident(BindingAnnotation(ByRef::No, m @ Mutability::Not), ..) = &mut pat.kind @@ -810,10 +823,25 @@ impl<'a> Parser<'a> { /// Parses `ident` or `ident @ pat`. /// Used by the copy foo and ref foo patterns to give a good /// error message when parsing mistakes like `ref foo(a, b)`. - fn parse_pat_ident(&mut self, binding_annotation: BindingAnnotation) -> PResult<'a, PatKind> { + fn parse_pat_ident( + &mut self, + binding_annotation: BindingAnnotation, + syntax_loc: Option<PatternLocation>, + ) -> PResult<'a, PatKind> { let ident = self.parse_ident()?; + + if !matches!(syntax_loc, Some(PatternLocation::FunctionParameter)) + && self.check_noexpect(&token::Lt) + && self.look_ahead(1, |t| t.can_begin_type()) + { + return Err(self.sess.create_err(GenericArgsInPatRequireTurbofishSyntax { + span: self.token.span, + suggest_turbofish: self.token.span.shrink_to_lo(), + })); + } + let sub = if self.eat(&token::At) { - Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?) + Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) } else { None }; @@ -902,14 +930,14 @@ impl<'a> Parser<'a> { // We cannot use `parse_pat_ident()` since it will complain `box` // is not an identifier. let sub = if self.eat(&token::At) { - Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern))?) + Some(self.parse_pat_no_top_alt(Some(Expected::BindingPattern), None)?) } else { None }; Ok(PatKind::Ident(BindingAnnotation::NONE, Ident::new(kw::Box, box_span), sub)) } else { - let pat = self.parse_pat_with_range_pat(false, None)?; + let pat = self.parse_pat_with_range_pat(false, None, None)?; self.sess.gated_spans.gate(sym::box_patterns, box_span.to(self.prev_token.span)); Ok(PatKind::Box(pat)) } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 1cdf2efa764..12c267351b9 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -193,10 +193,9 @@ impl<'a> Parser<'a> { /// At this point, the `!` token after the path has already been eaten. fn parse_stmt_mac(&mut self, lo: Span, attrs: AttrVec, path: ast::Path) -> PResult<'a, Stmt> { let args = self.parse_delim_args()?; - let delim = args.delim.to_token(); let hi = self.prev_token.span; - let style = match delim { + let style = match args.delim { Delimiter::Brace => MacStmtStyle::Braces, _ => MacStmtStyle::NoBraces, }; diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 2011083019c..f7396598220 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -2,9 +2,10 @@ use crate::{errors, parse_in}; +use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::MetaItemKind; -use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MacDelimiter, MetaItem}; +use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem}; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, FatalError, PResult}; use rustc_feature::{AttributeTemplate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; @@ -84,8 +85,8 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta }) } -pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter) { - if let ast::MacDelimiter::Parenthesis = delim { +pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: Delimiter) { + if let Delimiter::Parenthesis = delim { return; } sess.emit_err(errors::MetaBadDelim { @@ -94,8 +95,8 @@ pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimit }); } -pub fn check_cfg_attr_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter) { - if let ast::MacDelimiter::Parenthesis = delim { +pub fn check_cfg_attr_bad_delim(sess: &ParseSess, span: DelimSpan, delim: Delimiter) { + if let Delimiter::Parenthesis = delim { return; } sess.emit_err(errors::CfgAttrBadDelim { diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 15757a0f1ad..0c0b8b6d094 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1061,7 +1061,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&l, ln) } - hir::ExprKind::Index(ref l, ref r) | hir::ExprKind::Binary(_, ref l, ref r) => { + hir::ExprKind::Index(ref l, ref r, _) | hir::ExprKind::Binary(_, ref l, ref r) => { let r_succ = self.propagate_through_expr(&r, succ); self.propagate_through_expr(&l, r_succ) } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 5fa780a372b..af9efb82beb 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -147,7 +147,7 @@ where fn visit_clause(&mut self, clause: ty::Clause<'tcx>) -> ControlFlow<V::BreakTy> { match clause.kind().skip_binder() { - ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity: _ }) => { + ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity: _ }) => { self.visit_trait(trait_ref) } ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => { diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 4cf0f1305a7..53005ede843 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -11,6 +11,7 @@ #![allow(rustc::potential_query_instability, unused_parens)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate rustc_middle; diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index e02cf38b671..584355df802 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [lib] [dependencies] -parking_lot = "0.11" +parking_lot = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index b87757a3e1a..3803f7eca0a 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -1,4 +1,3 @@ -use parking_lot::Mutex; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::profiling::{EventId, QueryInvocationId, SelfProfilerRef}; @@ -88,7 +87,7 @@ pub struct DepGraphData<K: DepKind> { colors: DepNodeColorMap, - processed_side_effects: Mutex<FxHashSet<DepNodeIndex>>, + processed_side_effects: Lock<FxHashSet<DepNodeIndex>>, /// When we load, there may be `.o` files, cached MIR, or other such /// things available to us. If we find that they are not dirty, we diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index d2140161f1d..bfc51da170d 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -21,12 +21,11 @@ use { parking_lot::{Condvar, Mutex}, rayon_core, rustc_data_structures::fx::FxHashSet, - rustc_data_structures::sync::Lock, - rustc_data_structures::sync::Lrc, rustc_data_structures::{defer, jobserver}, rustc_span::DUMMY_SP, std::iter, std::process, + std::sync::Arc, }; /// Represents a span and a query key. @@ -191,7 +190,7 @@ struct QueryWaiter<D: DepKind> { query: Option<QueryJobId>, condvar: Condvar, span: Span, - cycle: Lock<Option<CycleError<D>>>, + cycle: Mutex<Option<CycleError<D>>>, } #[cfg(parallel_compiler)] @@ -205,20 +204,20 @@ impl<D: DepKind> QueryWaiter<D> { #[cfg(parallel_compiler)] struct QueryLatchInfo<D: DepKind> { complete: bool, - waiters: Vec<Lrc<QueryWaiter<D>>>, + waiters: Vec<Arc<QueryWaiter<D>>>, } #[cfg(parallel_compiler)] #[derive(Clone)] pub(super) struct QueryLatch<D: DepKind> { - info: Lrc<Mutex<QueryLatchInfo<D>>>, + info: Arc<Mutex<QueryLatchInfo<D>>>, } #[cfg(parallel_compiler)] impl<D: DepKind> QueryLatch<D> { fn new() -> Self { QueryLatch { - info: Lrc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })), + info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })), } } @@ -229,11 +228,11 @@ impl<D: DepKind> QueryLatch<D> { span: Span, ) -> Result<(), CycleError<D>> { let waiter = - Lrc::new(QueryWaiter { query, span, cycle: Lock::new(None), condvar: Condvar::new() }); + Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() }); self.wait_on_inner(&waiter); // FIXME: Get rid of this lock. We have ownership of the QueryWaiter - // although another thread may still have a Lrc reference so we cannot - // use Lrc::get_mut + // although another thread may still have a Arc reference so we cannot + // use Arc::get_mut let mut cycle = waiter.cycle.lock(); match cycle.take() { None => Ok(()), @@ -242,7 +241,7 @@ impl<D: DepKind> QueryLatch<D> { } /// Awaits the caller on this latch by blocking the current thread. - fn wait_on_inner(&self, waiter: &Lrc<QueryWaiter<D>>) { + fn wait_on_inner(&self, waiter: &Arc<QueryWaiter<D>>) { let mut info = self.info.lock(); if !info.complete { // We push the waiter on to the `waiters` list. It can be accessed inside @@ -276,7 +275,7 @@ impl<D: DepKind> QueryLatch<D> { /// Removes a single waiter from the list of waiters. /// This is used to break query cycles. - fn extract_waiter(&self, waiter: usize) -> Lrc<QueryWaiter<D>> { + fn extract_waiter(&self, waiter: usize) -> Arc<QueryWaiter<D>> { let mut info = self.info.lock(); debug_assert!(!info.complete); // Remove the waiter from the list of waiters @@ -428,7 +427,7 @@ where fn remove_cycle<D: DepKind>( query_map: &QueryMap<D>, jobs: &mut Vec<QueryJobId>, - wakelist: &mut Vec<Lrc<QueryWaiter<D>>>, + wakelist: &mut Vec<Arc<QueryWaiter<D>>>, ) -> bool { let mut visited = FxHashSet::default(); let mut stack = Vec::new(); diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2f432799022..80d6a47ff53 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -278,7 +278,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { }; match self.r.resolve_path( &segments, - Some(TypeNS), + None, parent_scope, finalize.then(|| Finalize::new(id, path.span)), None, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 0e9d74480a9..65fa077dcd9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -904,9 +904,12 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), &sig.decl.output, ); + + if let Some((async_node_id, span)) = sig.header.asyncness.opt_return_id() { + this.record_lifetime_params_for_impl_trait(async_node_id, span); + } }, ); - self.record_lifetime_params_for_async(fn_id, sig.header.asyncness.opt_return_id()); return; } FnKind::Fn(..) => { @@ -942,12 +945,14 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, .iter() .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), &declaration.output, - ) + ); + + if let Some((async_node_id, span)) = async_node_id { + this.record_lifetime_params_for_impl_trait(async_node_id, span); + } }, ); - this.record_lifetime_params_for_async(fn_id, async_node_id); - if let Some(body) = body { // Ignore errors in function bodies if this is rustdoc // Be sure not to set this until the function signature has been resolved. @@ -1694,6 +1699,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // Leave the responsibility to create the `LocalDefId` to lowering. let param = self.r.next_node_id(); let res = LifetimeRes::Fresh { param, binder }; + self.record_lifetime_param(param, res); // Record the created lifetime parameter so lowering can pick it up and add it to HIR. self.r @@ -3944,11 +3950,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { if path.len() > 1 && let Some(res) = result.full_res() + && let Some((&last_segment, prev_segs)) = path.split_last() + && prev_segs.iter().all(|seg| !seg.has_generic_args) && res != Res::Err && path[0].ident.name != kw::PathRoot && path[0].ident.name != kw::DollarCrate { - let last_segment = *path.last().unwrap(); let unqualified_result = { match self.resolve_path(&[last_segment], Some(ns), None) { PathResult::NonModule(path_res) => path_res.expect_full_res(), @@ -4269,7 +4276,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ExprKind::ConstBlock(ref ct) => { self.resolve_anon_const(ct, AnonConstKind::InlineConst); } - ExprKind::Index(ref elem, ref idx) => { + ExprKind::Index(ref elem, ref idx, _) => { self.resolve_expr(elem, Some(expr)); self.visit_expr(idx); } @@ -4325,39 +4332,32 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ) } - /// Construct the list of in-scope lifetime parameters for async lowering. + /// Construct the list of in-scope lifetime parameters for impl trait lowering. /// We include all lifetime parameters, either named or "Fresh". /// The order of those parameters does not matter, as long as it is /// deterministic. - fn record_lifetime_params_for_async( - &mut self, - fn_id: NodeId, - async_node_id: Option<(NodeId, Span)>, - ) { - if let Some((async_node_id, span)) = async_node_id { - let mut extra_lifetime_params = - self.r.extra_lifetime_params_map.get(&fn_id).cloned().unwrap_or_default(); - for rib in self.lifetime_ribs.iter().rev() { - extra_lifetime_params.extend( - rib.bindings.iter().map(|(&ident, &(node_id, res))| (ident, node_id, res)), - ); - match rib.kind { - LifetimeRibKind::Item => break, - LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { - if let Some(earlier_fresh) = self.r.extra_lifetime_params_map.get(&binder) { - extra_lifetime_params.extend(earlier_fresh); - } - } - LifetimeRibKind::Generics { .. } => {} - _ => { - // We are in a function definition. We should only find `Generics` - // and `AnonymousCreateParameter` inside the innermost `Item`. - span_bug!(span, "unexpected rib kind: {:?}", rib.kind) + fn record_lifetime_params_for_impl_trait(&mut self, impl_trait_node_id: NodeId, span: Span) { + let mut extra_lifetime_params = vec![]; + + for rib in self.lifetime_ribs.iter().rev() { + extra_lifetime_params + .extend(rib.bindings.iter().map(|(&ident, &(node_id, res))| (ident, node_id, res))); + match rib.kind { + LifetimeRibKind::Item => break, + LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { + if let Some(earlier_fresh) = self.r.extra_lifetime_params_map.get(&binder) { + extra_lifetime_params.extend(earlier_fresh); } } + LifetimeRibKind::Generics { .. } => {} + _ => { + // We are in a function definition. We should only find `Generics` + // and `AnonymousCreateParameter` inside the innermost `Item`. + span_bug!(span, "unexpected rib kind: {:?}", rib.kind) + } } - self.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params); } + self.r.extra_lifetime_params_map.insert(impl_trait_node_id, extra_lifetime_params); } fn resolve_and_cache_rustdoc_path(&mut self, path_str: &str, ns: Namespace) -> Option<Res> { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index da5e92a075a..e403386e60c 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -18,6 +18,7 @@ #![recursion_limit = "256"] #![allow(rustdoc::private_intra_doc_links)] #![allow(rustc::potential_query_instability)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate tracing; diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index d57aa820fcb..a270817f310 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -10,6 +10,7 @@ #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ad22e7c703d..ac745d16161 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1350,18 +1350,15 @@ fn default_emitter( ); Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } else { - let emitter = EmitterWriter::stderr( - color_config, - Some(source_map), - bundle, - fallback_bundle, - short, - sopts.unstable_opts.teach, - sopts.diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, - ); + let emitter = EmitterWriter::stderr(color_config, fallback_bundle) + .fluent_bundle(bundle) + .sm(Some(source_map)) + .short_message(short) + .teach(sopts.unstable_opts.teach) + .diagnostic_width(sopts.diagnostic_width) + .macro_backtrace(macro_backtrace) + .track_diagnostics(track_diagnostics) + .terminal_url(terminal_url); Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } } @@ -1794,18 +1791,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box<dyn Emitter + sync::Send + 'static let emitter: Box<dyn Emitter + sync::Send> = match output { config::ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); - Box::new(EmitterWriter::stderr( - color_config, - None, - None, - fallback_bundle, - short, - false, - None, - false, - false, - TerminalUrl::No, - )) + Box::new(EmitterWriter::stderr(color_config, fallback_bundle).short_message(short)) } config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::basic( pretty, diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index c4bdec0ee28..e377843cdb4 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -1063,3 +1063,34 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::BoundTy { BoundTy { var: self.var.as_usize(), kind: self.kind.stable(tables) } } } + +impl<'tcx> Stable<'tcx> for mir::interpret::Allocation { + type T = stable_mir::ty::Allocation; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + let size = self.size(); + let mut bytes: Vec<Option<u8>> = self + .inspect_with_uninit_and_ptr_outside_interpreter(0..size.bytes_usize()) + .iter() + .copied() + .map(Some) + .collect(); + for (i, b) in bytes.iter_mut().enumerate() { + if !self.init_mask().get(rustc_target::abi::Size::from_bytes(i)) { + *b = None; + } + } + stable_mir::ty::Allocation { + bytes: bytes, + provenance: { + let mut ptrs = Vec::new(); + for (size, prov) in self.provenance().ptrs().iter() { + ptrs.push((size.bytes_usize(), opaque(prov))); + } + stable_mir::ty::ProvenanceMap { ptrs } + }, + align: self.align.bytes(), + mutability: self.mutability.stable(tables), + } + } +} diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 025225b8d19..df28fc4eed9 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -242,3 +242,25 @@ pub struct BoundTy { pub var: usize, pub kind: BoundTyKind, } + +pub type Bytes = Vec<Option<u8>>; +pub type Size = usize; +pub type Prov = Opaque; +pub type Align = u64; +pub type InitMaskMaterialized = Vec<u64>; + +/// Stores the provenance information of pointers stored in memory. +#[derive(Clone, Debug)] +pub struct ProvenanceMap { + /// Provenance in this map applies from the given offset for an entire pointer-size worth of + /// bytes. Two entries in this map are always at least a pointer size apart. + pub ptrs: Vec<(Size, Prov)>, +} + +#[derive(Clone, Debug)] +pub struct Allocation { + pub bytes: Bytes, + pub provenance: ProvenanceMap, + pub align: Align, + pub mutability: Mutability, +} diff --git a/compiler/rustc_span/src/edit_distance.rs b/compiler/rustc_span/src/edit_distance.rs index 259f4238654..96a118e590f 100644 --- a/compiler/rustc_span/src/edit_distance.rs +++ b/compiler/rustc_span/src/edit_distance.rs @@ -238,8 +238,9 @@ fn find_best_match_for_name_impl( } fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option<Symbol> { + let lookup_sorted_by_words = sort_by_words(lookup); iter_names.iter().fold(None, |result, candidate| { - if sort_by_words(candidate.as_str()) == sort_by_words(lookup) { + if sort_by_words(candidate.as_str()) == lookup_sorted_by_words { Some(*candidate) } else { result @@ -247,9 +248,9 @@ fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option<Sym }) } -fn sort_by_words(name: &str) -> String { +fn sort_by_words(name: &str) -> Vec<&str> { let mut split_words: Vec<&str> = name.split('_').collect(); // We are sorting primitive &strs and can use unstable sort here. split_words.sort_unstable(); - split_words.join("_") + split_words } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index fbd9bf05528..afee5c0fe2b 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -23,6 +23,7 @@ #![feature(round_char_boundary)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate rustc_macros; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d3739733c1d..9ea9efb047c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -400,6 +400,7 @@ symbols! { async_await, async_closure, async_fn_in_trait, + async_fn_track_caller, atomic, atomic_mod, atomics, @@ -497,6 +498,7 @@ symbols! { cold, collapse_debuginfo, column, + compare_bytes, compare_exchange, compare_exchange_weak, compile_error, @@ -1155,8 +1157,10 @@ symbols! { profiler_builtins, profiler_runtime, ptr, + ptr_cast, ptr_cast_mut, ptr_const_is_null, + ptr_from_mut, ptr_from_ref, ptr_guaranteed_cmp, ptr_is_null, diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 1846fda7c63..da19a3ba4fd 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -628,7 +628,8 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { valtree, ty ) }); - let s = std::str::from_utf8(slice).expect("non utf8 str from miri"); + let s = std::str::from_utf8(slice) + .expect("non utf8 str from MIR interpreter"); self.push("e"); diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 3307244a217..b52002b1239 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -19,6 +19,7 @@ #![feature(step_trait)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] use std::path::{Path, PathBuf}; diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 9e556c6cb96..c215a7c4b6e 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1404,6 +1404,7 @@ supported_targets! { ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), ("wasm32-unknown-unknown", wasm32_unknown_unknown), ("wasm32-wasi", wasm32_wasi), + ("wasm32-wasi-preview1-threads", wasm32_wasi_preview1_threads), ("wasm64-unknown-unknown", wasm64_unknown_unknown), ("thumbv6m-none-eabi", thumbv6m_none_eabi), diff --git a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs index 75a65a26849..6389d68641a 100644 --- a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), - max_atomic_width: Some(0), + max_atomic_width: Some(32), atomic_cas: false, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs index f2242bbe087..a177a73483f 100644 --- a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), - max_atomic_width: Some(0), + max_atomic_width: Some(32), atomic_cas: false, features: "+m".into(), panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs index 01e773fae97..fd620696cb2 100644 --- a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs @@ -11,7 +11,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), - max_atomic_width: Some(0), + max_atomic_width: Some(32), atomic_cas: false, features: "+m,+c".into(), panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs new file mode 100644 index 00000000000..c567155fee6 --- /dev/null +++ b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs @@ -0,0 +1,134 @@ +//! The `wasm32-wasi-preview1-threads` target is a new and still (as of July 2023) an +//! experimental target. The definition in this file is likely to be tweaked +//! over time and shouldn't be relied on too much. +//! +//! The `wasi-threads` target is a proposal to define a standardized set of syscalls +//! that WebAssembly files can interoperate with. This set of syscalls is +//! intended to empower WebAssembly binaries with native capabilities such as +//! threads, filesystem access, network access, etc. +//! +//! You can see more about the proposal at <https://github.com/WebAssembly/wasi-threads>. +//! +//! The Rust target definition here is interesting in a few ways. We want to +//! serve two use cases here with this target: +//! +//! * First, we want Rust usage of the target to be as hassle-free as possible, +//! ideally avoiding the need to configure and install a local wasm32-wasi-preview1-threads +//! toolchain. +//! +//! * Second, one of the primary use cases of LLVM's new wasm backend and the +//! wasm support in LLD is that any compiled language can interoperate with +//! any other. To that the `wasm32-wasi-preview1-threads` target is the first with a viable C +//! standard library and sysroot common definition, so we want Rust and C/C++ +//! code to interoperate when compiled to `wasm32-unknown-unknown`. +//! +//! You'll note, however, that the two goals above are somewhat at odds with one +//! another. To attempt to solve both use cases in one go we define a target +//! that (ab)uses the `crt-static` target feature to indicate which one you're +//! in. +//! +//! ## No interop with C required +//! +//! By default the `crt-static` target feature is enabled, and when enabled +//! this means that the bundled version of `libc.a` found in `liblibc.rlib` +//! is used. This isn't intended really for interoperation with a C because it +//! may be the case that Rust's bundled C library is incompatible with a +//! foreign-compiled C library. In this use case, though, we use `rust-lld` and +//! some copied crt startup object files to ensure that you can download the +//! wasi target for Rust and you're off to the races, no further configuration +//! necessary. +//! +//! All in all, by default, no external dependencies are required. You can +//! compile `wasm32-wasi-preview1-threads` binaries straight out of the box. You can't, however, +//! reliably interoperate with C code in this mode (yet). +//! +//! ## Interop with C required +//! +//! For the second goal we repurpose the `target-feature` flag, meaning that +//! you'll need to do a few things to have C/Rust code interoperate. +//! +//! 1. All Rust code needs to be compiled with `-C target-feature=-crt-static`, +//! indicating that the bundled C standard library in the Rust sysroot will +//! not be used. +//! +//! 2. If you're using rustc to build a linked artifact then you'll need to +//! specify `-C linker` to a `clang` binary that supports +//! `wasm32-wasi-preview1-threads` and is configured with the `wasm32-wasi-preview1-threads` sysroot. This +//! will cause Rust code to be linked against the libc.a that the specified +//! `clang` provides. +//! +//! 3. If you're building a staticlib and integrating Rust code elsewhere, then +//! compiling with `-C target-feature=-crt-static` is all you need to do. +//! +//! You can configure the linker via Cargo using the +//! `CARGO_TARGET_WASM32_WASI_LINKER` env var. Be sure to also set +//! `CC_wasm32-wasi-preview1-threads` if any crates in the dependency graph are using the `cc` +//! crate. +//! +//! ## Remember, this is all in flux +//! +//! The wasi target is **very** new in its specification. It's likely going to +//! be a long effort to get it standardized and stable. We'll be following it as +//! 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}; + +pub fn target() -> Target { + let mut options = wasm_base::options(); + + options.os = "wasi".into(); + + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::No), + &["--import-memory", "--export-memory", "--shared-memory"], + ); + options.add_pre_link_args( + LinkerFlavor::WasmLld(Cc::Yes), + &[ + "--target=wasm32-wasi-threads", + "-Wl,--import-memory", + "-Wl,--export-memory,", + "-Wl,--shared-memory", + ], + ); + + options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained(); + options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained(); + + // FIXME: Figure out cases in which WASM needs to link with a native toolchain. + options.link_self_contained = LinkSelfContainedDefault::True; + + // Right now this is a bit of a workaround but we're currently saying that + // the target by default has a static crt which we're taking as a signal + // for "use the bundled crt". If that's turned off then the system's crt + // will be used, but this means that default usage of this target doesn't + // need an external compiler but it's still interoperable with an external + // compiler if configured correctly. + options.crt_static_default = true; + options.crt_static_respected = true; + + // Allow `+crt-static` to create a "cdylib" output which is just a wasm file + // without a main function. + options.crt_static_allows_dylibs = true; + + // WASI's `sys::args::init` function ignores its arguments; instead, + // `args::args()` makes the WASI API calls itself. + options.main_needs_argc_argv = false; + + // And, WASI mangles the name of "main" to distinguish between different + // signatures. + options.entry_name = "__main_void".into(); + + options.singlethread = false; + options.features = "+atomics,+bulk-memory,+mutable-globals".into(); + + Target { + llvm_target: "wasm32-wasi".into(), + pointer_width: 32, + data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(), + arch: "wasm32".into(), + options, + } +} diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 6efc1e7302c..38153cccfdd 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -72,7 +72,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { cause: traits::ObligationCause::dummy(), param_env, recursion_depth: 0, - predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx), + predicate: ty::Binder::dummy(trait_ref).to_predicate(self.tcx), }; self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr) } diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 1b4af95cb8a..6b839d64b87 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -162,7 +162,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.add_goal(Goal::new( self.tcx(), param_env, - ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }), + ty::ProjectionPredicate { projection_ty: alias, term: other }, )); Ok(()) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 5ec48c7ac57..267f345c062 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -1,6 +1,5 @@ //! Code shared by trait and projection goals for candidate assembly. -use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use crate::traits::coherence; use rustc_hir::def_id::DefId; @@ -315,7 +314,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return ambig; } - let mut candidates = self.assemble_candidates_via_self_ty(goal); + let mut candidates = self.assemble_candidates_via_self_ty(goal, 0); self.assemble_blanket_impl_candidates(goal, &mut candidates); @@ -351,6 +350,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn assemble_candidates_via_self_ty<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, + num_steps: usize, ) -> Vec<Candidate<'tcx>> { debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { @@ -369,7 +369,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.assemble_coherence_unknowable_candidates(goal, &mut candidates); - self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates); + self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates, num_steps); candidates } @@ -393,49 +393,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, G>, candidates: &mut Vec<Candidate<'tcx>>, + num_steps: usize, ) { let tcx = self.tcx(); let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return }; - let normalized_self_candidates: Result<_, NoSolution> = - self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| { - ecx.with_incremented_depth( - |ecx| { - let result = ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::OVERFLOW, - )?; - Ok(vec![Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), - result, - }]) - }, - |ecx| { - let normalized_ty = ecx.next_ty_infer(); - let normalizes_to_goal = goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), - ); - ecx.add_goal(normalizes_to_goal); - let _ = ecx.try_evaluate_added_goals().inspect_err(|_| { - debug!("self type normalization failed"); - })?; - let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty); - debug!(?normalized_ty, "self type normalized"); - // NOTE: Alternatively we could call `evaluate_goal` here and only - // have a `Normalized` candidate. This doesn't work as long as we - // use `CandidateSource` in winnowing. - let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); - Ok(ecx.assemble_candidates_via_self_ty(goal)) - }, - ) - }); - - if let Ok(normalized_self_candidates) = normalized_self_candidates { - candidates.extend(normalized_self_candidates); - } + candidates.extend(self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| { + if num_steps < ecx.local_overflow_limit() { + let normalized_ty = ecx.next_ty_infer(); + let normalizes_to_goal = goal.with( + tcx, + ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, + ); + ecx.add_goal(normalizes_to_goal); + if let Err(NoSolution) = ecx.try_evaluate_added_goals() { + debug!("self type normalization failed"); + return vec![]; + } + let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty); + debug!(?normalized_ty, "self type normalized"); + // NOTE: Alternatively we could call `evaluate_goal` here and only + // have a `Normalized` candidate. This doesn't work as long as we + // use `CandidateSource` in winnowing. + let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); + ecx.assemble_candidates_via_self_ty(goal, num_steps + 1) + } else { + match ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) { + Ok(result) => vec![Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }], + Err(NoSolution) => vec![], + } + } + })); } #[instrument(level = "debug", skip_all)] @@ -533,7 +524,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (), // FIXME: These should ideally not exist as a self type. It would be nice for - // the builtin auto trait impls of generators should instead directly recurse + // the builtin auto trait impls of generators to instead directly recurse // into the witness. ty::GeneratorWitness(_) | ty::GeneratorWitnessMIR(_, _) => (), @@ -727,7 +718,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.tcx(), ty::TraitPredicate { trait_ref: self_trait_ref, - constness: ty::BoundConstness::NotConst, polarity: ty::ImplPolarity::Positive, }, ); diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index a2db35e069e..72ba4f59137 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -150,7 +150,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( ty::Adt(def, args) => { let sized_crit = def.sized_constraint(ecx.tcx()); - Ok(sized_crit.iter_instantiated_copied(ecx.tcx(), args).collect()) + Ok(sized_crit.iter_instantiated(ecx.tcx(), args).collect()) } } } diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs index 88771f90756..a9d182abfb7 100644 --- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs +++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs @@ -207,23 +207,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { t } - fn fold_region(&mut self, mut r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match self.canonicalize_mode { - CanonicalizeMode::Input => { - // Don't resolve infer vars in input, since it affects - // caching and may cause trait selection bugs which rely - // on regions to be equal. - } - CanonicalizeMode::Response { .. } => { - if let ty::ReVar(vid) = *r { - r = self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.infcx.tcx, vid); - } - } + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + if let ty::ReVar(vid) = *r { + let resolved_region = self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid); + assert_eq!( + r, resolved_region, + "region var should have been resolved, {r} -> {resolved_region}" + ); } let kind = match *r { @@ -278,38 +273,22 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { ty::Region::new_late_bound(self.interner(), self.binder_index, br) } - fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> { + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { let kind = match *t.kind() { - ty::Infer(ty::TyVar(mut vid)) => { - // We need to canonicalize the *root* of our ty var. - // This is so that our canonical response correctly reflects - // any equated inference vars correctly! - let root_vid = self.infcx.root_var(vid); - if root_vid != vid { - t = Ty::new_var(self.infcx.tcx, root_vid); - vid = root_vid; - } - - match self.infcx.probe_ty_var(vid) { - Ok(t) => return self.fold_ty(t), - Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), - } + ty::Infer(ty::TyVar(vid)) => { + assert_eq!(self.infcx.root_var(vid), vid, "ty vid should have been resolved"); + let Err(ui) = self.infcx.probe_ty_var(vid) else { + bug!("ty var should have been resolved: {t}"); + }; + CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) } ty::Infer(ty::IntVar(vid)) => { - let nt = self.infcx.opportunistic_resolve_int_var(vid); - if nt != t { - return self.fold_ty(nt); - } else { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int) - } + assert_eq!(self.infcx.opportunistic_resolve_int_var(vid), t); + CanonicalVarKind::Ty(CanonicalTyVarKind::Int) } ty::Infer(ty::FloatVar(vid)) => { - let nt = self.infcx.opportunistic_resolve_float_var(vid); - if nt != t { - return self.fold_ty(nt); - } else { - CanonicalVarKind::Ty(CanonicalTyVarKind::Float) - } + assert_eq!(self.infcx.opportunistic_resolve_float_var(vid), t); + CanonicalVarKind::Ty(CanonicalTyVarKind::Float) } ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("fresh var during canonicalization: {t:?}") @@ -372,22 +351,19 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { Ty::new_bound(self.infcx.tcx, self.binder_index, bt) } - fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> { + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { let kind = match c.kind() { - ty::ConstKind::Infer(ty::InferConst::Var(mut vid)) => { - // We need to canonicalize the *root* of our const var. - // This is so that our canonical response correctly reflects - // any equated inference vars correctly! - let root_vid = self.infcx.root_const_var(vid); - if root_vid != vid { - c = ty::Const::new_var(self.infcx.tcx, root_vid, c.ty()); - vid = root_vid; - } - - match self.infcx.probe_const_var(vid) { - Ok(c) => return self.fold_const(c), - Err(universe) => CanonicalVarKind::Const(universe, c.ty()), - } + ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { + assert_eq!( + self.infcx.root_const_var(vid), + vid, + "const var should have been resolved" + ); + let Err(ui) = self.infcx.probe_const_var(vid) else { + bug!("const var should have been resolved"); + }; + // FIXME: we should fold this ty eventually + CanonicalVarKind::Const(ui, c.ty()) } ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => { bug!("fresh var during canonicalization: {c:?}") diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 485bc912c44..60c49f665a6 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -1,20 +1,21 @@ +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{ - DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, RegionVariableOrigin, - TyCtxtInferExt, + DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt, }; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; +use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::traits::solve::inspect; use rustc_middle::traits::solve::{ CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques, PredefinedOpaquesData, QueryResult, }; -use rustc_middle::traits::DefiningAnchor; +use rustc_middle::traits::{specialization_graph, DefiningAnchor}; use rustc_middle::ty::{ self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -24,11 +25,10 @@ use rustc_span::DUMMY_SP; use std::io::Write; use std::ops::ControlFlow; -use crate::traits::specialization_graph; use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use super::inspect::ProofTreeBuilder; -use super::search_graph::{self, OverflowHandler}; +use super::search_graph; use super::SolverMode; use super::{search_graph::SearchGraph, Goal}; pub use select::InferCtxtSelectExt; @@ -55,6 +55,9 @@ pub struct EvalCtxt<'a, 'tcx> { /// the job already. infcx: &'a InferCtxt<'tcx>, + /// The variable info for the `var_values`, only used to make an ambiguous response + /// with no constraints. + variables: CanonicalVarInfos<'tcx>, pub(super) var_values: CanonicalVarValues<'tcx>, predefined_opaques_in_body: PredefinedOpaques<'tcx>, @@ -171,6 +174,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.search_graph.solver_mode() } + pub(super) fn local_overflow_limit(&self) -> usize { + self.search_graph.local_overflow_limit() + } + /// Creates a root evaluation context and search graph. This should only be /// used from outside of any evaluation, and other methods should be preferred /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]). @@ -184,18 +191,19 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let mut ecx = EvalCtxt { search_graph: &mut search_graph, - infcx: infcx, + infcx, + nested_goals: NestedGoals::new(), + inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree), + // Only relevant when canonicalizing the response, // which we don't do within this evaluation context. predefined_opaques_in_body: infcx .tcx .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()), - // Only relevant when canonicalizing the response. max_input_universe: ty::UniverseIndex::ROOT, + variables: ty::List::empty(), var_values: CanonicalVarValues::dummy(), - nested_goals: NestedGoals::new(), tainted: Ok(()), - inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree), }; let result = f(&mut ecx); @@ -245,6 +253,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let mut ecx = EvalCtxt { infcx, + variables: canonical_input.variables, var_values, predefined_opaques_in_body: input.predefined_opaques_in_body, max_input_universe: canonical_input.max_universe, @@ -300,24 +309,26 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // Deal with overflow, caching, and coinduction. // // The actual solver logic happens in `ecx.compute_goal`. - search_graph.with_new_goal( - tcx, - canonical_input, - goal_evaluation, - |search_graph, goal_evaluation| { - EvalCtxt::enter_canonical( - tcx, - search_graph, - canonical_input, - goal_evaluation, - |ecx, goal| { - let result = ecx.compute_goal(goal); - ecx.inspect.query_result(result); - result - }, - ) - }, - ) + ensure_sufficient_stack(|| { + search_graph.with_new_goal( + tcx, + canonical_input, + goal_evaluation, + |search_graph, goal_evaluation| { + EvalCtxt::enter_canonical( + tcx, + search_graph, + canonical_input, + goal_evaluation, + |ecx, goal| { + let result = ecx.compute_goal(goal); + ecx.inspect.query_result(result); + result + }, + ) + }, + ) + }) } /// Recursively evaluates `goal`, returning whether any inference vars have @@ -329,6 +340,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> { let (orig_values, canonical_goal) = self.canonicalize_goal(goal); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack); + let encountered_overflow = self.search_graph.encountered_overflow(); let canonical_response = EvalCtxt::evaluate_canonical_goal( self.tcx(), self.search_graph, @@ -377,14 +389,21 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { && !self.search_graph.in_cycle() { debug!("rerunning goal to check result is stable"); + self.search_graph.reset_encountered_overflow(encountered_overflow); let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); - let new_canonical_response = EvalCtxt::evaluate_canonical_goal( + let Ok(new_canonical_response) = EvalCtxt::evaluate_canonical_goal( self.tcx(), self.search_graph, canonical_goal, // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal` &mut ProofTreeBuilder::new_noop(), - )?; + ) else { + bug!( + "goal went from {certainty:?} to error: re-canonicalized goal={canonical_goal:#?} \ + first_response={canonical_response:#?}, + second response was error" + ); + }; // We only check for modulo regions as we convert all regions in // the input to new existentials, even if they're expected to be // `'static` or a placeholder region. @@ -471,101 +490,22 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let inspect = self.inspect.new_evaluate_added_goals(); let inspect = core::mem::replace(&mut self.inspect, inspect); - let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new()); - let mut new_goals = NestedGoals::new(); - - let response = self.repeat_while_none( - |_| Ok(Certainty::OVERFLOW), - |this| { - this.inspect.evaluate_added_goals_loop_start(); - - let mut has_changed = Err(Certainty::Yes); - - if let Some(goal) = goals.normalizes_to_hack_goal.take() { - // Replace the goal with an unconstrained infer var, so the - // RHS does not affect projection candidate assembly. - let unconstrained_rhs = this.next_term_infer_of_kind(goal.predicate.term); - let unconstrained_goal = goal.with( - this.tcx(), - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: goal.predicate.projection_ty, - term: unconstrained_rhs, - }), - ); - - let (_, certainty, instantiate_goals) = - match this.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal) { - Ok(r) => r, - Err(NoSolution) => return Some(Err(NoSolution)), - }; - new_goals.goals.extend(instantiate_goals); - - // Finally, equate the goal's RHS with the unconstrained var. - // We put the nested goals from this into goals instead of - // next_goals to avoid needing to process the loop one extra - // time if this goal returns something -- I don't think this - // matters in practice, though. - match this.eq_and_get_goals( - goal.param_env, - goal.predicate.term, - unconstrained_rhs, - ) { - Ok(eq_goals) => { - goals.goals.extend(eq_goals); - } - Err(NoSolution) => return Some(Err(NoSolution)), - }; - - // We only look at the `projection_ty` part here rather than - // looking at the "has changed" return from evaluate_goal, - // because we expect the `unconstrained_rhs` part of the predicate - // to have changed -- that means we actually normalized successfully! - if goal.predicate.projection_ty - != this.resolve_vars_if_possible(goal.predicate.projection_ty) - { - has_changed = Ok(()) - } - - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - // We need to resolve vars here so that we correctly - // deal with `has_changed` in the next iteration. - new_goals.normalizes_to_hack_goal = - Some(this.resolve_vars_if_possible(goal)); - has_changed = has_changed.map_err(|c| c.unify_with(certainty)); - } - } - } - - for goal in goals.goals.drain(..) { - let (changed, certainty, instantiate_goals) = - match this.evaluate_goal(IsNormalizesToHack::No, goal) { - Ok(result) => result, - Err(NoSolution) => return Some(Err(NoSolution)), - }; - new_goals.goals.extend(instantiate_goals); - - if changed { - has_changed = Ok(()); - } - - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - new_goals.goals.push(goal); - has_changed = has_changed.map_err(|c| c.unify_with(certainty)); - } - } + let mut response = Ok(Certainty::OVERFLOW); + for _ in 0..self.local_overflow_limit() { + // FIXME: This match is a bit ugly, it might be nice to change the inspect + // stuff to use a closure instead. which should hopefully simplify this a bit. + match self.evaluate_added_goals_step() { + Ok(Some(cert)) => { + response = Ok(cert); + break; } - - core::mem::swap(&mut new_goals, &mut goals); - match has_changed { - Ok(()) => None, - Err(certainty) => Some(Ok(certainty)), + Ok(None) => {} + Err(NoSolution) => { + response = Err(NoSolution); + break; } - }, - ); + } + } self.inspect.eval_added_goals_result(response); @@ -576,9 +516,84 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let goal_evaluations = std::mem::replace(&mut self.inspect, inspect); self.inspect.added_goals_evaluation(goal_evaluations); - self.nested_goals = goals; response } + + /// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning. + /// + /// Goals for the next step get directly added the the nested goals of the `EvalCtxt`. + fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> { + let tcx = self.tcx(); + let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new()); + + self.inspect.evaluate_added_goals_loop_start(); + // If this loop did not result in any progress, what's our final certainty. + let mut unchanged_certainty = Some(Certainty::Yes); + if let Some(goal) = goals.normalizes_to_hack_goal.take() { + // Replace the goal with an unconstrained infer var, so the + // RHS does not affect projection candidate assembly. + let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term); + let unconstrained_goal = goal.with( + tcx, + ty::ProjectionPredicate { + projection_ty: goal.predicate.projection_ty, + term: unconstrained_rhs, + }, + ); + + let (_, certainty, instantiate_goals) = + self.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal)?; + self.add_goals(instantiate_goals); + + // Finally, equate the goal's RHS with the unconstrained var. + // We put the nested goals from this into goals instead of + // next_goals to avoid needing to process the loop one extra + // time if this goal returns something -- I don't think this + // matters in practice, though. + let eq_goals = + self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?; + goals.goals.extend(eq_goals); + + // We only look at the `projection_ty` part here rather than + // looking at the "has changed" return from evaluate_goal, + // because we expect the `unconstrained_rhs` part of the predicate + // to have changed -- that means we actually normalized successfully! + if goal.predicate.projection_ty + != self.resolve_vars_if_possible(goal.predicate.projection_ty) + { + unchanged_certainty = None; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + // We need to resolve vars here so that we correctly + // deal with `has_changed` in the next iteration. + self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal)); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } + } + } + + for goal in goals.goals.drain(..) { + let (has_changed, certainty, instantiate_goals) = + self.evaluate_goal(IsNormalizesToHack::No, goal)?; + self.add_goals(instantiate_goals); + if has_changed { + unchanged_certainty = None; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.add_goal(goal); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } + } + } + + Ok(unchanged_certainty) + } } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -593,10 +608,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } - pub(super) fn next_region_infer(&self) -> ty::Region<'tcx> { - self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) - } - pub(super) fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> { self.infcx.next_const_var( ty, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 7323b98b8ce..0990b9bee90 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -10,17 +10,21 @@ //! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer}; -use crate::solve::{CanonicalResponse, QueryResult, Response}; +use crate::solve::{response_no_constraints_raw, CanonicalResponse, QueryResult, Response}; use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; +use rustc_infer::infer::InferCtxt; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{ - ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, + ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, +}; +use rustc_middle::ty::{ + self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, }; -use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use rustc_span::DUMMY_SP; use std::iter; use std::ops::Deref; @@ -32,6 +36,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &self, goal: Goal<'tcx, T>, ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) { + let opaque_types = self.infcx.clone_opaque_types_for_query_response(); + let (goal, opaque_types) = + (goal, opaque_types).fold_with(&mut EagerResolver { infcx: self.infcx }); + let mut orig_values = Default::default(); let canonical_goal = Canonicalizer::canonicalize( self.infcx, @@ -40,11 +48,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { QueryInput { goal, anchor: self.infcx.defining_use_anchor, - predefined_opaques_in_body: self.tcx().mk_predefined_opaques_in_body( - PredefinedOpaquesData { - opaque_types: self.infcx.clone_opaque_types_for_query_response(), - }, - ), + predefined_opaques_in_body: self + .tcx() + .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), }, ); (orig_values, canonical_goal) @@ -70,34 +76,43 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ); let certainty = certainty.unify_with(goals_certainty); + if let Certainty::OVERFLOW = certainty { + // If we have overflow, it's probable that we're substituting a type + // into itself infinitely and any partial substitutions in the query + // response are probably not useful anyways, so just return an empty + // query response. + // + // This may prevent us from potentially useful inference, e.g. + // 2 candidates, one ambiguous and one overflow, which both + // have the same inference constraints. + // + // Changing this to retain some constraints in the future + // won't be a breaking change, so this is good enough for now. + return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow)); + } - let response = match certainty { - Certainty::Yes | Certainty::Maybe(MaybeCause::Ambiguity) => { - let external_constraints = self.compute_external_query_constraints()?; - Response { var_values: self.var_values, external_constraints, certainty } - } - Certainty::Maybe(MaybeCause::Overflow) => { - // If we have overflow, it's probable that we're substituting a type - // into itself infinitely and any partial substitutions in the query - // response are probably not useful anyways, so just return an empty - // query response. - // - // This may prevent us from potentially useful inference, e.g. - // 2 candidates, one ambiguous and one overflow, which both - // have the same inference constraints. - // - // Changing this to retain some constraints in the future - // won't be a breaking change, so this is good enough for now. - return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow)); - } - }; + let var_values = self.var_values; + let external_constraints = self.compute_external_query_constraints()?; + + let (var_values, mut external_constraints) = + (var_values, external_constraints).fold_with(&mut EagerResolver { infcx: self.infcx }); + // Remove any trivial region constraints once we've resolved regions + external_constraints + .region_constraints + .outlives + .retain(|(outlives, _)| outlives.0.as_region().map_or(true, |re| re != outlives.1)); let canonical = Canonicalizer::canonicalize( self.infcx, CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, &mut Default::default(), - response, + Response { + var_values, + certainty, + external_constraints: self.tcx().mk_external_constraints(external_constraints), + }, ); + Ok(canonical) } @@ -109,29 +124,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &self, maybe_cause: MaybeCause, ) -> CanonicalResponse<'tcx> { - let unconstrained_response = Response { - var_values: CanonicalVarValues { - var_values: self.tcx().mk_args_from_iter(self.var_values.var_values.iter().map( - |arg| -> ty::GenericArg<'tcx> { - match arg.unpack() { - GenericArgKind::Lifetime(_) => self.next_region_infer().into(), - GenericArgKind::Type(_) => self.next_ty_infer().into(), - GenericArgKind::Const(ct) => self.next_const_infer(ct.ty()).into(), - } - }, - )), - }, - external_constraints: self - .tcx() - .mk_external_constraints(ExternalConstraintsData::default()), - certainty: Certainty::Maybe(maybe_cause), - }; - - Canonicalizer::canonicalize( - self.infcx, - CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, - &mut Default::default(), - unconstrained_response, + response_no_constraints_raw( + self.tcx(), + self.max_input_universe, + self.variables, + Certainty::Maybe(maybe_cause), ) } @@ -143,7 +140,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// further constrained by inference, that will be passed back in the var /// values. #[instrument(level = "debug", skip(self), ret)] - fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> { + fn compute_external_query_constraints( + &self, + ) -> Result<ExternalConstraintsData<'tcx>, NoSolution> { // We only check for leaks from universes which were entered inside // of the query. self.infcx.leak_check(self.max_input_universe, None).map_err(|e| { @@ -173,9 +172,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a) }); - Ok(self - .tcx() - .mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types })) + Ok(ExternalConstraintsData { region_constraints, opaque_types }) } /// After calling a canonical query, we apply the constraints returned @@ -333,3 +330,65 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok(()) } } + +/// Resolves ty, region, and const vars to their inferred values or their root vars. +struct EagerResolver<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerResolver<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) { + Ok(t) => t.fold_with(self), + Err(_) => Ty::new_var(self.infcx.tcx, self.infcx.root_var(vid)), + }, + ty::Infer(ty::IntVar(vid)) => self.infcx.opportunistic_resolve_int_var(vid), + ty::Infer(ty::FloatVar(vid)) => self.infcx.opportunistic_resolve_float_var(vid), + _ => { + if t.has_infer() { + t.super_fold_with(self) + } else { + t + } + } + } + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReVar(vid) => self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid), + _ => r, + } + } + + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + match c.kind() { + ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { + // FIXME: we need to fold the ty too, I think. + match self.infcx.probe_const_var(vid) { + Ok(c) => c.fold_with(self), + Err(_) => { + ty::Const::new_var(self.infcx.tcx, self.infcx.root_const_var(vid), c.ty()) + } + } + } + _ => { + if c.has_infer() { + c.super_fold_with(self) + } else { + c + } + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs index 4477ea7d501..317c43baf8f 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -17,6 +17,7 @@ where let mut nested_ecx = EvalCtxt { infcx: outer_ecx.infcx, + variables: outer_ecx.variables, var_values: outer_ecx.var_values, predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body, max_input_universe: outer_ecx.max_input_universe, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 1aef9a885bc..ca4a4c9510c 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -14,7 +14,6 @@ use rustc_span::DUMMY_SP; use crate::solve::assembly::{Candidate, CandidateSource}; use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; use crate::solve::inspect::ProofTreeBuilder; -use crate::solve::search_graph::OverflowHandler; use crate::traits::StructurallyNormalizeExt; use crate::traits::TraitEngineExt; @@ -143,7 +142,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // the cycle anyways one step later. EvalCtxt::enter_canonical( self.tcx(), - self.search_graph(), + self.search_graph, canonical_input, // FIXME: This is wrong, idk if we even want to track stuff here. &mut ProofTreeBuilder::new_noop(), @@ -235,7 +234,10 @@ fn rematch_unsize<'tcx>( goal.param_env, &mut nested, ); + match (a_ty.kind(), b_ty.kind()) { + // Don't try to coerce `?0` to `dyn Trait` + (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => Ok(None), // Stall any ambiguous upcasting goals, since we can't rematch those (ty::Dynamic(_, _, ty::Dyn), ty::Dynamic(_, _, ty::Dyn)) => match certainty { Certainty::Yes => Ok(Some(ImplSource::Builtin(source, nested))), @@ -266,7 +268,7 @@ fn rematch_unsize<'tcx>( infcx.tcx, ObligationCause::dummy(), goal.param_env, - ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), + ty::OutlivesPredicate(a_ty, region), )); Ok(Some(ImplSource::Builtin(source, nested))) diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 63e48c94a86..63d4a38119f 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -17,10 +17,11 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_infer::traits::query::NoSolution; +use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::traits::solve::{ CanonicalResponse, Certainty, ExternalConstraintsData, Goal, QueryResult, Response, }; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; @@ -284,20 +285,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } -pub(super) fn response_no_constraints<'tcx>( +fn response_no_constraints_raw<'tcx>( tcx: TyCtxt<'tcx>, - goal: Canonical<'tcx, impl Sized>, + max_universe: UniverseIndex, + variables: CanonicalVarInfos<'tcx>, certainty: Certainty, -) -> QueryResult<'tcx> { - Ok(Canonical { - max_universe: goal.max_universe, - variables: goal.variables, +) -> CanonicalResponse<'tcx> { + Canonical { + max_universe, + variables, value: Response { - var_values: CanonicalVarValues::make_identity(tcx, goal.variables), + var_values: CanonicalVarValues::make_identity(tcx, variables), // FIXME: maybe we should store the "no response" version in tcx, like // we do for tcx.types and stuff. external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()), certainty, }, - }) + } } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 091b7f33834..872f0c87916 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -75,10 +75,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { tcx, self.at.cause.clone(), self.at.param_env, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: alias, - term: new_infer_ty.into(), - }), + ty::ProjectionPredicate { projection_ty: alias, term: new_infer_ty.into() }, ); // Do not emit an error if normalization is known to fail but instead @@ -131,10 +128,10 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { tcx, self.at.cause.clone(), self.at.param_env, - ty::Binder::dummy(ty::ProjectionPredicate { + ty::ProjectionPredicate { projection_ty: tcx.mk_alias_ty(uv.def, uv.args), term: new_infer_ct.into(), - }), + }, ); let result = if infcx.predicate_may_hold(&obligation) { diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 2ce57751740..75cf33d8194 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -393,10 +393,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { None => tcx.types.unit, Some(field_def) => { let self_ty = field_def.ty(tcx, args); - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), - )); + ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty))); return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } @@ -406,10 +403,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ty::Tuple(elements) => match elements.last() { None => tcx.types.unit, Some(&self_ty) => { - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), - )); + ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty))); return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } @@ -450,10 +444,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { Self::consider_implied_clause( ecx, goal, - ty::Binder::dummy(ty::ProjectionPredicate { + ty::ProjectionPredicate { projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]), term, - }) + } .to_predicate(tcx), // Technically, we need to check that the future type is Sized, // but that's already proven by the generator being WF. @@ -490,12 +484,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { Self::consider_implied_clause( ecx, goal, - ty::Binder::dummy(ty::ProjectionPredicate { + ty::ProjectionPredicate { projection_ty: ecx .tcx() .mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]), term, - }) + } .to_predicate(tcx), // Technically, we need to check that the future type is Sized, // but that's already proven by the generator being WF. diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index c25d6eca918..21c8d476902 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -1,37 +1,51 @@ mod cache; -mod overflow; - -pub(super) use overflow::OverflowHandler; -use rustc_middle::traits::solve::inspect::CacheHit; use self::cache::ProvisionalEntry; +use super::inspect::ProofTreeBuilder; +use super::SolverMode; use cache::ProvisionalCache; -use overflow::OverflowData; +use rustc_data_structures::fx::FxHashSet; +use rustc_index::Idx; use rustc_index::IndexVec; use rustc_middle::dep_graph::DepKind; +use rustc_middle::traits::solve::inspect::CacheHit; +use rustc_middle::traits::solve::CacheData; use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; use rustc_middle::ty::TyCtxt; +use rustc_session::Limit; use std::{collections::hash_map::Entry, mem}; -use super::inspect::ProofTreeBuilder; -use super::SolverMode; - rustc_index::newtype_index! { pub struct StackDepth {} } -struct StackElem<'tcx> { +#[derive(Debug)] +struct StackEntry<'tcx> { input: CanonicalInput<'tcx>, + available_depth: Limit, + // The maximum depth reached by this stack entry, only up-to date + // for the top of the stack and lazily updated for the rest. + reached_depth: StackDepth, + encountered_overflow: bool, has_been_used: bool, + + /// We put only the root goal of a coinductive cycle into the global cache. + /// + /// If we were to use that result when later trying to prove another cycle + /// participant, we can end up with unstable query results. + /// + /// See tests/ui/new-solver/coinduction/incompleteness-unstable-result.rs for + /// an example of where this is needed. + cycle_participants: FxHashSet<CanonicalInput<'tcx>>, } pub(super) struct SearchGraph<'tcx> { mode: SolverMode, + local_overflow_limit: usize, /// The stack of goals currently being computed. /// /// An element is *deeper* in the stack if its index is *lower*. - stack: IndexVec<StackDepth, StackElem<'tcx>>, - overflow_data: OverflowData, + stack: IndexVec<StackDepth, StackEntry<'tcx>>, provisional_cache: ProvisionalCache<'tcx>, } @@ -39,8 +53,8 @@ impl<'tcx> SearchGraph<'tcx> { pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> { Self { mode, + local_overflow_limit: tcx.recursion_limit().0.ilog2() as usize, stack: Default::default(), - overflow_data: OverflowData::new(tcx), provisional_cache: ProvisionalCache::empty(), } } @@ -49,15 +63,38 @@ impl<'tcx> SearchGraph<'tcx> { self.mode } - /// We do not use the global cache during coherence. + pub(super) fn local_overflow_limit(&self) -> usize { + self.local_overflow_limit + } + + /// Update the stack and reached depths on cache hits. + #[instrument(level = "debug", skip(self))] + fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) { + let reached_depth = self.stack.next_index().plus(additional_depth); + if let Some(last) = self.stack.raw.last_mut() { + last.reached_depth = last.reached_depth.max(reached_depth); + last.encountered_overflow |= encountered_overflow; + } + } + + /// Pops the highest goal from the stack, lazily updating the + /// the next goal in the stack. /// + /// Directly popping from the stack instead of using this method + /// would cause us to not track overflow and recursion depth correctly. + fn pop_stack(&mut self) -> StackEntry<'tcx> { + let elem = self.stack.pop().unwrap(); + if let Some(last) = self.stack.raw.last_mut() { + last.reached_depth = last.reached_depth.max(elem.reached_depth); + last.encountered_overflow |= elem.encountered_overflow; + } + elem + } + /// The trait solver behavior is different for coherence - /// so we would have to add the solver mode to the cache key. - /// This is probably not worth it as trait solving during - /// coherence tends to already be incredibly fast. - /// - /// We could add another global cache for coherence instead, - /// but that's effort so let's only do it if necessary. + /// so we use a separate cache. Alternatively we could use + /// a single cache and share it between coherence and ordinary + /// trait solving. pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> { match self.mode { SolverMode::Normal => &tcx.new_solver_evaluation_cache, @@ -87,36 +124,107 @@ impl<'tcx> SearchGraph<'tcx> { } } - /// Tries putting the new goal on the stack, returning an error if it is already cached. + /// Fetches whether the current goal encountered overflow. + /// + /// This should only be used for the check in `evaluate_goal`. + pub(super) fn encountered_overflow(&self) -> bool { + if let Some(last) = self.stack.raw.last() { last.encountered_overflow } else { false } + } + + /// Resets `encountered_overflow` of the current goal. + /// + /// This should only be used for the check in `evaluate_goal`. + pub(super) fn reset_encountered_overflow(&mut self, encountered_overflow: bool) { + if encountered_overflow { + self.stack.raw.last_mut().unwrap().encountered_overflow = true; + } + } + + /// Returns the remaining depth allowed for nested goals. + /// + /// This is generally simply one less than the current depth. + /// However, if we encountered overflow, we significantly reduce + /// the remaining depth of all nested goals to prevent hangs + /// in case there is exponential blowup. + fn allowed_depth_for_nested( + tcx: TyCtxt<'tcx>, + stack: &IndexVec<StackDepth, StackEntry<'tcx>>, + ) -> Option<Limit> { + if let Some(last) = stack.raw.last() { + if last.available_depth.0 == 0 { + return None; + } + + Some(if last.encountered_overflow { + Limit(last.available_depth.0 / 4) + } else { + Limit(last.available_depth.0 - 1) + }) + } else { + Some(tcx.recursion_limit()) + } + } + + /// Probably the most involved method of the whole solver. /// - /// This correctly updates the provisional cache if there is a cycle. - #[instrument(level = "debug", skip(self, tcx, inspect), ret)] - fn try_push_stack( + /// Given some goal which is proven via the `prove_goal` closure, this + /// handles caching, overflow, and coinductive cycles. + pub(super) fn with_new_goal( &mut self, tcx: TyCtxt<'tcx>, input: CanonicalInput<'tcx>, inspect: &mut ProofTreeBuilder<'tcx>, - ) -> Result<(), QueryResult<'tcx>> { - // Look at the provisional cache to check for cycles. + mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx> { + // Check for overflow. + let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else { + if let Some(last) = self.stack.raw.last_mut() { + last.encountered_overflow = true; + } + return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); + }; + + // Try to fetch the goal from the global cache. + if inspect.use_global_cache() { + if let Some(CacheData { result, reached_depth, encountered_overflow }) = + self.global_cache(tcx).get( + tcx, + input, + |cycle_participants| { + self.stack.iter().any(|entry| cycle_participants.contains(&entry.input)) + }, + available_depth, + ) + { + self.on_cache_hit(reached_depth, encountered_overflow); + return result; + } + } + + // Look at the provisional cache to detect cycles. let cache = &mut self.provisional_cache; match cache.lookup_table.entry(input) { - // No entry, simply push this goal on the stack after dealing with overflow. + // No entry, we push this goal on the stack and try to prove it. Entry::Vacant(v) => { - if self.overflow_data.has_overflow(self.stack.len()) { - return Err(self.deal_with_overflow(tcx, input)); - } - - let depth = self.stack.push(StackElem { input, has_been_used: false }); - let response = super::response_no_constraints(tcx, input, Certainty::Yes); + let depth = self.stack.next_index(); + let entry = StackEntry { + input, + available_depth, + reached_depth: depth, + encountered_overflow: false, + has_been_used: false, + cycle_participants: Default::default(), + }; + assert_eq!(self.stack.push(entry), depth); + let response = Self::response_no_constraints(tcx, input, Certainty::Yes); let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input }); v.insert(entry_index); - Ok(()) } // We have a nested goal which relies on a goal `root` deeper in the stack. // - // We first store that we may have to rerun `evaluate_goal` for `root` in case the - // provisional response is not equal to the final response. We also update the depth - // of all goals which recursively depend on our current goal to depend on `root` + // We first store that we may have to reprove `root` in case the provisional + // response is not equal to the final response. We also update the depth of all + // goals which recursively depend on our current goal to depend on `root` // instead. // // Finally we can return either the provisional response for that goal if we have a @@ -125,13 +233,16 @@ impl<'tcx> SearchGraph<'tcx> { inspect.cache_hit(CacheHit::Provisional); let entry_index = *entry_index.get(); - let stack_depth = cache.depth(entry_index); debug!("encountered cycle with depth {stack_depth:?}"); cache.add_dependency_of_leaf_on(entry_index); + let mut iter = self.stack.iter_mut(); + let root = iter.nth(stack_depth.as_usize()).unwrap(); + for e in iter { + root.cycle_participants.insert(e.input); + } - self.stack[stack_depth].has_been_used = true; // NOTE: The goals on the stack aren't the only goals involved in this cycle. // We can also depend on goals which aren't part of the stack but coinductively // depend on the stack themselves. We already checked whether all the goals @@ -142,145 +253,111 @@ impl<'tcx> SearchGraph<'tcx> { .iter() .all(|g| g.input.value.goal.predicate.is_coinductive(tcx)) { - Err(cache.provisional_result(entry_index)) + // If we're in a coinductive cycle, we have to retry proving the current goal + // until we reach a fixpoint. + self.stack[stack_depth].has_been_used = true; + return cache.provisional_result(entry_index); } else { - Err(super::response_no_constraints(tcx, input, Certainty::OVERFLOW)) + return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); } } } - } - /// We cannot simply store the result of [super::EvalCtxt::compute_goal] as we have to deal with - /// coinductive cycles. - /// - /// When we encounter a coinductive cycle, we have to prove the final result of that cycle - /// while we are still computing that result. Because of this we continuously recompute the - /// cycle until the result of the previous iteration is equal to the final result, at which - /// point we are done. - /// - /// This function returns `true` if we were able to finalize the goal and `false` if it has - /// updated the provisional cache and we have to recompute the current goal. - /// - /// FIXME: Refer to the rustc-dev-guide entry once it exists. - #[instrument(level = "debug", skip(self, actual_input), ret)] - fn try_finalize_goal( - &mut self, - actual_input: CanonicalInput<'tcx>, - response: QueryResult<'tcx>, - ) -> bool { - let stack_elem = self.stack.pop().unwrap(); - let StackElem { input, has_been_used } = stack_elem; - assert_eq!(input, actual_input); + // This is for global caching, so we properly track query dependencies. + // Everything that affects the `result` should be performed within this + // `with_anon_task` closure. + let ((final_entry, result), dep_node) = + tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || { + // When we encounter a coinductive cycle, we have to fetch the + // result of that cycle while we are still computing it. Because + // of this we continuously recompute the cycle until the result + // of the previous iteration is equal to the final result, at which + // point we are done. + for _ in 0..self.local_overflow_limit() { + let response = prove_goal(self, inspect); - let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(&input).unwrap(); - let provisional_entry = &mut cache.entries[provisional_entry_index]; - // We eagerly update the response in the cache here. If we have to reevaluate - // this goal we use the new response when hitting a cycle, and we definitely - // want to access the final response whenever we look at the cache. - let prev_response = mem::replace(&mut provisional_entry.response, response); - - // Was the current goal the root of a cycle and was the provisional response - // different from the final one. - if has_been_used && prev_response != response { - // If so, remove all entries whose result depends on this goal - // from the provisional cache... - // - // That's not completely correct, as a nested goal can also - // depend on a goal which is lower in the stack so it doesn't - // actually depend on the current goal. This should be fairly - // rare and is hopefully not relevant for performance. - #[allow(rustc::potential_query_instability)] - cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index); - cache.entries.truncate(provisional_entry_index.index() + 1); - - // ...and finally push our goal back on the stack and reevaluate it. - self.stack.push(StackElem { input, has_been_used: false }); - false - } else { - true - } - } + // Check whether the current goal is the root of a cycle and whether + // we have to rerun because its provisional result differed from the + // final result. + // + // Also update the response for this goal stored in the provisional + // cache. + let stack_entry = self.pop_stack(); + debug_assert_eq!(stack_entry.input, input); + let cache = &mut self.provisional_cache; + let provisional_entry_index = + *cache.lookup_table.get(&stack_entry.input).unwrap(); + let provisional_entry = &mut cache.entries[provisional_entry_index]; + let prev_response = mem::replace(&mut provisional_entry.response, response); + if stack_entry.has_been_used && prev_response != response { + // If so, remove all entries whose result depends on this goal + // from the provisional cache... + // + // That's not completely correct, as a nested goal can also + // depend on a goal which is lower in the stack so it doesn't + // actually depend on the current goal. This should be fairly + // rare and is hopefully not relevant for performance. + #[allow(rustc::potential_query_instability)] + cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index); + cache.entries.truncate(provisional_entry_index.index() + 1); - pub(super) fn with_new_goal( - &mut self, - tcx: TyCtxt<'tcx>, - canonical_input: CanonicalInput<'tcx>, - inspect: &mut ProofTreeBuilder<'tcx>, - mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, - ) -> QueryResult<'tcx> { - if inspect.use_global_cache() { - if let Some(result) = self.global_cache(tcx).get(&canonical_input, tcx) { - debug!(?canonical_input, ?result, "cache hit"); - inspect.cache_hit(CacheHit::Global); - return result; - } - } + // ...and finally push our goal back on the stack and reevaluate it. + self.stack.push(StackEntry { has_been_used: false, ..stack_entry }); + } else { + return (stack_entry, response); + } + } - match self.try_push_stack(tcx, canonical_input, inspect) { - Ok(()) => {} - // Our goal is already on the stack, eager return. - Err(response) => return response, - } - - // This is for global caching, so we properly track query dependencies. - // Everything that affects the `Result` should be performed within this - // `with_anon_task` closure. - let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || { - self.repeat_while_none( - |this| { - let result = this.deal_with_overflow(tcx, canonical_input); - let _ = this.stack.pop().unwrap(); - result - }, - |this| { - let result = loop_body(this, inspect); - this.try_finalize_goal(canonical_input, result).then(|| result) - }, - ) - }); + debug!("canonical cycle overflow"); + let current_entry = self.pop_stack(); + let result = Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); + (current_entry, result) + }); + // We're now done with this goal. In case this goal is involved in a larger cycle + // do not remove it from the provisional cache and do not add it to the global + // cache. + // + // It is not possible for any nested goal to depend on something deeper on the + // stack, as this would have also updated the depth of the current goal. let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(&canonical_input).unwrap(); + let provisional_entry_index = *cache.lookup_table.get(&input).unwrap(); let provisional_entry = &mut cache.entries[provisional_entry_index]; let depth = provisional_entry.depth; - - // If not, we're done with this goal. - // - // Check whether that this goal doesn't depend on a goal deeper on the stack - // and if so, move it to the global cache. - // - // Note that if any nested goal were to depend on something deeper on the stack, - // this would have also updated the depth of the current goal. if depth == self.stack.next_index() { - // If the current goal is the head of a cycle, we drop all other - // cycle participants without moving them to the global cache. - let other_cycle_participants = provisional_entry_index.index() + 1; - for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) { + for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) { let actual_index = cache.lookup_table.remove(&entry.input); debug_assert_eq!(Some(i), actual_index); debug_assert!(entry.depth == depth); } - let current_goal = cache.entries.pop().unwrap(); - let actual_index = cache.lookup_table.remove(¤t_goal.input); - debug_assert_eq!(Some(provisional_entry_index), actual_index); - debug_assert!(current_goal.depth == depth); - - // We move the root goal to the global cache if we either did not hit an overflow or if it's - // the root goal as that will now always hit the same overflow limit. - // - // NOTE: We cannot move any non-root goals to the global cache. When replaying the root goal's - // dependencies, our non-root goal may no longer appear as child of the root goal. + // When encountering a cycle, both inductive and coinductive, we only + // move the root into the global cache. We also store all other cycle + // participants involved. // - // See https://github.com/rust-lang/rust/pull/108071 for some additional context. - let can_cache = inspect.use_global_cache() - && (!self.overflow_data.did_overflow() || self.stack.is_empty()); - if can_cache { - self.global_cache(tcx).insert(current_goal.input, dep_node, current_goal.response) - } + // We disable the global cache entry of the root goal if a cycle + // participant is on the stack. This is necessary to prevent unstable + // results. See the comment of `StackEntry::cycle_participants` for + // more details. + let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len(); + self.global_cache(tcx).insert( + input, + reached_depth, + final_entry.encountered_overflow, + final_entry.cycle_participants, + dep_node, + result, + ) } result } + + fn response_no_constraints( + tcx: TyCtxt<'tcx>, + goal: CanonicalInput<'tcx>, + certainty: Certainty, + ) -> QueryResult<'tcx> { + Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty)) + } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs deleted file mode 100644 index 83a051eeb1a..00000000000 --- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs +++ /dev/null @@ -1,120 +0,0 @@ -use rustc_infer::infer::canonical::Canonical; -use rustc_infer::traits::query::NoSolution; -use rustc_middle::traits::solve::{Certainty, QueryResult}; -use rustc_middle::ty::TyCtxt; -use rustc_session::Limit; - -use super::SearchGraph; -use crate::solve::{response_no_constraints, EvalCtxt}; - -/// When detecting a solver overflow, we return ambiguity. Overflow can be -/// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**. -/// -/// This is in issue in case of exponential blowup, e.g. if each goal on the stack -/// has multiple nested (overflowing) candidates. To deal with this, we reduce the limit -/// used by the solver when hitting the default limit for the first time. -/// -/// FIXME: Get tests where always using the `default_limit` results in a hang and refer -/// to them here. We can also improve the overflow strategy if necessary. -pub(super) struct OverflowData { - default_limit: Limit, - current_limit: Limit, - /// When proving an **AND** we have to repeatedly iterate over the yet unproven goals. - /// - /// Because of this each iteration also increases the depth in addition to the stack - /// depth. - additional_depth: usize, -} - -impl OverflowData { - pub(super) fn new(tcx: TyCtxt<'_>) -> OverflowData { - let default_limit = tcx.recursion_limit(); - OverflowData { default_limit, current_limit: default_limit, additional_depth: 0 } - } - - #[inline] - pub(super) fn did_overflow(&self) -> bool { - self.default_limit.0 != self.current_limit.0 - } - - #[inline] - pub(super) fn has_overflow(&self, depth: usize) -> bool { - !self.current_limit.value_within_limit(depth + self.additional_depth) - } - - /// Updating the current limit when hitting overflow. - fn deal_with_overflow(&mut self) { - // When first hitting overflow we reduce the overflow limit - // for all future goals to prevent hangs if there's an exponential - // blowup. - self.current_limit.0 = self.default_limit.0 / 8; - } -} - -pub(in crate::solve) trait OverflowHandler<'tcx> { - fn search_graph(&mut self) -> &mut SearchGraph<'tcx>; - - fn repeat_while_none<T>( - &mut self, - on_overflow: impl FnOnce(&mut Self) -> Result<T, NoSolution>, - mut loop_body: impl FnMut(&mut Self) -> Option<Result<T, NoSolution>>, - ) -> Result<T, NoSolution> { - let start_depth = self.search_graph().overflow_data.additional_depth; - let depth = self.search_graph().stack.len(); - while !self.search_graph().overflow_data.has_overflow(depth) { - if let Some(result) = loop_body(self) { - self.search_graph().overflow_data.additional_depth = start_depth; - return result; - } - - self.search_graph().overflow_data.additional_depth += 1; - } - self.search_graph().overflow_data.additional_depth = start_depth; - self.search_graph().overflow_data.deal_with_overflow(); - on_overflow(self) - } - - // Increment the `additional_depth` by one and evaluate `body`, or `on_overflow` - // if the depth is overflown. - fn with_incremented_depth<T>( - &mut self, - on_overflow: impl FnOnce(&mut Self) -> T, - body: impl FnOnce(&mut Self) -> T, - ) -> T { - let depth = self.search_graph().stack.len(); - self.search_graph().overflow_data.additional_depth += 1; - - let result = if self.search_graph().overflow_data.has_overflow(depth) { - self.search_graph().overflow_data.deal_with_overflow(); - on_overflow(self) - } else { - body(self) - }; - - self.search_graph().overflow_data.additional_depth -= 1; - result - } -} - -impl<'tcx> OverflowHandler<'tcx> for EvalCtxt<'_, 'tcx> { - fn search_graph(&mut self) -> &mut SearchGraph<'tcx> { - &mut self.search_graph - } -} - -impl<'tcx> OverflowHandler<'tcx> for SearchGraph<'tcx> { - fn search_graph(&mut self) -> &mut SearchGraph<'tcx> { - self - } -} - -impl<'tcx> SearchGraph<'tcx> { - pub fn deal_with_overflow( - &mut self, - tcx: TyCtxt<'tcx>, - goal: Canonical<'tcx, impl Sized>, - ) -> QueryResult<'tcx> { - self.overflow_data.deal_with_overflow(); - response_no_constraints(tcx, goal, Certainty::OVERFLOW) - } -} diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 14a5b9656e5..db80b62d8a2 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,7 +1,6 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. use super::assembly::{self, structural_traits}; -use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; @@ -444,7 +443,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { Err(NoSolution) => vec![], }; - ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { + ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| { let a_ty = goal.predicate.self_ty(); // We need to normalize the b_ty since it's matched structurally // in the other functions below. @@ -526,7 +525,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { b_region: ty::Region<'tcx>, ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { let tcx = self.tcx(); - let Goal { predicate: (a_ty, b_ty), .. } = goal; + let Goal { predicate: (a_ty, _b_ty), .. } = goal; // All of a's auto traits need to be in b's auto traits. let auto_traits_compatible = @@ -535,51 +534,30 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return vec![]; } - // Try to match `a_ty` against `b_ty`, replacing `a_ty`'s principal trait ref with - // the supertrait principal and subtyping the types. - let unsize_dyn_to_principal = - |ecx: &mut Self, principal: Option<ty::PolyExistentialTraitRef<'tcx>>| { - ecx.probe_candidate("upcast dyn to principle").enter( - |ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(goal.param_env, new_a_ty, b_ty)?; - ecx.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_region, b_region))); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }, - ) - }; - let mut responses = vec![]; // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(resp) = unsize_dyn_to_principal(self, a_data.principal()) { + if let Ok(resp) = self.consider_builtin_upcast_to_principal( + goal, + a_data, + a_region, + b_data, + b_region, + a_data.principal(), + ) { responses.push((resp, BuiltinImplSource::Misc)); } } else if let Some(a_principal) = a_data.principal() { self.walk_vtable( a_principal.with_self_ty(tcx, a_ty), |ecx, new_a_principal, _, vtable_vptr_slot| { - if let Ok(resp) = unsize_dyn_to_principal( - ecx, + if let Ok(resp) = ecx.consider_builtin_upcast_to_principal( + goal, + a_data, + a_region, + b_data, + b_region, Some(new_a_principal.map_bound(|trait_ref| { ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) })), @@ -631,6 +609,83 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + fn consider_builtin_upcast_to_principal( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + a_region: ty::Region<'tcx>, + b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + b_region: ty::Region<'tcx>, + upcast_principal: Option<ty::PolyExistentialTraitRef<'tcx>>, + ) -> QueryResult<'tcx> { + let param_env = goal.param_env; + + // More than one projection in a_ty's bounds may match the projection + // in b_ty's bound. Use this to first determine *which* apply without + // having any inference side-effects. We process obligations because + // unification may initially succeed due to deferred projection equality. + let projection_may_match = + |ecx: &mut Self, + source_projection: ty::PolyExistentialProjection<'tcx>, + target_projection: ty::PolyExistentialProjection<'tcx>| { + source_projection.item_def_id() == target_projection.item_def_id() + && ecx + .probe(|_| CandidateKind::UpcastProbe) + .enter(|ecx| -> Result<(), NoSolution> { + ecx.eq(param_env, source_projection, target_projection)?; + let _ = ecx.try_evaluate_added_goals()?; + Ok(()) + }) + .is_ok() + }; + + for bound in b_data { + match bound.skip_binder() { + // Check that a's supertrait (upcast_principal) is compatible + // with the target (b_ty). + ty::ExistentialPredicate::Trait(target_principal) => { + self.eq(param_env, upcast_principal.unwrap(), bound.rebind(target_principal))?; + } + // Check that b_ty's projection is satisfied by exactly one of + // a_ty's projections. First, we look through the list to see if + // any match. If not, error. Then, if *more* than one matches, we + // return ambiguity. Otherwise, if exactly one matches, equate + // it with b_ty's projection. + ty::ExistentialPredicate::Projection(target_projection) => { + let target_projection = bound.rebind(target_projection); + let mut matching_projections = + a_data.projection_bounds().filter(|source_projection| { + projection_may_match(self, *source_projection, target_projection) + }); + let Some(source_projection) = matching_projections.next() else { + return Err(NoSolution); + }; + if matching_projections.next().is_some() { + return self.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + self.eq(param_env, source_projection, target_projection)?; + } + // Check that b_ty's auto traits are present in a_ty's bounds. + ty::ExistentialPredicate::AutoTrait(def_id) => { + if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) { + return Err(NoSolution); + } + } + } + } + + // Also require that a_ty's lifetime outlives b_ty's lifetime. + self.add_goal(Goal::new( + self.tcx(), + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + /// We have the following builtin impls for arrays: /// ```ignore (builtin impl example) /// impl<T: ?Sized, const N: usize> Unsize<[T]> for [T; N] {} @@ -857,12 +912,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ecx.add_goals( constituent_tys(ecx, goal.predicate.self_ty())? .into_iter() - .map(|ty| { - goal.with( - ecx.tcx(), - ty::Binder::dummy(goal.predicate.with_self_ty(ecx.tcx(), ty)), - ) - }) + .map(|ty| goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty))) .collect::<Vec<_>>(), ); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -879,7 +929,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } /// Normalize a non-self type when it is structually matched on when solving - /// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty` + /// a built-in goal. + /// + /// This is handled already through `assemble_candidates_after_normalizing_self_ty` /// for the self type, but for other goals, additional normalization of other /// arguments may be needed to completely implement the semantics of the trait. /// @@ -894,30 +946,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return Ok(Some(ty)); } - self.repeat_while_none( - |_| Ok(None), - |ecx| { - let ty::Alias(_, projection_ty) = *ty.kind() else { - return Some(Ok(Some(ty))); - }; - - let normalized_ty = ecx.next_ty_infer(); - let normalizes_to_goal = Goal::new( - ecx.tcx(), - param_env, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), - ); - ecx.add_goal(normalizes_to_goal); - if let Err(err) = ecx.try_evaluate_added_goals() { - return Some(Err(err)); - } + for _ in 0..self.local_overflow_limit() { + let ty::Alias(_, projection_ty) = *ty.kind() else { + return Ok(Some(ty)); + }; - ty = ecx.resolve_vars_if_possible(normalized_ty); - None - }, - ) + let normalized_ty = self.next_ty_infer(); + let normalizes_to_goal = Goal::new( + self.tcx(), + param_env, + ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, + ); + self.add_goal(normalizes_to_goal); + self.try_evaluate_added_goals()?; + ty = self.resolve_vars_if_possible(normalized_ty); + } + + Ok(None) } } diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 94d3cd035aa..ba5000da6cd 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -97,7 +97,6 @@ impl<'tcx> AutoTraitFinder<'tcx> { orig_env, ty::TraitPredicate { trait_ref, - constness: ty::BoundConstness::NotConst, polarity: if polarity { ImplPolarity::Positive } else { @@ -260,7 +259,6 @@ impl<'tcx> AutoTraitFinder<'tcx> { predicates.push_back(ty::Binder::dummy(ty::TraitPredicate { trait_ref: ty::TraitRef::new(infcx.tcx, trait_did, [ty]), - constness: ty::BoundConstness::NotConst, // Auto traits are positive polarity: ty::ImplPolarity::Positive, })); diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 61f693e1bd1..820973dc090 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -97,7 +97,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { cause, recursion_depth: 0, param_env, - predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx), + predicate: ty::Binder::dummy(trait_ref).to_predicate(tcx), }); } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 14020c05251..4d85e2b6089 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -100,7 +100,6 @@ pub trait InferCtxtExt<'tcx> { &self, param_env: ty::ParamEnv<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>, - constness: ty::BoundConstness, polarity: ty::ImplPolarity, ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>; } @@ -356,7 +355,6 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { &self, param_env: ty::ParamEnv<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>, - constness: ty::BoundConstness, polarity: ty::ImplPolarity, ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { self.commit_if_ok(|_| { @@ -372,12 +370,13 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { span: DUMMY_SP, kind: TypeVariableOriginKind::MiscVariable, }); + // FIXME(effects) let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); let obligation = Obligation::new( self.tcx, ObligationCause::dummy(), param_env, - ty.rebind(ty::TraitPredicate { trait_ref, constness, polarity }), + ty.rebind(ty::TraitPredicate { trait_ref, polarity }), ); let ocx = ObligationCtxt::new(self); ocx.register_obligation(obligation); @@ -689,8 +688,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let trait_predicate = bound_predicate.rebind(trait_predicate); let trait_predicate = self.resolve_vars_if_possible(trait_predicate); - let predicate_is_const = ty::BoundConstness::ConstIfConst - == trait_predicate.skip_binder().constness; + // FIXME(effects) + let predicate_is_const = false; if self.tcx.sess.has_errors().is_some() && trait_predicate.references_error() @@ -1909,9 +1908,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .all_impls(trait_pred.def_id()) .filter_map(|def_id| { if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative - || !trait_pred - .skip_binder() - .is_constness_satisfied_by(self.tcx.constness(def_id)) || !self.tcx.is_user_visible_dep(def_id.krate) { return None; @@ -2991,12 +2987,19 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { unsatisfied_const: bool, ) { let body_def_id = obligation.cause.body_id; + let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } = + obligation.cause.code() + { + *rhs_span + } else { + span + }; + // Try to report a help message if is_fn_trait && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( obligation.param_env, trait_ref.self_ty(), - trait_predicate.skip_binder().constness, trait_predicate.skip_binder().polarity, ) { @@ -3030,8 +3033,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.report_similar_impl_candidates_for_root_obligation(&obligation, *trait_predicate, body_def_id, err); } - self.maybe_suggest_convert_to_slice( + self.suggest_convert_to_slice( err, + obligation, trait_ref, impl_candidates.as_slice(), span, @@ -3098,14 +3102,15 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn maybe_add_note_for_unsatisfied_const( &self, - obligation: &PredicateObligation<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - trait_predicate: &ty::PolyTraitPredicate<'tcx>, - err: &mut Diagnostic, - span: Span, + _obligation: &PredicateObligation<'tcx>, + _trait_ref: ty::PolyTraitRef<'tcx>, + _trait_predicate: &ty::PolyTraitPredicate<'tcx>, + _err: &mut Diagnostic, + _span: Span, ) -> UnsatisfiedConst { - let mut unsatisfied_const = UnsatisfiedConst(false); - if trait_predicate.is_const_if_const() { + let unsatisfied_const = UnsatisfiedConst(false); + // FIXME(effects) + /* if trait_predicate.is_const_if_const() { let non_const_predicate = trait_ref.without_const(); let non_const_obligation = Obligation { cause: obligation.cause.clone(), @@ -3125,7 +3130,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ), ); } - } + } */ unsatisfied_const } 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 e62be1adbca..2b8571796df 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -398,9 +398,10 @@ pub trait TypeErrCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>; - fn maybe_suggest_convert_to_slice( + fn suggest_convert_to_slice( &self, err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, candidate_impls: &[ImplCandidate<'tcx>], span: Span, @@ -2786,7 +2787,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // implement this trait and list them. err.note(format!( "`{short_item_name}` is a \"sealed trait\", because to implement \ - it you also need to implelement `{}`, which is not accessible; \ + it you also need to implement `{}`, which is not accessible; \ this is usually done to force you to use one of the provided \ types that already implement it", with_no_trimmed_paths!(tcx.def_path_str(def_id)), @@ -2851,7 +2852,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note("all local variables must have a statically known size"); } Some(Node::Local(hir::Local { - init: Some(hir::Expr { kind: hir::ExprKind::Index(_, _), span, .. }), + init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }), .. })) => { // When encountering an assignment of an unsized trait, like @@ -3942,13 +3943,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { /// If the type that failed selection is an array or a reference to an array, /// but the trait is implemented for slices, suggest that the user converts /// the array into a slice. - fn maybe_suggest_convert_to_slice( + fn suggest_convert_to_slice( &self, err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, candidate_impls: &[ImplCandidate<'tcx>], span: Span, ) { + // We can only suggest the slice coersion for function and binary operation arguments, + // since the suggestion would make no sense in turbofish or call + let (ObligationCauseCode::BinOp { .. } + | ObligationCauseCode::FunctionArgumentObligation { .. }) = obligation.cause.code() + else { + return; + }; + // Three cases where we can make a suggestion: // 1. `[T; _]` (array of T) // 2. `&[T; _]` (reference to array of T) diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 593f669e9bb..d2210c6d5d9 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -133,7 +133,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>( def_id: DefId, ) -> bool { let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]); - pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref.without_const()) + pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref) } /// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 76752edc4ae..5823b4508d9 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -574,7 +574,6 @@ fn virtual_call_violation_for_method<'tcx>( // implement auto traits if the underlying type does as well. if let ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref: pred_trait_ref, - constness: ty::BoundConstness::NotConst, polarity: ty::ImplPolarity::Positive, }) = pred.kind().skip_binder() && pred_trait_ref.self_ty() == tcx.types.self_param diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 98f45826727..3d800421b76 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1306,7 +1306,7 @@ fn normalize_to_error<'a, 'tcx>( cause, recursion_depth: depth, param_env, - predicate: trait_ref.without_const().to_predicate(selcx.tcx()), + predicate: trait_ref.to_predicate(selcx.tcx()), }; let tcx = selcx.infcx.tcx; let new_value = selcx.infcx.next_ty_var(TypeVariableOrigin { @@ -1867,8 +1867,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( if selcx.infcx.predicate_must_hold_modulo_regions( &obligation.with( selcx.tcx(), - ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]) - .without_const(), + ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]), ), ) => { @@ -2152,8 +2151,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>( LangItem::Sized, obligation.cause.span(), [self_ty], - ) - .without_const(); + ); obligations.push(obligation.with(tcx, sized_predicate)); } (metadata_ty.into(), obligations) diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index ef989d8c9d6..9484a50e3a9 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -291,9 +291,9 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( return Err(NoSolution); } - constraints.outlives.extend( - args.as_generator().upvar_tys().map(|t| -> ty::GenericArg<'tcx> { t.into() }), - ); + constraints + .outlives + .extend(args.as_generator().upvar_tys().iter().map(ty::GenericArg::from)); constraints.outlives.push(args.as_generator().resume_ty().into()); } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index 98894263374..59f4a22ac75 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -31,16 +31,6 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> { - // Subtle: note that we are not invoking - // `infcx.at(...).dropck_outlives(...)` here, but rather the - // underlying `dropck_outlives` query. This same underlying - // query is also used by the - // `infcx.at(...).dropck_outlives(...)` fn. Avoiding the - // wrapper means we don't need an infcx in this code, which is - // good because the interface doesn't give us one (so that we - // know we are not registering any subregion relations or - // other things). - // FIXME convert to the type expected by the `dropck_outlives` // query. This should eventually be fixed by changing the // *underlying query*. 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 b9f31be25b1..e3da87a2210 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -154,9 +154,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .infcx .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation)); - candidates - .vec - .extend(result.into_iter().map(|(idx, constness)| ProjectionCandidate(idx, constness))); + // FIXME(effects) proper constness needed? + candidates.vec.extend( + result.into_iter().map(|idx| ProjectionCandidate(idx, ty::BoundConstness::NotConst)), + ); } /// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller @@ -744,7 +745,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(ref data_a, _, ty::Dyn), &ty::Dynamic(ref data_b, _, ty::Dyn)) => { + ( + &ty::Dynamic(ref a_data, a_region, ty::Dyn), + &ty::Dynamic(ref b_data, b_region, ty::Dyn), + ) => { // Upcast coercions permit several things: // // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` @@ -756,19 +760,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // // We always perform upcasting coercions when we can because of reason // #2 (region bounds). - let auto_traits_compatible = data_b + let auto_traits_compatible = b_data .auto_traits() // All of a's auto traits need to be in b's auto traits. - .all(|b| data_a.auto_traits().any(|a| a == b)); + .all(|b| a_data.auto_traits().any(|a| a == b)); if auto_traits_compatible { - let principal_def_id_a = data_a.principal_def_id(); - let principal_def_id_b = data_b.principal_def_id(); + let principal_def_id_a = a_data.principal_def_id(); + let principal_def_id_b = b_data.principal_def_id(); if principal_def_id_a == principal_def_id_b { // no cyclic candidates.vec.push(BuiltinUnsizeCandidate); } else if principal_def_id_a.is_some() && principal_def_id_b.is_some() { // not casual unsizing, now check whether this is trait upcasting coercion. - let principal_a = data_a.principal().unwrap(); + let principal_a = a_data.principal().unwrap(); let target_trait_did = principal_def_id_b.unwrap(); let source_trait_ref = principal_a.with_self_ty(self.tcx(), source); if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object( @@ -784,9 +788,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for (idx, upcast_trait_ref) in util::supertraits(self.tcx(), source_trait_ref).enumerate() { - if upcast_trait_ref.def_id() == target_trait_did { - candidates.vec.push(TraitUpcastingUnsizeCandidate(idx)); - } + self.infcx.probe(|_| { + if upcast_trait_ref.def_id() == target_trait_did + && let Ok(nested) = self.match_upcast_principal( + obligation, + upcast_trait_ref, + a_data, + b_data, + a_region, + b_region, + ) + { + if nested.is_none() { + candidates.ambiguous = true; + } + candidates.vec.push(TraitUpcastingUnsizeCandidate(idx)); + } + }) } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 7140fedb74a..71a82baeec6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -59,7 +59,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ParamCandidate(param) => { let obligations = self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref)); - ImplSource::Param(param.skip_binder().constness, obligations) + // FIXME(effects) + ImplSource::Param(ty::BoundConstness::NotConst, obligations) } ImplCandidate(impl_def_id) => { @@ -128,14 +129,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { subobligation.set_depth_from_parent(obligation.recursion_depth); } - if !obligation.predicate.is_const_if_const() { - // normalize nested predicates according to parent predicate's constness. - impl_src = impl_src.map(|mut o| { - o.predicate = o.predicate.without_const(self.tcx()); - o - }); - } - Ok(impl_src) } @@ -886,68 +879,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `assemble_candidates_for_unsizing` should ensure there are no late-bound // regions here. See the comment there for more details. - let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); - let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); - let target = self.infcx.shallow_resolve(target); - - debug!(?source, ?target, "confirm_trait_upcasting_unsize_candidate"); - - let mut nested = vec![]; - let source_trait_ref; - let upcast_trait_ref; - match (source.kind(), target.kind()) { - // TraitA+Kx+'a -> TraitB+Ky+'b (trait upcasting coercion). - ( - &ty::Dynamic(ref data_a, r_a, repr_a @ ty::Dyn), - &ty::Dynamic(ref data_b, r_b, ty::Dyn), - ) => { - // See `assemble_candidates_for_unsizing` for more info. - // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. - let principal_a = data_a.principal().unwrap(); - source_trait_ref = principal_a.with_self_ty(tcx, source); - upcast_trait_ref = util::supertraits(tcx, source_trait_ref).nth(idx).unwrap(); - assert_eq!(data_b.principal_def_id(), Some(upcast_trait_ref.def_id())); - let existential_predicate = upcast_trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( - tcx, trait_ref, - )) - }); - let iter = Some(existential_predicate) - .into_iter() - .chain( - data_a - .projection_bounds() - .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)), - ) - .chain( - data_b - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter); - let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, repr_a); - - // Require that the traits involved in this upcast are **equal**; - // only the **lifetime bound** is changed. - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, target, source_trait) - .map_err(|_| Unimplemented)?; - nested.extend(obligations); - - let outlives = ty::OutlivesPredicate(r_a, r_b); - nested.push(Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - obligation.predicate.rebind(outlives), - )); - } - _ => bug!(), - }; + let predicate = obligation.predicate.no_bound_vars().unwrap(); + let a_ty = self.infcx.shallow_resolve(predicate.self_ty()); + let b_ty = self.infcx.shallow_resolve(predicate.trait_ref.args.type_at(1)); + + let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { bug!() }; + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { bug!() }; + + let source_principal = a_data.principal().unwrap().with_self_ty(tcx, a_ty); + let unnormalized_upcast_principal = + util::supertraits(tcx, source_principal).nth(idx).unwrap(); + + let nested = self + .match_upcast_principal( + obligation, + unnormalized_upcast_principal, + a_data, + b_data, + a_region, + b_region, + )? + .expect("did not expect ambiguity during confirmation"); let vtable_segment_callback = { let mut vptr_offset = 0; @@ -958,7 +910,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { vptr_offset += count_own_vtable_entries(tcx, trait_ref); - if trait_ref == upcast_trait_ref { + if trait_ref == unnormalized_upcast_principal { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); } else { @@ -976,7 +928,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; let vtable_vptr_slot = - prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap(); + prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap(); Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested)) } @@ -994,9 +946,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let target = self.infcx.shallow_resolve(target); debug!(?source, ?target, "confirm_builtin_unsize_candidate"); - let mut nested = vec![]; - let src; - match (source.kind(), target.kind()) { + Ok(match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). (&ty::Dynamic(ref data_a, r_a, dyn_a), &ty::Dynamic(ref data_b, r_b, dyn_b)) if dyn_a == dyn_b => @@ -1023,16 +973,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. - let InferOk { obligations, .. } = self + let InferOk { mut obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) .sup(DefineOpaqueTypes::No, target, source_trait) .map_err(|_| Unimplemented)?; - nested.extend(obligations); // Register one obligation for 'a: 'b. let outlives = ty::OutlivesPredicate(r_a, r_b); - nested.push(Obligation::with_depth( + obligations.push(Obligation::with_depth( tcx, obligation.cause.clone(), obligation.recursion_depth + 1, @@ -1040,7 +989,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.predicate.rebind(outlives), )); - src = BuiltinImplSource::Misc; + ImplSource::Builtin(BuiltinImplSource::Misc, obligations) } // `T` -> `Trait` @@ -1066,11 +1015,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // words, if the object type is `Foo + Send`, this would create an obligation for // the `Send` check.) // - Projection predicates - nested.extend( - data.iter().map(|predicate| { - predicate_to_obligation(predicate.with_self_ty(tcx, source)) - }), - ); + let mut nested: Vec<_> = data + .iter() + .map(|predicate| predicate_to_obligation(predicate.with_self_ty(tcx, source))) + .collect(); // We can only make objects from sized types. let tr = ty::TraitRef::from_lang_item( @@ -1088,7 +1036,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx), )); - src = BuiltinImplSource::Misc; + ImplSource::Builtin(BuiltinImplSource::Misc, nested) } // `[T; n]` -> `[T]` @@ -1098,9 +1046,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .at(&obligation.cause, obligation.param_env) .eq(DefineOpaqueTypes::No, b, a) .map_err(|_| Unimplemented)?; - nested.extend(obligations); - src = BuiltinImplSource::Misc; + ImplSource::Builtin(BuiltinImplSource::Misc, obligations) } // `Struct<T>` -> `Struct<U>` @@ -1113,6 +1060,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tail_field = def.non_enum_variant().tail(); let tail_field_ty = tcx.type_of(tail_field.did); + let mut nested = vec![]; + // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`, // normalizing in the process, since `type_of` returns something directly from // astconv (which means it's un-normalized). @@ -1158,7 +1107,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); nested.push(tail_unsize_obligation); - src = BuiltinImplSource::Misc; + ImplSource::Builtin(BuiltinImplSource::Misc, nested) } // `(.., T)` -> `(.., U)` @@ -1173,27 +1122,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // last element is equal to the target. let new_tuple = Ty::new_tup_from_iter(tcx, a_mid.iter().copied().chain(iter::once(b_last))); - let InferOk { obligations, .. } = self + let InferOk { mut obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) .eq(DefineOpaqueTypes::No, target, new_tuple) .map_err(|_| Unimplemented)?; - nested.extend(obligations); // Add a nested `T: Unsize<U>` predicate. let last_unsize_obligation = obligation.with( tcx, ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]), ); - nested.push(last_unsize_obligation); + obligations.push(last_unsize_obligation); - src = BuiltinImplSource::TupleUnsizing; + ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, obligations) } _ => bug!("source: {source}, target: {target}"), - }; - - Ok(ImplSource::Builtin(src, nested)) + }) } fn confirm_const_destruct_candidate( @@ -1305,6 +1251,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If we have a projection type, make sure to normalize it so we replace it // with a fresh infer variable ty::Alias(ty::Projection | ty::Inherent, ..) => { + // FIXME(effects) this needs constness let predicate = normalize_with_depth_to( self, obligation.param_env, @@ -1317,7 +1264,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause.span, [nested_ty], ), - constness: ty::BoundConstness::ConstIfConst, polarity: ty::ImplPolarity::Positive, }), &mut nested, @@ -1336,6 +1282,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // since it's either not `const Drop` (and we raise an error during selection), // or it's an ADT (and we need to check for a custom impl during selection) _ => { + // FIXME(effects) this needs constness let predicate = self_ty.rebind(ty::TraitPredicate { trait_ref: ty::TraitRef::from_lang_item( self.tcx(), @@ -1343,7 +1290,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause.span, [nested_ty], ), - constness: ty::BoundConstness::ConstIfConst, polarity: ty::ImplPolarity::Positive, }); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c29696bc817..f1bd9f8b71a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1583,7 +1583,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn match_projection_obligation_against_definition_bounds( &mut self, obligation: &PolyTraitObligation<'tcx>, - ) -> smallvec::SmallVec<[(usize, ty::BoundConstness); 2]> { + ) -> smallvec::SmallVec<[usize; 2]> { let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate); let placeholder_trait_predicate = self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate); @@ -1632,7 +1632,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => false, } }) { - return Some((idx, pred.constness)); + return Some(idx); } } None @@ -1820,7 +1820,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { (ParamCandidate(other), ParamCandidate(victim)) => { let same_except_bound_vars = other.skip_binder().trait_ref == victim.skip_binder().trait_ref - && other.skip_binder().constness == victim.skip_binder().constness && other.skip_binder().polarity == victim.skip_binder().polarity && !other.skip_binder().trait_ref.has_escaping_bound_vars(); if same_except_bound_vars { @@ -1830,12 +1829,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // probably best characterized as a "hack", since we might prefer to just do our // best to *not* create essentially duplicate candidates in the first place. DropVictim::drop_if(other.bound_vars().len() <= victim.bound_vars().len()) - } else if other.skip_binder().trait_ref == victim.skip_binder().trait_ref - && victim.skip_binder().constness == ty::BoundConstness::NotConst - && other.skip_binder().polarity == victim.skip_binder().polarity - { - // Drop otherwise equivalent non-const candidates in favor of const candidates. - DropVictim::Yes } else { DropVictim::No } @@ -2099,7 +2092,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { Where( obligation .predicate - .rebind(sized_crit.iter_instantiated_copied(self.tcx(), args).collect()), + .rebind(sized_crit.iter_instantiated(self.tcx(), args).collect()), ) } @@ -2169,7 +2162,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let all = args .as_generator() .upvar_tys() - .chain(iter::once(args.as_generator().witness())) + .iter() + .chain([args.as_generator().witness()]) .collect::<Vec<_>>(); Where(obligation.predicate.rebind(all)) } @@ -2210,7 +2204,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // Not yet resolved. Ambiguous } else { - Where(obligation.predicate.rebind(args.as_closure().upvar_tys().collect())) + Where(obligation.predicate.rebind(args.as_closure().upvar_tys().to_vec())) } } @@ -2483,6 +2477,98 @@ impl<'tcx> SelectionContext<'_, 'tcx> { Ok(Normalized { value: impl_args, obligations: nested_obligations }) } + fn match_upcast_principal( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + unnormalized_upcast_principal: ty::PolyTraitRef<'tcx>, + a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + a_region: ty::Region<'tcx>, + b_region: ty::Region<'tcx>, + ) -> SelectionResult<'tcx, Vec<PredicateObligation<'tcx>>> { + let tcx = self.tcx(); + let mut nested = vec![]; + + let upcast_principal = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + unnormalized_upcast_principal, + &mut nested, + ); + + for bound in b_data { + match bound.skip_binder() { + // Check that a_ty's supertrait (upcast_principal) is compatible + // with the target (b_ty). + ty::ExistentialPredicate::Trait(target_principal) => { + nested.extend( + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup( + DefineOpaqueTypes::No, + upcast_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + }), + bound.rebind(target_principal), + ) + .map_err(|_| SelectionError::Unimplemented)? + .into_obligations(), + ); + } + // Check that b_ty's projection is satisfied by exactly one of + // a_ty's projections. First, we look through the list to see if + // any match. If not, error. Then, if *more* than one matches, we + // return ambiguity. Otherwise, if exactly one matches, equate + // it with b_ty's projection. + ty::ExistentialPredicate::Projection(target_projection) => { + let target_projection = bound.rebind(target_projection); + let mut matching_projections = + a_data.projection_bounds().filter(|source_projection| { + // Eager normalization means that we can just use can_eq + // here instead of equating and processing obligations. + source_projection.item_def_id() == target_projection.item_def_id() + && self.infcx.can_eq( + obligation.param_env, + *source_projection, + target_projection, + ) + }); + let Some(source_projection) = matching_projections.next() else { + return Err(SelectionError::Unimplemented); + }; + if matching_projections.next().is_some() { + return Ok(None); + } + nested.extend( + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::No, source_projection, target_projection) + .map_err(|_| SelectionError::Unimplemented)? + .into_obligations(), + ); + } + // Check that b_ty's auto traits are present in a_ty's bounds. + ty::ExistentialPredicate::AutoTrait(def_id) => { + if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) { + return Err(SelectionError::Unimplemented); + } + } + } + } + + nested.push(Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + + Ok(Some(nested)) + } + /// Normalize `where_clause_trait_ref` and try to match it against /// `obligation`. If successful, return any predicates that /// result from the normalization. diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 61e631c438d..729cf2f3313 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -500,16 +500,12 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti let mut pretty_predicates = Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); - for (mut p, _) in predicates { + for (p, _) in predicates { if let Some(poly_trait_ref) = p.as_trait_clause() { if Some(poly_trait_ref.def_id()) == sized_trait { types_without_default_bounds.remove(&poly_trait_ref.self_ty().skip_binder()); continue; } - - if ty::BoundConstness::ConstIfConst == poly_trait_ref.skip_binder().constness { - p = p.without_const(tcx); - } } pretty_predicates.push(p.to_string()); } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 4c27fe8b29b..a76272e9d09 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -101,7 +101,7 @@ impl<'tcx> TraitAliasExpander<'tcx> { fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { let tcx = self.tcx; let trait_ref = item.trait_ref(); - let pred = trait_ref.without_const().to_predicate(tcx); + let pred = trait_ref.to_predicate(tcx); debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); @@ -113,9 +113,13 @@ impl<'tcx> TraitAliasExpander<'tcx> { // Don't recurse if this trait alias is already on the stack for the DFS search. let anon_pred = anonymize_predicate(tcx, pred); - if item.path.iter().rev().skip(1).any(|&(tr, _)| { - anonymize_predicate(tcx, tr.without_const().to_predicate(tcx)) == anon_pred - }) { + if item + .path + .iter() + .rev() + .skip(1) + .any(|&(tr, _)| anonymize_predicate(tcx, tr.to_predicate(tcx)) == anon_pred) + { return false; } diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 3964dda1fdb..427ac368432 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -86,7 +86,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( let mut emit_vptr_on_new_entry = false; let mut visited = PredicateSet::new(tcx); - let predicate = trait_ref.without_const().to_predicate(tcx); + let predicate = trait_ref.to_predicate(tcx); let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> = smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))]; visited.insert(predicate); diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 5f6bb04fda4..f26310665f9 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -348,11 +348,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } // if the trait predicate is not const, the wf obligations should not be const as well. - let obligations = if trait_pred.constness == ty::BoundConstness::NotConst { - self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.args) - } else { - self.nominal_obligations(trait_ref.def_id, trait_ref.args) - }; + let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.args); debug!("compute_trait_pred obligations {:?}", obligations); let param_env = self.param_env; @@ -445,8 +441,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // `i32: Clone` // `i32: Copy` // ] - // Projection types do not require const predicates. - let obligations = self.nominal_obligations_without_const(data.def_id, data.args); + let obligations = self.nominal_obligations(data.def_id, data.args); self.out.extend(obligations); self.compute_projection_args(data.args); @@ -472,8 +467,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.recursion_depth, &mut self.out, ); - // Inherent projection types do not require const predicates. - let obligations = self.nominal_obligations_without_const(data.def_id, args); + let obligations = self.nominal_obligations(data.def_id, args); self.out.extend(obligations); } @@ -516,7 +510,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, self.recursion_depth, self.param_env, - ty::Binder::dummy(trait_ref).without_const(), + ty::Binder::dummy(trait_ref), )); } } @@ -667,7 +661,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } ty::FnDef(did, args) => { - let obligations = self.nominal_obligations_without_const(did, args); + let obligations = self.nominal_obligations(did, args); self.out.extend(obligations); } @@ -822,11 +816,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn nominal_obligations_inner( + fn nominal_obligations( &mut self, def_id: DefId, args: GenericArgsRef<'tcx>, - remap_constness: bool, ) -> Vec<traits::PredicateObligation<'tcx>> { let predicates = self.tcx().predicates_of(def_id); let mut origins = vec![def_id; predicates.predicates.len()]; @@ -841,16 +834,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { debug_assert_eq!(predicates.predicates.len(), origins.len()); iter::zip(predicates, origins.into_iter().rev()) - .map(|((mut pred, span), origin_def_id)| { + .map(|((pred, span), origin_def_id)| { let code = if span.is_dummy() { traits::ItemObligation(origin_def_id) } else { traits::BindingObligation(origin_def_id, span) }; let cause = self.cause(code); - if remap_constness { - pred = pred.without_const(self.tcx()); - } traits::Obligation::with_depth( self.tcx(), cause, @@ -863,22 +853,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .collect() } - fn nominal_obligations( - &mut self, - def_id: DefId, - args: GenericArgsRef<'tcx>, - ) -> Vec<traits::PredicateObligation<'tcx>> { - self.nominal_obligations_inner(def_id, args, false) - } - - fn nominal_obligations_without_const( - &mut self, - def_id: DefId, - args: GenericArgsRef<'tcx>, - ) -> Vec<traits::PredicateObligation<'tcx>> { - self.nominal_obligations_inner(def_id, args, true) - } - fn from_object_ty( &mut self, ty: Ty<'tcx>, diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 3500c2cc370..6b4273c03e4 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -317,7 +317,9 @@ fn layout_of_uncached<'tcx>( ty::Closure(_, ref args) => { let tys = args.as_closure().upvar_tys(); univariant( - &tys.map(|ty| Ok(cx.layout_of(ty)?.layout)).try_collect::<IndexVec<_, _>>()?, + &tys.iter() + .map(|ty| Ok(cx.layout_of(ty)?.layout)) + .try_collect::<IndexVec<_, _>>()?, &ReprOptions::default(), StructKind::AlwaysSized, )? @@ -729,7 +731,7 @@ fn generator_layout<'tcx>( // Build a prefix layout, including "promoting" all ineligible // locals as part of the prefix. We compute the layout of all of // these fields at once to get optimal packing. - let tag_index = args.as_generator().prefix_tys().count(); + let tag_index = args.as_generator().prefix_tys().len(); // `info.variant_fields` already accounts for the reserved variants, so no need to add them. let max_discr = (info.variant_fields.len() - 1) as u128; @@ -748,6 +750,7 @@ fn generator_layout<'tcx>( let prefix_layouts = args .as_generator() .prefix_tys() + .iter() .map(|ty| Ok(cx.layout_of(ty)?.layout)) .chain(iter::once(Ok(tag_layout))) .chain(promoted_layouts) @@ -1062,6 +1065,7 @@ fn variant_info_for_generator<'tcx>( let upvar_fields: Vec<_> = args .as_generator() .upvar_tys() + .iter() .zip(upvar_names) .enumerate() .map(|(field_idx, (_, name))| { diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index f89558a4599..34fd31e49e1 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -120,12 +120,16 @@ where _ if component.is_copy_modulo_regions(tcx, self.param_env) => (), ty::Closure(_, args) => { - queue_type(self, args.as_closure().tupled_upvars_ty()); + for upvar in args.as_closure().upvar_tys() { + queue_type(self, upvar); + } } ty::Generator(def_id, args, _) => { let args = args.as_generator(); - queue_type(self, args.tupled_upvars_ty()); + for upvar in args.upvar_tys() { + queue_type(self, upvar); + } let witness = args.witness(); let interior_tys = match witness.kind() { diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 791a1f4fa99..ba0258b63cb 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -42,7 +42,7 @@ fn sized_constraint_for_ty<'tcx>( let adt_tys = adt.sized_constraint(tcx); debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys); adt_tys - .iter_instantiated_copied(tcx, args) + .iter_instantiated(tcx, args) .flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty)) .collect() } @@ -58,11 +58,18 @@ fn sized_constraint_for_ty<'tcx>( // we know that `T` is Sized and do not need to check // it on the impl. - let Some(sized_trait) = tcx.lang_items().sized_trait() else { return vec![ty] }; - let sized_predicate = - ty::TraitRef::new(tcx, sized_trait, [ty]).without_const().to_predicate(tcx); + let Some(sized_trait_def_id) = tcx.lang_items().sized_trait() else { return vec![ty] }; let predicates = tcx.predicates_of(adtdef.did()).predicates; - if predicates.iter().any(|(p, _)| *p == sized_predicate) { vec![] } else { vec![ty] } + if predicates.iter().any(|(p, _)| { + p.as_trait_clause().is_some_and(|trait_pred| { + trait_pred.def_id() == sized_trait_def_id + && trait_pred.self_ty().skip_binder() == ty + }) + }) { + vec![] + } else { + vec![ty] + } } Placeholder(..) | Bound(..) | Infer(..) => { @@ -92,10 +99,13 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { /// - a tuple of type parameters or projections, if there are multiple /// such. /// - an Error, if a type is infinitely sized -fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> &[Ty<'_>] { +fn adt_sized_constraint<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> { if let Some(def_id) = def_id.as_local() { if matches!(tcx.representability(def_id), ty::Representability::Infinite) { - return tcx.mk_type_list(&[Ty::new_misc_error(tcx)]); + return ty::EarlyBinder::bind(tcx.mk_type_list(&[Ty::new_misc_error(tcx)])); } } let def = tcx.adt_def(def_id); @@ -107,7 +117,7 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> &[Ty<'_>] { debug!("adt_sized_constraint: {:?} => {:?}", def, result); - result + ty::EarlyBinder::bind(result) } /// See `ParamEnv` struct definition for details. diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index c2655d68b79..b0f8ea7a05d 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -6,6 +6,7 @@ #![feature(unwrap_infallible)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] +#![cfg_attr(not(bootstrap), allow(internal_features))] #[macro_use] extern crate bitflags; @@ -215,6 +216,11 @@ bitflags! { /// Does this have `ConstKind::Placeholder`? const HAS_CT_PLACEHOLDER = 1 << 8; + /// Does this have placeholders? + const HAS_PLACEHOLDER = TypeFlags::HAS_TY_PLACEHOLDER.bits + | TypeFlags::HAS_RE_PLACEHOLDER.bits + | TypeFlags::HAS_CT_PLACEHOLDER.bits; + /// `true` if there are "names" of regions and so forth /// that are local to a particular fn/inferctxt const HAS_FREE_LOCAL_REGIONS = 1 << 9; |
