diff options
1098 files changed, 17846 insertions, 7182 deletions
diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index c182f3245e5..d7a31c8e80f 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -42,7 +42,7 @@ jobs: # Exit with error if open and S-waiting-on-bors if [[ "$STATE" == "OPEN" && "$WAITING_ON_BORS" == "true" ]]; then - exit 1 + gh run cancel ${{ github.run_id }} fi update: @@ -65,7 +65,10 @@ jobs: - name: cargo update # Remove first line that always just says "Updating crates.io index" - run: cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log + # If there are no changes, cancel the job here + run: | + cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log + git status --porcelain | grep -q Cargo.lock || gh run cancel ${{ github.run_id }} - name: upload Cargo.lock artifact for use in PR uses: actions/upload-artifact@v3 with: @@ -131,7 +134,7 @@ jobs: # Exit with error if PR is closed STATE=$(gh pr view cargo_update --repo $GITHUB_REPOSITORY --json state --jq '.state') if [[ "$STATE" != "OPEN" ]]; then - exit 1 + gh run cancel ${{ github.run_id }} fi gh pr edit cargo_update --title "${PR_TITLE}" --body-file body.md --repo $GITHUB_REPOSITORY diff --git a/.mailmap b/.mailmap index f37ac7609e0..0d96f5f3d4f 100644 --- a/.mailmap +++ b/.mailmap @@ -259,6 +259,7 @@ James Hinshelwood <jameshinshelwood1@gmail.com> <james.hinshelwood@bigpayme.com> James Miller <bladeon@gmail.com> <james@aatch.net> James Perry <james.austin.perry@gmail.com> James Sanderson <zofrex@gmail.com> +Jan-Erik Rediger <janerik@fnordig.de> <badboy@archlinux.us> Jaro Fietz <jaro.fietz@gmx.de> Jason Fager <jfager@gmail.com> Jason Liquorish <jason@liquori.sh> <Bassetts@users.noreply.github.com> diff --git a/Cargo.lock b/Cargo.lock index 3110f32ade9..b8fe1ebaf80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,7 +214,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9792d37ca5173d7e7f4fe453739a0671d0557915a030a383d6b866476bbc3e71" dependencies = [ - "object", + "object 0.32.2", ] [[package]] @@ -281,7 +281,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.32.2", "rustc-demangle", ] @@ -574,7 +574,7 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clippy" -version = "0.1.78" +version = "0.1.79" dependencies = [ "anstream", "clippy_config", @@ -602,7 +602,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.78" +version = "0.1.79" dependencies = [ "rustc-semver", "serde", @@ -625,7 +625,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.78" +version = "0.1.79" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -650,7 +650,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.78" +version = "0.1.79" dependencies = [ "arrayvec", "clippy_config", @@ -971,7 +971,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" [[package]] name = "declare_clippy_lint" -version = "0.1.78" +version = "0.1.79" dependencies = [ "itertools 0.12.1", "quote", @@ -2205,7 +2205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets 0.48.5", ] [[package]] @@ -2636,11 +2636,22 @@ dependencies = [ "memchr", "rustc-std-workspace-alloc", "rustc-std-workspace-core", - "ruzstd", + "ruzstd 0.5.0", "wasmparser", ] [[package]] +name = "object" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7090bae93f8585aad99e595b7073c5de9ba89fbd6b4e9f0cdd7a10177273ac8" +dependencies = [ + "flate2", + "memchr", + "ruzstd 0.6.0", +] + +[[package]] name = "odht" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3323,6 +3334,7 @@ dependencies = [ name = "run_make_support" version = "0.0.0" dependencies = [ + "object 0.34.0", "wasmparser", ] @@ -3634,7 +3646,7 @@ dependencies = [ "itertools 0.12.1", "libc", "measureme", - "object", + "object 0.32.2", "rustc-demangle", "rustc_ast", "rustc_attr", @@ -3670,7 +3682,7 @@ dependencies = [ "itertools 0.12.1", "jobserver", "libc", - "object", + "object 0.32.2", "pathdiff", "regex", "rustc_arena", @@ -3686,6 +3698,7 @@ dependencies = [ "rustc_macros", "rustc_metadata", "rustc_middle", + "rustc_monomorphize", "rustc_query_system", "rustc_serialize", "rustc_session", @@ -4236,6 +4249,7 @@ name = "rustc_middle" version = "0.0.0" dependencies = [ "bitflags 2.4.2", + "derivative", "either", "field-offset", "gsgdt", @@ -4440,6 +4454,8 @@ dependencies = [ "rustc_target", "smallvec", "tracing", + "tracing-subscriber", + "tracing-tree", ] [[package]] @@ -4627,7 +4643,7 @@ name = "rustc_target" version = "0.0.0" dependencies = [ "bitflags 2.4.2", - "object", + "object 0.32.2", "rustc_abi", "rustc_data_structures", "rustc_feature", @@ -4895,6 +4911,17 @@ dependencies = [ ] [[package]] +name = "ruzstd" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5174a470eeb535a721ae9fdd6e291c2411a906b96592182d05217591d5c5cf7b" +dependencies = [ + "byteorder", + "derive_more", + "twox-hash", +] + +[[package]] name = "ryu" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5197,7 +5224,7 @@ dependencies = [ "hermit-abi", "libc", "miniz_oxide", - "object", + "object 0.32.2", "panic_abort", "panic_unwind", "profiler_builtins", @@ -5514,7 +5541,7 @@ checksum = "4db52ee8fec06e119b692ef3dd2c4cf621a99204c1b8c47407870ed050305b9b" dependencies = [ "gimli", "hashbrown", - "object", + "object 0.32.2", "tracing", ] diff --git a/Cargo.toml b/Cargo.toml index 5dd315ef2f7..e12c968e205 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,15 +66,6 @@ exclude = [ ] [profile.release.package.compiler_builtins] -# The compiler-builtins crate cannot reference libcore, and its own CI will -# verify that this is the case. This requires, however, that the crate is built -# without overflow checks and debug assertions. Forcefully disable debug -# assertions and overflow checks here which should ensure that even if these -# assertions are enabled for libstd we won't enable them for compiler_builtins -# which should ensure we still link everything correctly. -debug-assertions = false -overflow-checks = false - # For compiler-builtins we always use a high number of codegen units. # The goal here is to place every single intrinsic into its own object # file to avoid symbol clashes with the system libgcc if possible. Note diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index d0e8b86b71d..915cb386075 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -303,10 +303,6 @@ impl TraitBoundModifiers { }; } -/// The AST represents all type param bounds as types. -/// `typeck::collect::compute_bounds` matches these against -/// the "special" built-in traits (see `middle::lang_items`) and -/// detects `Copy`, `Send` and `Sync`. #[derive(Clone, Encodable, Decodable, Debug)] pub enum GenericBound { Trait(PolyTraitRef, TraitBoundModifiers), @@ -621,7 +617,9 @@ impl Pat { | PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)), // Trivial wrappers over inner patterns. - PatKind::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => s.walk(it), + PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => { + s.walk(it) + } // These patterns do not contain subpatterns, skip. PatKind::Wild @@ -792,6 +790,9 @@ pub enum PatKind { /// A `box` pattern. Box(P<Pat>), + /// A `deref` pattern (currently `deref!()` macro-based syntax). + Deref(P<Pat>), + /// A reference pattern (e.g., `&mut (a, b)`). Ref(P<Pat>, Mutability), @@ -1436,7 +1437,7 @@ pub enum ExprKind { /// `'label: loop { block }` Loop(P<Block>, Option<Label>, Span), /// A `match` block. - Match(P<Expr>, ThinVec<Arm>), + Match(P<Expr>, ThinVec<Arm>, MatchKind), /// A closure (e.g., `move |a, b, c| a + b + c`). Closure(Box<Closure>), /// A block (`'label: { ... }`). @@ -1761,6 +1762,15 @@ pub enum StrStyle { Raw(u8), } +/// The kind of match expression +#[derive(Clone, Copy, Encodable, Decodable, Debug, PartialEq)] +pub enum MatchKind { + /// match expr { ... } + Prefix, + /// expr.match { ... } + Postfix, +} + /// A literal in a meta item. #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] pub struct MetaItemLit { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 83468c5f101..7337b969242 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1295,6 +1295,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) { fields.flat_map_in_place(|field| vis.flat_map_pat_field(field)); } PatKind::Box(inner) => vis.visit_pat(inner), + PatKind::Deref(inner) => vis.visit_pat(inner), PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner), PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => { visit_opt(e1, |e| vis.visit_expr(e)); @@ -1424,7 +1425,7 @@ pub fn noop_visit_expr<T: MutVisitor>( visit_opt(label, |label| vis.visit_label(label)); vis.visit_span(span); } - ExprKind::Match(expr, arms) => { + ExprKind::Match(expr, arms, _kind) => { vis.visit_expr(expr); arms.flat_map_in_place(|arm| vis.flat_map_arm(arm)); } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index c17020ed663..f49eb2f22c5 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -105,7 +105,7 @@ impl Lit { } } - /// Keep this in sync with `Token::can_begin_literal_or_bool` excluding unary negation. + /// Keep this in sync with `Token::can_begin_literal_maybe_minus` excluding unary negation. pub fn from_token(token: &Token) -> Option<Lit> { match token.uninterpolate().kind { Ident(name, IdentIsRaw::No) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)), @@ -664,7 +664,7 @@ impl Token { } /// Returns `true` if the token is an interpolated path. - fn is_path(&self) -> bool { + fn is_whole_path(&self) -> bool { if let Interpolated(nt) = &self.kind && let NtPath(..) = &nt.0 { @@ -710,7 +710,7 @@ impl Token { pub fn is_path_start(&self) -> bool { self == &ModSep || self.is_qpath_start() - || self.is_path() + || self.is_whole_path() || self.is_path_segment_keyword() || self.is_ident() && !self.is_reserved_ident() } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index adc3056cc29..239735456ad 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -28,18 +28,7 @@ use smallvec::{smallvec, SmallVec}; use std::borrow::Cow; use std::{cmp, fmt, iter}; -/// When the main Rust parser encounters a syntax-extension invocation, it -/// parses the arguments to the invocation as a token tree. This is a very -/// loose structure, such that all sorts of different AST fragments can -/// be passed to syntax extensions using a uniform type. -/// -/// If the syntax extension is an MBE macro, it will attempt to match its -/// LHS token tree against the provided token tree, and if it finds a -/// match, will transcribe the RHS token tree, splicing in any captured -/// `macro_parser::matched_nonterminals` into the `SubstNt`s it finds. -/// -/// The RHS of an MBE macro is the only place `SubstNt`s are substituted. -/// Nothing special happens to misnamed or misplaced `SubstNt`s. +/// Part of a `TokenStream`. #[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] pub enum TokenTree { /// A single token. Should never be `OpenDelim` or `CloseDelim`, because diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 825f8dad8e4..18986fb7504 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -480,7 +480,7 @@ pub fn walk_use_tree<'a, V: Visitor<'a>>( try_visit!(visitor.visit_path(&use_tree.prefix, id)); match use_tree.kind { UseTreeKind::Simple(rename) => { - // The extra IDs are handled during HIR lowering. + // The extra IDs are handled during AST lowering. visit_opt!(visitor, visit_ident, rename); } UseTreeKind::Glob => {} @@ -576,7 +576,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res try_visit!(visitor.visit_path(path, pattern.id)); walk_list!(visitor, visit_pat_field, fields); } - PatKind::Box(subpattern) | PatKind::Ref(subpattern, _) | PatKind::Paren(subpattern) => { + PatKind::Box(subpattern) + | PatKind::Deref(subpattern) + | PatKind::Ref(subpattern, _) + | PatKind::Paren(subpattern) => { try_visit!(visitor.visit_pat(subpattern)); } PatKind::Ident(_, ident, optional_subpattern) => { @@ -920,7 +923,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V visit_opt!(visitor, visit_label, opt_label); try_visit!(visitor.visit_block(block)); } - ExprKind::Match(subexpression, arms) => { + ExprKind::Match(subexpression, arms, _kind) => { try_visit!(visitor.visit_expr(subexpression)); walk_list!(visitor, visit_arm, arms); } diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 11a66fe87c9..eef6e8280af 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -81,7 +81,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (self.arena.alloc_from_iter(stmts), expr) } - fn lower_local(&mut self, l: &Local) -> &'hir hir::Local<'hir> { + fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> { let ty = l .ty .as_ref() @@ -97,7 +97,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let span = self.lower_span(l.span); let source = hir::LocalSource::Normal; self.lower_attrs(hir_id, &l.attrs); - self.arena.alloc(hir::Local { hir_id, ty, pat, init, els, span, source }) + self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source }) } fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode { diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 4e1c477a3d7..e26a65c1f29 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -29,11 +29,12 @@ //! item id (`item_id`) in case of impl trait or path resolution id (`path_id`) otherwise. //! //! Since we do not have a proper way to obtain function type information by path resolution -//! in AST, we mark each function parameter type as `InferDelegation` and inherit it in `AstConv`. +//! in AST, we mark each function parameter type as `InferDelegation` and inherit it during +//! HIR ty lowering. //! //! Similarly generics, predicates and header are set to the "default" values. //! In case of discrepancy with callee function the `NotSupportedDelegation` error will -//! also be emitted in `AstConv`. +//! also be emitted during HIR ty lowering. use crate::{ImplTraitPosition, ResolverAstLoweringExt}; @@ -129,7 +130,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> &'hir hir::FnDecl<'hir> { let args_count = if let Some(local_sig_id) = sig_id.as_local() { // Map may be filled incorrectly due to recursive delegation. - // Error will be emmited later in astconv. + // Error will be emitted later during HIR ty lowering. self.resolver.fn_parameter_counts.get(&local_sig_id).cloned().unwrap_or_default() } else { self.tcx.fn_arg_names(sig_id).len() diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 41f7418ddde..389cf4e3132 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -157,7 +157,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::AddrOf(*k, *m, ohs) } ExprKind::Let(pat, scrutinee, span, is_recovered) => { - hir::ExprKind::Let(self.arena.alloc(hir::Let { + hir::ExprKind::Let(self.arena.alloc(hir::LetExpr { span: self.lower_span(*span), pat: self.lower_pat(pat), ty: None, @@ -181,10 +181,13 @@ impl<'hir> LoweringContext<'_, 'hir> { ) }), ExprKind::TryBlock(body) => self.lower_expr_try_block(body), - ExprKind::Match(expr, arms) => hir::ExprKind::Match( + ExprKind::Match(expr, arms, kind) => hir::ExprKind::Match( self.lower_expr(expr), self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))), - hir::MatchSource::Normal, + match kind { + MatchKind::Prefix => hir::MatchSource::Normal, + MatchKind::Postfix => hir::MatchSource::Postfix, + }, ), ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr), ExprKind::Closure(box Closure { diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 9078b1f889c..a1164008d0d 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -302,8 +302,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - fn visit_local(&mut self, l: &'hir Local<'hir>) { - self.insert(l.span, l.hir_id, Node::Local(l)); + fn visit_local(&mut self, l: &'hir LetStmt<'hir>) { + self.insert(l.span, l.hir_id, Node::LetStmt(l)); self.with_parent(l.hir_id, |this| { intravisit::walk_local(this, l); }) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 2d03c854bd0..c9786328565 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1427,8 +1427,8 @@ impl<'hir> LoweringContext<'_, 'hir> { // Error if `?Trait` bounds in where clauses don't refer directly to type parameters. // Note: we used to clone these bounds directly onto the type parameter (and avoid lowering // these into hir when we lower thee where clauses), but this makes it quite difficult to - // keep track of the Span info. Now, `add_implicitly_sized` in `AstConv` checks both param bounds and - // where clauses for `?Sized`. + // keep track of the Span info. Now, `<dyn HirTyLowerer>::add_implicit_sized_bound` + // checks both param bounds and where clauses for `?Sized`. for pred in &generics.where_clause.predicates { let WherePredicate::BoundPredicate(bound_pred) = pred else { continue; diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 6a7ee936f66..b5b98659e2f 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -55,7 +55,9 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxt, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_hir::{ConstArg, GenericArg, ItemLocalMap, ParamName, TraitCandidate}; +use rustc_hir::{ + ConstArg, GenericArg, ItemLocalMap, MissingLifetimeKind, ParamName, TraitCandidate, +}; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::extension; use rustc_middle::span_bug; @@ -797,7 +799,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { LifetimeRes::Param { .. } => { (hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit) } - LifetimeRes::Fresh { param, .. } => { + LifetimeRes::Fresh { param, kind, .. } => { // Late resolution delegates to us the creation of the `LocalDefId`. let _def_id = self.create_def( self.current_hir_id_owner.def_id, @@ -808,7 +810,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ); debug!(?_def_id); - (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided) + (hir::ParamName::Fresh, hir::LifetimeParamKind::Elided(kind)) } LifetimeRes::Static | LifetimeRes::Error => return None, res => panic!( @@ -1605,13 +1607,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { for lifetime in captured_lifetimes_to_duplicate { let res = self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error); - let old_def_id = match res { - LifetimeRes::Param { param: old_def_id, binder: _ } => old_def_id, + let (old_def_id, missing_kind) = match res { + LifetimeRes::Param { param: old_def_id, binder: _ } => (old_def_id, None), - LifetimeRes::Fresh { param, binder: _ } => { + LifetimeRes::Fresh { param, kind, .. } => { debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime); if let Some(old_def_id) = self.orig_opt_local_def_id(param) { - old_def_id + (old_def_id, Some(kind)) } else { self.dcx() .span_delayed_bug(lifetime.ident.span, "no def-id for fresh lifetime"); @@ -1651,6 +1653,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { duplicated_lifetime_node_id, duplicated_lifetime_def_id, self.lower_ident(lifetime.ident), + missing_kind, )); // Now make an arg that we can use for the generic params of the opaque tykind. @@ -1668,27 +1671,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { 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) - }; + let generic_params = + this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map( + |&(new_node_id, new_def_id, ident, missing_kind)| { + let hir_id = this.lower_node_id(new_node_id); + let (name, kind) = if ident.name == kw::UnderscoreLifetime { + ( + hir::ParamName::Fresh, + hir::LifetimeParamKind::Elided( + missing_kind.unwrap_or(MissingLifetimeKind::Underscore), + ), + ) + } 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, - } - }), - ); + 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 = self.arena.alloc_slice(&synthesized_lifetime_args); @@ -2332,7 +2341,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug_assert!(!a.is_empty()); self.attrs.insert(hir_id.local_id, a); } - let local = hir::Local { + let local = hir::LetStmt { hir_id, init, pat, diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 469cdac11e4..8631d90be81 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -91,6 +91,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { PatKind::Box(inner) => { break hir::PatKind::Box(self.lower_pat(inner)); } + PatKind::Deref(inner) => { + break hir::PatKind::Deref(self.lower_pat(inner)); + } PatKind::Ref(inner, mutbl) => { break hir::PatKind::Ref(self.lower_pat(inner), *mutbl); } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 2c396a64789..bb83f4c0f0e 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -564,6 +564,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(generic_const_items, "generic const items are experimental"); gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); + gate_all!(postfix_match, "postfix match is experimental"); if !visitor.features.never_patterns { if let Some(spans) = spans.get(&sym::never_patterns) { @@ -607,13 +608,13 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { }; } + gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); // Despite being a new feature, `where T: Trait<Assoc(): Sized>`, which is RTN syntax now, // used to be gated under associated_type_bounds, which are right above, so RTN needs to // be too. gate_all_legacy_dont_use!(return_type_notation, "return type notation is experimental"); gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental"); - gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); gate_all_legacy_dont_use!( exclusive_range_pattern, "exclusive range pattern syntax is experimental" diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index c50878e32a4..a70daf1b644 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1626,6 +1626,12 @@ impl<'a> State<'a> { self.word("box "); self.print_pat(inner); } + PatKind::Deref(inner) => { + self.word("deref!"); + self.popen(); + self.print_pat(inner); + self.pclose(); + } PatKind::Ref(inner, mutbl) => { self.word("&"); if mutbl.is_mut() { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 433ef03b6e5..8bd8b6ac144 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -1,6 +1,6 @@ use crate::pp::Breaks::Inconsistent; use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; -use ast::ForLoopKind; +use ast::{ForLoopKind, MatchKind}; use itertools::{Itertools, Position}; use rustc_ast::ptr::P; use rustc_ast::token; @@ -589,12 +589,22 @@ impl<'a> State<'a> { self.word_nbsp("loop"); self.print_block_with_attrs(blk, attrs); } - ast::ExprKind::Match(expr, arms) => { + ast::ExprKind::Match(expr, arms, match_kind) => { self.cbox(0); self.ibox(0); - self.word_nbsp("match"); - self.print_expr_as_cond(expr); - self.space(); + + match match_kind { + MatchKind::Prefix => { + self.word_nbsp("match"); + self.print_expr_as_cond(expr); + self.space(); + } + MatchKind::Postfix => { + self.print_expr_as_cond(expr); + self.word_nbsp(".match"); + } + } + self.bopen(); self.print_inner_attributes_no_trailing_hardbreak(attrs); for arm in arms { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 0109f188772..35bd7d37992 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -622,7 +622,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // FIXME: We make sure that this is a normal top-level binding, // but we could suggest `todo!()` for all uninitalized bindings in the pattern pattern - if let hir::StmtKind::Let(hir::Local { span, ty, init: None, pat, .. }) = + if let hir::StmtKind::Let(hir::LetStmt { span, ty, init: None, pat, .. }) = &ex.kind && let hir::PatKind::Binding(..) = pat.kind && span.contains(self.decl_span) @@ -716,7 +716,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .copied() .find_map(find_fn_kind_from_did), ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => tcx - .explicit_item_bounds(def_id) + .explicit_item_super_predicates(def_id) .iter_instantiated_copied(tcx, args) .find_map(|(clause, span)| find_fn_kind_from_did((clause, span))), ty::Closure(_, args) => match args.as_closure().kind() { @@ -800,7 +800,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { for (_, node) in tcx.hir().parent_iter(expr.hir_id) { let e = match node { hir::Node::Expr(e) => e, - hir::Node::Local(hir::Local { els: Some(els), .. }) => { + hir::Node::LetStmt(hir::LetStmt { els: Some(els), .. }) => { let mut finder = BreakFinder { found_breaks: vec![], found_continues: vec![] }; finder.visit_block(els); if !finder.found_breaks.is_empty() { @@ -2124,7 +2124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { hir::intravisit::walk_expr(self, e); } - fn visit_local(&mut self, local: &'hir hir::Local<'hir>) { + fn visit_local(&mut self, local: &'hir hir::LetStmt<'hir>) { if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat && let Some(init) = local.init diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 914f68a38b4..11561539f6d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -366,7 +366,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(variant.fields[field].name.to_string()) } ty::Tuple(_) => Some(field.index().to_string()), - ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { + ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => { self.describe_field_from_ty(ty, field, variant_index, including_tuple_field) } ty::Array(ty, _) | ty::Slice(ty) => { diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 36c2723b66d..2aeea1dd341 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -558,7 +558,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { hir::intravisit::walk_stmt(self, stmt); let expr = match stmt.kind { hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, - hir::StmtKind::Let(hir::Local { init: Some(expr), .. }) => expr, + hir::StmtKind::Let(hir::LetStmt { init: Some(expr), .. }) => expr, _ => { return; } @@ -737,7 +737,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { && let body = self.infcx.tcx.hir().body(body_id) && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(body).break_value() && let node = self.infcx.tcx.hir_node(hir_id) - && let hir::Node::Local(hir::Local { + && let hir::Node::LetStmt(hir::LetStmt { pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. }, .. }) @@ -1170,7 +1170,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if let Some(hir_id) = hir_id - && let hir::Node::Local(local) = self.infcx.tcx.hir_node(hir_id) + && let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id) { let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap()); if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait() diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index c06bf94a6fd..c92fccc959f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -503,10 +503,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ty::VarianceDiagInfo::None => {} ty::VarianceDiagInfo::Invariant { ty, param_index } => { let (desc, note) = match ty.kind() { - ty::RawPtr(ty_mut) => { - assert_eq!(ty_mut.mutbl, rustc_hir::Mutability::Mut); + ty::RawPtr(ty, mutbl) => { + assert_eq!(*mutbl, rustc_hir::Mutability::Mut); ( - format!("a mutable pointer to `{}`", ty_mut.ty), + format!("a mutable pointer to `{}`", ty), "mutable pointers are invariant over their type parameter".to_string(), ) } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 08199068020..cda61360404 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -555,8 +555,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { search_stack.push((*elem_ty, elem_hir_ty)); } - (ty::RawPtr(mut_ty), hir::TyKind::Ptr(mut_hir_ty)) => { - search_stack.push((mut_ty.ty, &mut_hir_ty.ty)); + (ty::RawPtr(mut_ty, _), hir::TyKind::Ptr(mut_hir_ty)) => { + search_stack.push((*mut_ty, &mut_hir_ty.ty)); } _ => { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 9f4f88b2b93..4a5ba441878 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1649,7 +1649,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -2284,8 +2284,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } } - ty::RawPtr(tnm) => { - match tnm.mutbl { + ty::RawPtr(_, mutbl) => { + match mutbl { // `*const` raw pointers are not mutable hir::Mutability::Not => Err(place), // `*mut` raw pointers are always mutable, regardless of diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index c3800a1f1f2..54c516c960c 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -2065,7 +2065,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { from_closure: constraint.from_closure, cause: ObligationCause::new(constraint.span, CRATE_DEF_ID, cause_code.clone()), variance_info: constraint.variance_info, - outlives_constraint: *constraint, }) .collect(); debug!("categorized_path={:#?}", categorized_path); @@ -2294,5 +2293,4 @@ pub struct BlameConstraint<'tcx> { pub from_closure: bool, pub cause: ObligationCause<'tcx>, pub variance_info: ty::VarianceDiagInfo<'tcx>, - pub outlives_constraint: OutlivesConstraint<'tcx>, } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index bea9be24028..d5875a226fe 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -192,6 +192,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { .find(|ur_vid| self.eval_equal(vid, **ur_vid)) .and_then(|ur_vid| self.definitions[*ur_vid].external_name) .unwrap_or(infcx.tcx.lifetimes.re_erased), + ty::RePlaceholder(_) => ty::Region::new_error_with_message( + infcx.tcx, + concrete_type.span, + "hidden type contains placeholders, we don't support higher kinded opaques yet", + ), _ => region, }); debug!(?universal_concrete_type); @@ -434,10 +439,6 @@ fn check_opaque_type_parameter_valid( // Only check the parent generics, which will ignore any of the // duplicated lifetime args that come from reifying late-bounds. for (i, arg) in opaque_type_key.args.iter().take(parent_generics.count()).enumerate() { - if let Err(guar) = arg.error_reported() { - return Err(guar); - } - let arg_is_param = match arg.unpack() { GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), GenericArgKind::Lifetime(lt) if is_ty_alias => { diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index a673c4c2aca..f28b786e4f7 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -82,7 +82,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ) { self.prove_predicate( ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Trait( - ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Positive }, + ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive }, ))), locations, category, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index acda2a7524c..a206aac0467 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2000,7 +2000,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::SizedBound, ); } - &Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {} + &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} Rvalue::ShallowInitBox(operand, ty) => { self.check_operand(operand, location); @@ -2157,15 +2157,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } CastKind::PointerCoercion(PointerCoercion::MutToConstPointer) => { - let ty::RawPtr(ty::TypeAndMut { ty: ty_from, mutbl: hir::Mutability::Mut }) = - op.ty(body, tcx).kind() + let ty::RawPtr(ty_from, hir::Mutability::Mut) = op.ty(body, tcx).kind() else { span_mirbug!(self, rvalue, "unexpected base type for cast {:?}", ty,); return; }; - let ty::RawPtr(ty::TypeAndMut { ty: ty_to, mutbl: hir::Mutability::Not }) = - ty.kind() - else { + let ty::RawPtr(ty_to, hir::Mutability::Not) = ty.kind() else { span_mirbug!(self, rvalue, "unexpected target type for cast {:?}", ty,); return; }; @@ -2190,12 +2187,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_from = op.ty(body, tcx); let opt_ty_elem_mut = match ty_from.kind() { - ty::RawPtr(ty::TypeAndMut { mutbl: array_mut, ty: array_ty }) => { - match array_ty.kind() { - ty::Array(ty_elem, _) => Some((ty_elem, *array_mut)), - _ => None, - } - } + ty::RawPtr(array_ty, array_mut) => match array_ty.kind() { + ty::Array(ty_elem, _) => Some((ty_elem, *array_mut)), + _ => None, + }, _ => None, }; @@ -2210,9 +2205,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { }; let (ty_to, ty_to_mut) = match ty.kind() { - ty::RawPtr(ty::TypeAndMut { mutbl: ty_to_mut, ty: ty_to }) => { - (ty_to, *ty_to_mut) - } + ty::RawPtr(ty_to, ty_to_mut) => (ty_to, *ty_to_mut), _ => { span_mirbug!( self, @@ -2413,7 +2406,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_left = left.ty(body, tcx); match ty_left.kind() { // Types with regions are comparable if they have a common super-type. - ty::RawPtr(_) | ty::FnPtr(_) => { + ty::RawPtr(_, _) | ty::FnPtr(_) => { let ty_right = right.ty(body, tcx); let common_ty = self.infcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index 56c56b2704b..92efeab08eb 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -245,7 +245,7 @@ impl<'cx, 'a> Context<'cx, 'a> { ExprKind::Let(_, local_expr, _, _) => { self.manage_cond_expr(local_expr); } - ExprKind::Match(local_expr, _) => { + ExprKind::Match(local_expr, ..) => { self.manage_cond_expr(local_expr); } ExprKind::MethodCall(call) => { diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 6eccd67f8af..60dbdf8b544 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -132,7 +132,7 @@ fn cs_partial_cmp( // Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354 if !tag_then_data - && let ExprKind::Match(_, arms) = &mut expr1.kind + && let ExprKind::Match(_, arms, _) = &mut expr1.kind && let Some(last) = arms.last_mut() && let PatKind::Wild = last.pat.kind { diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index b11a1b6cda1..03acd7f489f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; -use ast::EnumDef; -use rustc_ast::{self as ast, MetaItem}; +use rustc_ast::{self as ast, EnumDef, MetaItem}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; @@ -52,7 +51,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> let (ident, vdata, fields) = match substr.fields { Struct(vdata, fields) => (substr.type_ident, *vdata, fields), - EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields), + EnumMatching(_, v, fields) => (v.ident, &v.data, fields), AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr), EnumTag(..) | StaticStruct(..) | StaticEnum(..) => { cx.dcx().span_bug(span, "nonsensical .fields in `#[derive(Debug)]`") diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index 14d93a8cc23..d939f8c7aeb 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -226,7 +226,7 @@ fn encodable_substructure( BlockOrExpr::new_expr(expr) } - EnumMatching(idx, _, variant, fields) => { + EnumMatching(idx, variant, fields) => { // We're not generating an AST that the borrow checker is expecting, // so we need to generate a unique local variable to take the // mutable loan out on, otherwise we get conflicts which don't diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 3cb3e30daa7..afa73b672da 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -310,10 +310,10 @@ pub enum SubstructureFields<'a> { /// variants has any fields). AllFieldlessEnum(&'a ast::EnumDef), - /// Matching variants of the enum: variant index, variant count, ast::Variant, + /// Matching variants of the enum: variant index, ast::Variant, /// fields: the field name is only non-`None` in the case of a struct /// variant. - EnumMatching(usize, usize, &'a ast::Variant, Vec<FieldInfo>), + EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo>), /// The tag of an enum. The first field is a `FieldInfo` for the tags, as /// if they were fields. The second field is the expression to combine the @@ -1272,7 +1272,7 @@ impl<'a> MethodDef<'a> { trait_, type_ident, nonselflike_args, - &EnumMatching(0, 1, &variants[0], Vec::new()), + &EnumMatching(0, &variants[0], Vec::new()), ); } } @@ -1318,7 +1318,7 @@ impl<'a> MethodDef<'a> { // expressions for referencing every field of every // Self arg, assuming all are instances of VariantK. // Build up code associated with such a case. - let substructure = EnumMatching(index, variants.len(), variant, fields); + let substructure = EnumMatching(index, variant, fields); let arm_expr = self .call_substructure_method( cx, @@ -1346,7 +1346,7 @@ impl<'a> MethodDef<'a> { trait_, type_ident, nonselflike_args, - &EnumMatching(0, variants.len(), v, Vec::new()), + &EnumMatching(0, v, Vec::new()), ) .into_expr(cx, span), ) diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index f344dbcd10c..554dac0852f 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -49,7 +49,6 @@ mod log_syntax; mod source_util; mod test; mod trace_macros; -mod type_ascribe; mod util; pub mod asm; @@ -99,7 +98,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { std_panic: edition_panic::expand_panic, stringify: source_util::expand_stringify, trace_macros: trace_macros::expand_trace_macros, - type_ascribe: type_ascribe::expand_type_ascribe, unreachable: edition_panic::expand_unreachable, // tidy-alphabetical-end } diff --git a/compiler/rustc_builtin_macros/src/type_ascribe.rs b/compiler/rustc_builtin_macros/src/type_ascribe.rs deleted file mode 100644 index f3e66ffc759..00000000000 --- a/compiler/rustc_builtin_macros/src/type_ascribe.rs +++ /dev/null @@ -1,35 +0,0 @@ -use rustc_ast::ptr::P; -use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{token, Expr, ExprKind, Ty}; -use rustc_errors::PResult; -use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; -use rustc_span::Span; - -pub fn expand_type_ascribe( - cx: &mut ExtCtxt<'_>, - span: Span, - tts: TokenStream, -) -> MacroExpanderResult<'static> { - let (expr, ty) = match parse_ascribe(cx, tts) { - Ok(parsed) => parsed, - Err(err) => { - let guar = err.emit(); - return ExpandResult::Ready(DummyResult::any(span, guar)); - } - }; - - let asc_expr = cx.expr(span, ExprKind::Type(expr, ty)); - - ExpandResult::Ready(MacEager::expr(asc_expr)) -} - -fn parse_ascribe<'a>(cx: &mut ExtCtxt<'a>, stream: TokenStream) -> PResult<'a, (P<Expr>, P<Ty>)> { - let mut parser = cx.new_parser_from_tts(stream); - - let expr = parser.parse_expr()?; - parser.expect(&token::Comma)?; - - let ty = parser.parse_ty()?; - - Ok((expr, ty)) -} diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 6e846d721f2..b0af421008a 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -8,8 +8,11 @@ use std::borrow::Cow; use cranelift_codegen::ir::SigRef; use cranelift_module::ModuleError; +use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::FnAbiOf; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_target::abi::call::{Conv, FnAbi}; @@ -372,6 +375,17 @@ pub(crate) fn codegen_terminator_call<'tcx>( ty::Instance::expect_resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, fn_args) .polymorphize(fx.tcx); + if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { + if target.is_some() { + let caller = with_no_trimmed_paths!(fx.tcx.def_path_str(fx.instance.def_id())); + let callee = with_no_trimmed_paths!(fx.tcx.def_path_str(def_id)); + fx.tcx.dcx().emit_err(CompilerBuiltinsCannotCall { caller, callee }); + } else { + fx.bcx.ins().trap(TrapCode::User(0)); + return; + } + } + if fx.tcx.symbol_name(instance).name.starts_with("llvm.") { crate::intrinsics::codegen_llvm_intrinsic_call( fx, @@ -663,11 +677,7 @@ pub(crate) fn codegen_drop<'tcx>( let arg_value = drop_place.place_ref( fx, - fx.layout_of(Ty::new_ref( - fx.tcx, - fx.tcx.lifetimes.re_erased, - TypeAndMut { ty, mutbl: crate::rustc_hir::Mutability::Mut }, - )), + fx.layout_of(Ty::new_mut_ref(fx.tcx, fx.tcx.lifetimes.re_erased, ty)), ); let arg_value = adjust_arg_for_abi(fx, arg_value, &fn_abi.args[0], true); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 2415c2c90b2..dbce6d165d2 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -8,6 +8,7 @@ use rustc_index::IndexVec; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use crate::constant::ConstantCx; use crate::debuginfo::FunctionDebugContext; @@ -779,7 +780,7 @@ fn codegen_stmt<'tcx>( NullOp::OffsetOf(fields) => { layout.offset_of_subfield(fx, fields.iter()).bytes() } - NullOp::UbCheck(_) => { + NullOp::UbChecks => { let val = fx.tcx.sess.opts.debug_assertions; let val = CValue::by_val( fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), @@ -999,6 +1000,12 @@ fn codegen_panic_inner<'tcx>( let def_id = fx.tcx.require_lang_item(lang_item, span); let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); + + if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) { + fx.bcx.ins().trap(TrapCode::User(0)); + return; + } + let symbol_name = fx.tcx.symbol_name(instance).name; fx.lib_call( diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 7e29d407a1f..a7c3d68ff8c 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -69,7 +69,7 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<types::Typ FloatTy::F128 => unimplemented!("f16_f128"), }, ty::FnPtr(_) => pointer_ty(tcx), - ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl: _ }) | ty::Ref(_, pointee_ty, _) => { + ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => { if has_ptr_meta(tcx, *pointee_ty) { return None; } else { @@ -89,7 +89,7 @@ fn clif_pair_type_from_ty<'tcx>( ty::Tuple(types) if types.len() == 2 => { (clif_type_from_ty(tcx, types[0])?, clif_type_from_ty(tcx, types[1])?) } - ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl: _ }) | ty::Ref(_, pointee_ty, _) => { + ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => { if has_ptr_meta(tcx, *pointee_ty) { (pointer_ty(tcx), pointer_ty(tcx)) } else { diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 7e2e1f7c6ac..a59a39074f8 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -21,6 +21,7 @@ extern crate rustc_hir; extern crate rustc_incremental; extern crate rustc_index; extern crate rustc_metadata; +extern crate rustc_monomorphize; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 7b61dc64cb1..f33bacb99a3 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -70,10 +70,8 @@ fn unsize_ptr<'tcx>( ) -> (Value, Value) { match (&src_layout.ty.kind(), &dst_layout.ty.kind()) { (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) - | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) - | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { - (src, unsized_info(fx, *a, *b, old_info)) - } + | (&ty::Ref(_, a, _), &ty::RawPtr(b, _)) + | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => (src, unsized_info(fx, *a, *b, old_info)), (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index f016e6950d4..fc5b88a54fe 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -865,15 +865,10 @@ pub(crate) fn assert_assignable<'tcx>( return; } match (from_ty.kind(), to_ty.kind()) { - (ty::Ref(_, a, _), ty::Ref(_, b, _)) - | ( - ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), - ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }), - ) => { + (ty::Ref(_, a, _), ty::Ref(_, b, _)) | (ty::RawPtr(a, _), ty::RawPtr(b, _)) => { assert_assignable(fx, *a, *b, limit - 1); } - (ty::Ref(_, a, _), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ })) - | (ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::Ref(_, b, _)) => { + (ty::Ref(_, a, _), ty::RawPtr(b, _)) | (ty::RawPtr(a, _), ty::Ref(_, b, _)) => { assert_assignable(fx, *a, *b, limit - 1); } (ty::FnPtr(_), ty::FnPtr(_)) => { diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 8f643c7db72..9e6cf3e34df 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -110,6 +110,7 @@ pub struct CodegenCx<'gcc, 'tcx> { local_gen_sym_counter: Cell<usize>, eh_personality: Cell<Option<RValue<'gcc>>>, + #[cfg(feature="master")] pub rust_try_fn: Cell<Option<(Type<'gcc>, Function<'gcc>)>>, pub pointee_infos: RefCell<FxHashMap<(Ty<'tcx>, Size), Option<PointeeInfo>>>, @@ -121,6 +122,7 @@ pub struct CodegenCx<'gcc, 'tcx> { /// FIXME(antoyo): fix the rustc API to avoid having this hack. pub structs_as_pointer: RefCell<FxHashSet<RValue<'gcc>>>, + #[cfg(feature="master")] pub cleanup_blocks: RefCell<FxHashSet<Block<'gcc>>>, } @@ -325,9 +327,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { struct_types: Default::default(), local_gen_sym_counter: Cell::new(0), eh_personality: Cell::new(None), + #[cfg(feature="master")] rust_try_fn: Cell::new(None), pointee_infos: Default::default(), structs_as_pointer: Default::default(), + #[cfg(feature="master")] cleanup_blocks: Default::default(), }; // TODO(antoyo): instead of doing this, add SsizeT to libgccjit. diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index e9af34059a0..60361a44c2d 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -796,16 +796,16 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { - match t.kind() { - ty::RawPtr(p) => 1 + ptr_count(p.ty), + match *t.kind() { + ty::RawPtr(p_ty, _) => 1 + ptr_count(p_ty), _ => 0, } } // Non-ptr type fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.kind() { - ty::RawPtr(p) => non_ptr(p.ty), + match *t.kind() { + ty::RawPtr(p_ty, _) => non_ptr(p_ty), _ => t, } } @@ -814,8 +814,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // to the element type of the first argument let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); - let (pointer_count, underlying_ty) = match element_ty1.kind() { - ty::RawPtr(p) if p.ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)), + let (pointer_count, underlying_ty) = match *element_ty1.kind() { + ty::RawPtr(p_ty, _) if p_ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)), _ => { require!( false, @@ -910,16 +910,16 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { - match t.kind() { - ty::RawPtr(p) => 1 + ptr_count(p.ty), + match *t.kind() { + ty::RawPtr(p_ty, _) => 1 + ptr_count(p_ty), _ => 0, } } // Non-ptr type fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.kind() { - ty::RawPtr(p) => non_ptr(p.ty), + match *t.kind() { + ty::RawPtr(p_ty, _) => non_ptr(p_ty), _ => t, } } @@ -929,8 +929,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); - let (pointer_count, underlying_ty) = match element_ty1.kind() { - ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::Mutability::Mut => { + let (pointer_count, underlying_ty) = match *element_ty1.kind() { + ty::RawPtr(p_ty, mutbl) if p_ty == in_elem && mutbl == hir::Mutability::Mut => { (ptr_count(element_ty1), non_ptr(element_ty1)) } _ => { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 649ff9df2cc..df9f066e58a 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -27,9 +27,7 @@ use rustc_session::config::{CrateType, DebugInfo, PAuthKey, PacRet}; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_span::Span; -use rustc_target::abi::{ - call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx, -}; +use rustc_target::abi::{call::FnAbi, HasDataLayout, TargetDataLayout, VariantIdx}; use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel}; use smallvec::SmallVec; @@ -83,7 +81,6 @@ pub struct CodegenCx<'ll, 'tcx> { /// Mapping of scalar types to llvm types. pub scalar_lltypes: RefCell<FxHashMap<Ty<'tcx>, &'ll Type>>, - pub pointee_infos: RefCell<FxHashMap<(Ty<'tcx>, Size), Option<PointeeInfo>>>, pub isize_ty: &'ll Type, pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'ll, 'tcx>>, @@ -450,7 +447,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { compiler_used_statics: RefCell::new(Vec::new()), type_lowering: Default::default(), scalar_lltypes: Default::default(), - pointee_infos: Default::default(), isize_ty, coverage_cx, dbg_cx, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 133084b7c12..54f4bc06340 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -85,14 +85,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { let bx = self; - match coverage.kind { - // Marker statements have no effect during codegen, - // so return early and don't create `func_coverage`. - CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => return, - // Match exhaustively to ensure that newly-added kinds are classified correctly. - CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } => {} - } - let Some(function_coverage_info) = bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref() else { @@ -109,7 +101,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { let Coverage { kind } = coverage; match *kind { CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( - "unexpected marker statement {kind:?} should have caused an early return" + "marker statement {kind:?} should have been removed by CleanupPostBorrowck" ), CoverageKind::CounterIncrement { id } => { func_coverage.mark_counter_id_seen(id); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 5782b156335..3c76df11e3f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -452,7 +452,7 @@ pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll D ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id), ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id), ty::Foreign(..) => build_foreign_type_di_node(cx, t, unique_type_id), - ty::RawPtr(ty::TypeAndMut { ty: pointee_type, .. }) | ty::Ref(_, pointee_type, _) => { + ty::RawPtr(pointee_type, _) | ty::Ref(_, pointee_type, _) => { build_pointer_or_reference_di_node(cx, t, pointee_type, unique_type_id) } // Some `Box` are newtyped pointers, make debuginfo aware of that. diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 71b69a94e99..2409b2e78d7 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1483,7 +1483,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( v.normalize(bx.target_spec().pointer_width).bit_width().unwrap() ), ty::Float(v) => format!("v{}f{}", vec_len, v.bit_width()), - ty::RawPtr(_) => format!("v{}p0", vec_len), + ty::RawPtr(_, _) => format!("v{}p0", vec_len), _ => unreachable!(), } } @@ -1493,7 +1493,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ty::Int(v) => cx.type_int_from_ty(v), ty::Uint(v) => cx.type_uint_from_ty(v), ty::Float(v) => cx.type_float_from_ty(v), - ty::RawPtr(_) => cx.type_ptr(), + ty::RawPtr(_, _) => cx.type_ptr(), _ => unreachable!(), }; cx.type_vector(elem_ty, vec_len) @@ -1548,8 +1548,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( require!( matches!( - element_ty1.kind(), - ty::RawPtr(p) if p.ty == in_elem && p.ty.kind() == element_ty0.kind() + *element_ty1.kind(), + ty::RawPtr(p_ty, _) if p_ty == in_elem && p_ty.kind() == element_ty0.kind() ), InvalidMonomorphization::ExpectedElementType { span, @@ -1654,8 +1654,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( require!( matches!( - pointer_ty.kind(), - ty::RawPtr(p) if p.ty == values_elem && p.ty.kind() == values_elem.kind() + *pointer_ty.kind(), + ty::RawPtr(p_ty, _) if p_ty == values_elem && p_ty.kind() == values_elem.kind() ), InvalidMonomorphization::ExpectedElementType { span, @@ -1746,8 +1746,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // The second argument must be a mutable pointer type matching the element type require!( matches!( - pointer_ty.kind(), - ty::RawPtr(p) if p.ty == values_elem && p.ty.kind() == values_elem.kind() && p.mutbl.is_mut() + *pointer_ty.kind(), + ty::RawPtr(p_ty, p_mutbl) if p_ty == values_elem && p_ty.kind() == values_elem.kind() && p_mutbl.is_mut() ), InvalidMonomorphization::ExpectedElementType { span, @@ -1843,9 +1843,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( require!( matches!( - element_ty1.kind(), - ty::RawPtr(p) - if p.ty == in_elem && p.mutbl.is_mut() && p.ty.kind() == element_ty0.kind() + *element_ty1.kind(), + ty::RawPtr(p_ty, p_mutbl) + if p_ty == in_elem && p_mutbl.is_mut() && p_ty.kind() == element_ty0.kind() ), InvalidMonomorphization::ExpectedElementType { span, @@ -2074,8 +2074,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); match in_elem.kind() { - ty::RawPtr(p) => { - let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| { + ty::RawPtr(p_ty, _) => { + let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); require!( @@ -2088,8 +2088,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } } match out_elem.kind() { - ty::RawPtr(p) => { - let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| { + ty::RawPtr(p_ty, _) => { + let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| { bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); require!( @@ -2120,7 +2120,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); match in_elem.kind() { - ty::RawPtr(_) => {} + ty::RawPtr(_, _) => {} _ => { return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem }) } @@ -2152,7 +2152,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( _ => return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: in_elem }), } match out_elem.kind() { - ty::RawPtr(_) => {} + ty::RawPtr(_, _) => {} _ => { return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem }) } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 7851b9e8e03..baf10622a6d 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -25,6 +25,7 @@ rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } +rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 5ba66d1be43..d159fe58d3e 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -16,6 +16,9 @@ codegen_ssa_cgu_not_recorded = codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. +codegen_ssa_compiler_builtins_cannot_call = + `compiler_builtins` cannot call functions through upstream monomorphizations; encountered invalid call from `{$caller}` to `{$callee}` + codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error} codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$error} diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index 9b0ba34135c..3a89be89951 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -100,12 +100,6 @@ impl Command { Program::Lld(ref p, flavor) => { let mut c = process::Command::new(p); c.arg("-flavor").arg(flavor.as_str()); - if let LldFlavor::Wasm = flavor { - // LLVM expects host-specific formatting for @file - // arguments, but we always generate posix formatted files - // at this time. Indicate as such. - c.arg("--rsp-quoting=posix"); - } c } }; diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f5e8d5fc92a..c8b8594c0dd 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -785,7 +785,7 @@ fn link_natively<'a>( let mut i = 0; loop { i += 1; - prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, tmpdir)); + prog = sess.time("run_linker", || exec_linker(sess, &cmd, out_filename, flavor, tmpdir)); let Ok(ref output) = prog else { break; }; @@ -1576,6 +1576,7 @@ fn exec_linker( sess: &Session, cmd: &Command, out_filename: &Path, + flavor: LinkerFlavor, tmpdir: &Path, ) -> io::Result<Output> { // When attempting to spawn the linker we run a risk of blowing out the @@ -1584,9 +1585,9 @@ fn exec_linker( // // Here we attempt to handle errors from the OS saying "your list of // arguments is too big" by reinvoking the linker again with an `@`-file - // that contains all the arguments. The theory is that this is then - // accepted on all linkers and the linker will read all its options out of - // there instead of looking at the command line. + // that contains all the arguments (aka 'response' files). + // The theory is that this is then accepted on all linkers and the linker + // will read all its options out of there instead of looking at the command line. if !cmd.very_likely_to_exceed_some_spawn_limit() { match cmd.command().stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() { Ok(child) => { @@ -1606,8 +1607,12 @@ fn exec_linker( let mut args = String::new(); for arg in cmd2.take_args() { args.push_str( - &Escape { arg: arg.to_str().unwrap(), is_like_msvc: sess.target.is_like_msvc } - .to_string(), + &Escape { + arg: arg.to_str().unwrap(), + // LLD also uses MSVC-like parsing for @-files by default when running on windows hosts + is_like_msvc: sess.target.is_like_msvc || (cfg!(windows) && flavor.uses_lld()), + } + .to_string(), ); args.push('\n'); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index c316d19e041..13809ef72ec 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -193,8 +193,8 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> (Bx::Value, Bx::Value) { debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty); match (src_ty.kind(), dst_ty.kind()) { - (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) - | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { + (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _)) + | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => { assert_eq!(bx.cx().type_is_sized(a), old_info.is_none()); (src, unsized_info(bx, a, b, old_info)) } diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 44a2434238d..71fca403def 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -2,6 +2,7 @@ use rustc_hir::LangItem; use rustc_middle::mir; +use rustc_middle::ty::Instance; use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt}; use rustc_span::Span; @@ -120,11 +121,11 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &Bx, span: Option<Span>, li: LangItem, -) -> (Bx::FnAbiOfResult, Bx::Value) { +) -> (Bx::FnAbiOfResult, Bx::Value, Instance<'tcx>) { let tcx = bx.tcx(); let def_id = tcx.require_lang_item(li, span); let instance = ty::Instance::mono(tcx, def_id); - (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance)) + (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance), instance) } // To avoid UB from LLVM, these two functions mask RHS with an diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index fcd7fa9247b..64448441acb 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -138,7 +138,7 @@ fn push_debuginfo_type_name<'tcx>( output.push(')'); } } - ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => { + ty::RawPtr(inner_type, mutbl) => { if cpp_like_debuginfo { match mutbl { Mutability::Not => output.push_str("ptr_const$<"), diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 3572ee301c8..b843d1bdf23 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1030,3 +1030,10 @@ pub struct FailedToGetLayout<'tcx> { pub struct ErrorCreatingRemarkDir { pub error: std::io::Error, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_compiler_builtins_cannot_call)] +pub struct CompilerBuiltinsCannotCall { + pub caller: String, + pub callee: String, +} diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 02e7bb05b77..dcc27a4f0e5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -5,6 +5,7 @@ use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base; use crate::common::{self, IntPredicate}; +use crate::errors::CompilerBuiltinsCannotCall; use crate::meth; use crate::traits::*; use crate::MemFlags; @@ -16,6 +17,7 @@ use rustc_middle::mir::{self, AssertKind, BasicBlock, SwitchTargets, UnwindTermi use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty}; +use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::config::OptLevel; use rustc_span::{source_map::Spanned, sym, Span}; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg}; @@ -157,8 +159,28 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>, mut unwind: mir::UnwindAction, copied_constant_arguments: &[PlaceRef<'tcx, <Bx as BackendTypes>::Value>], + instance: Option<Instance<'tcx>>, mergeable_succ: bool, ) -> MergingSucc { + let tcx = bx.tcx(); + if let Some(instance) = instance { + if is_call_from_compiler_builtins_to_upstream_monomorphization(tcx, instance) { + if destination.is_some() { + let caller = with_no_trimmed_paths!(tcx.def_path_str(fx.instance.def_id())); + let callee = with_no_trimmed_paths!(tcx.def_path_str(instance.def_id())); + tcx.dcx().emit_err(CompilerBuiltinsCannotCall { caller, callee }); + } else { + info!( + "compiler_builtins call to diverging function {:?} replaced with abort", + instance.def_id() + ); + bx.abort(); + bx.unreachable(); + return MergingSucc::False; + } + } + } + // If there is a cleanup block and the function we're calling can unwind, then // do an invoke, otherwise do a call. let fn_ty = bx.fn_decl_backend_type(fn_abi); @@ -480,6 +502,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = location.ty(self.mir, bx.tcx()).ty; let ty = self.monomorphize(ty); let drop_fn = Instance::resolve_drop_in_place(bx.tcx(), ty); + let instance = drop_fn.clone(); if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { // we don't actually need to drop anything. @@ -582,6 +605,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Some((ReturnDest::Nothing, target)), unwind, &[], + Some(instance), mergeable_succ, ) } @@ -658,10 +682,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } }; - let (fn_abi, llfn) = common::build_langcall(bx, Some(span), lang_item); + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), lang_item); // Codegen the actual panic invoke/call. - let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], false); + let merging_succ = + helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], Some(instance), false); assert_eq!(merging_succ, MergingSucc::False); MergingSucc::False } @@ -677,7 +702,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(bx, terminator.source_info); // Obtain the panic entry point. - let (fn_abi, llfn) = common::build_langcall(bx, Some(span), reason.lang_item()); + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), reason.lang_item()); // Codegen the actual panic invoke/call. let merging_succ = helper.do_call( @@ -689,6 +714,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { None, mir::UnwindAction::Unreachable, &[], + Some(instance), false, ); assert_eq!(merging_succ, MergingSucc::False); @@ -738,7 +764,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let msg = bx.const_str(&msg_str); // Obtain the panic entry point. - let (fn_abi, llfn) = + let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(source_info.span), LangItem::PanicNounwind); // Codegen the actual panic invoke/call. @@ -751,6 +777,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)), unwind, &[], + Some(instance), mergeable_succ, ) } else { @@ -798,6 +825,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ty::FnPtr(_) => (None, Some(callee.immediate())), _ => bug!("{} is not callable", callee.layout.ty), }; + let def = instance.map(|i| i.def); if let Some(ty::InstanceDef::DropGlue(_, None)) = def { @@ -1106,6 +1134,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { destination, unwind, &copied_constant_arguments, + instance, mergeable_succ, ) } @@ -1664,11 +1693,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span)); - let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, reason.lang_item()); - let fn_ty = bx.fn_decl_backend_type(fn_abi); + let (fn_abi, fn_ptr, instance) = common::build_langcall(&bx, None, reason.lang_item()); + if is_call_from_compiler_builtins_to_upstream_monomorphization(bx.tcx(), instance) { + bx.abort(); + } else { + let fn_ty = bx.fn_decl_backend_type(fn_abi); - let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); - bx.apply_attrs_to_cleanup_callsite(llret); + let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); + bx.apply_attrs_to_cleanup_callsite(llret); + } bx.unreachable(); diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 48f3f4f2522..0387c430d32 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -414,10 +414,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { calculate_debuginfo_offset(bx, var.projection, base); // Create a variable which will be a pointer to the actual value - let ptr_ty = Ty::new_ptr( - bx.tcx(), - ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty }, - ); + let ptr_ty = Ty::new_mut_ptr(bx.tcx(), place.layout.ty); let ptr_layout = bx.layout_of(ptr_ty); let alloca = PlaceRef::alloca(bx, ptr_layout); bx.set_var_name(alloca.llval, &(var.name.to_string() + ".dbg.spill")); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 5532ff6e6a5..3e6cf0ece29 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -9,6 +9,7 @@ use crate::traits::*; use crate::MemFlags; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_session::config::OptLevel; use rustc_span::{sym, Span}; use rustc_target::abi::{ call::{FnAbi, PassMode}, @@ -75,6 +76,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let name = bx.tcx().item_name(def_id); let name_str = name.as_str(); + // If we're swapping something that's *not* an `OperandValue::Ref`, + // then we can do it directly and avoid the alloca. + // Otherwise, we'll let the fallback MIR body take care of it. + if let sym::typed_swap = name { + let pointee_ty = fn_args.type_at(0); + let pointee_layout = bx.layout_of(pointee_ty); + if !bx.is_backend_ref(pointee_layout) + // But if we're not going to optimize, trying to use the fallback + // body just makes things worse, so don't bother. + || bx.sess().opts.optimize == OptLevel::No + // NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary + // reinterpretation of values as (chunkable) byte arrays, and the loop in the + // block optimization in `ptr::swap_nonoverlapping` is hard to rewrite back + // into the (unoptimized) direct swapping implementation, so we disable it. + || bx.sess().target.arch == "spirv" + { + let x_place = PlaceRef::new_sized(args[0].immediate(), pointee_layout); + let y_place = PlaceRef::new_sized(args[1].immediate(), pointee_layout); + bx.typed_place_swap(x_place, y_place); + return Ok(()); + } + } + let llret_ty = bx.backend_type(bx.layout_of(ret_ty)); let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 8159f76b421..0e8c4abf212 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -572,20 +572,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Ref(_, bk, place) => { let mk_ref = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { - Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty, mutbl: bk.to_mutbl_lossy() }, - ) + Ty::new_ref(tcx, tcx.lifetimes.re_erased, ty, bk.to_mutbl_lossy()) }; self.codegen_place_to_pointer(bx, place, mk_ref) } mir::Rvalue::CopyForDeref(place) => self.codegen_operand(bx, &Operand::Copy(place)), mir::Rvalue::AddressOf(mutability, place) => { - let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { - Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl: mutability }) - }; + let mk_ptr = + move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| Ty::new_ptr(tcx, ty, mutability); self.codegen_place_to_pointer(bx, place, mk_ptr) } @@ -685,8 +680,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes(); bx.cx().const_usize(val) } - mir::NullOp::UbCheck(_) => { - // In codegen, we want to check for language UB and library UB + mir::NullOp::UbChecks => { let val = bx.tcx().sess.opts.debug_assertions; bx.cx().const_bool(val) } diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 087836ca37d..e2e95cede60 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -62,7 +62,8 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let msg = bx.const_str(&msg_str); // Obtain the panic entry point. - let (fn_abi, llfn) = common::build_langcall(bx, None, LangItem::PanicNounwind); + let (fn_abi, llfn, _instance) = + common::build_langcall(bx, None, LangItem::PanicNounwind); // Generate the call. // Cannot use `do_call` since we don't have a MIR terminator so we can't create a `TerminationCodegenHelper`. diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index e6d42c596d2..e8b9490d401 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -21,7 +21,6 @@ use rustc_session::{ }; use rustc_span::symbol::Symbol; use rustc_target::abi::call::FnAbi; -use rustc_target::spec::Target; use std::fmt; @@ -70,12 +69,6 @@ pub trait CodegenBackend { fn print_passes(&self) {} fn print_version(&self) {} - /// If this plugin provides additional builtin targets, provide the one enabled by the options here. - /// Be careful: this is called *before* init() is called. - fn target_override(&self, _opts: &config::Options) -> Option<Target> { - None - } - /// The metadata loader used to load rlib and dylib metadata. /// /// Alternative codegen backends may want to use different rlib or dylib formats than the diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 36f37e3791b..7bc9dee3a89 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -1,22 +1,24 @@ use super::abi::AbiBuilderMethods; use super::asm::AsmBuilderMethods; +use super::consts::ConstMethods; use super::coverageinfo::CoverageInfoBuilderMethods; use super::debuginfo::DebugInfoBuilderMethods; use super::intrinsic::IntrinsicCallMethods; use super::misc::MiscMethods; -use super::type_::{ArgAbiMethods, BaseTypeMethods}; +use super::type_::{ArgAbiMethods, BaseTypeMethods, LayoutTypeMethods}; use super::{HasCodegen, StaticBuilderMethods}; use crate::common::{ AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, }; -use crate::mir::operand::OperandRef; +use crate::mir::operand::{OperandRef, OperandValue}; use crate::mir::place::PlaceRef; use crate::MemFlags; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; use rustc_middle::ty::Ty; +use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Abi, Align, Scalar, Size, WrappingRange}; @@ -267,6 +269,54 @@ pub trait BuilderMethods<'a, 'tcx>: flags: MemFlags, ); + /// *Typed* copy for non-overlapping places. + /// + /// Has a default implementation in terms of `memcpy`, but specific backends + /// can override to do something smarter if possible. + /// + /// (For example, typed load-stores with alias metadata.) + fn typed_place_copy( + &mut self, + dst: PlaceRef<'tcx, Self::Value>, + src: PlaceRef<'tcx, Self::Value>, + ) { + debug_assert!(src.llextra.is_none()); + debug_assert!(dst.llextra.is_none()); + debug_assert_eq!(dst.layout.size, src.layout.size); + if self.sess().opts.optimize == OptLevel::No && self.is_backend_immediate(dst.layout) { + // If we're not optimizing, the aliasing information from `memcpy` + // isn't useful, so just load-store the value for smaller code. + let temp = self.load_operand(src); + temp.val.store(self, dst); + } else if !dst.layout.is_zst() { + let bytes = self.const_usize(dst.layout.size.bytes()); + self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, MemFlags::empty()); + } + } + + /// *Typed* swap for non-overlapping places. + /// + /// Avoids `alloca`s for Immediates and ScalarPairs. + /// + /// FIXME: Maybe do something smarter for Ref types too? + /// For now, the `typed_swap` intrinsic just doesn't call this for those + /// cases (in non-debug), preferring the fallback body instead. + fn typed_place_swap( + &mut self, + left: PlaceRef<'tcx, Self::Value>, + right: PlaceRef<'tcx, Self::Value>, + ) { + let mut temp = self.load_operand(left); + if let OperandValue::Ref(..) = temp.val { + // The SSA value isn't stand-alone, so we need to copy it elsewhere + let alloca = PlaceRef::alloca(self, left.layout); + self.typed_place_copy(alloca, left); + temp = self.load_operand(alloca); + } + self.typed_place_copy(left, right); + temp.val.store(self, right); + } + fn select( &mut self, cond: Self::Value, diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 72cce43a3fa..555833759eb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -120,6 +120,20 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { immediate: bool, ) -> Self::Type; + /// A type that produces an [`OperandValue::Ref`] when loaded. + /// + /// AKA one that's not a ZST, not `is_backend_immediate`, and + /// not `is_backend_scalar_pair`. For such a type, a + /// [`load_operand`] doesn't actually `load` anything. + /// + /// [`OperandValue::Ref`]: crate::mir::operand::OperandValue::Ref + /// [`load_operand`]: super::BuilderMethods::load_operand + fn is_backend_ref(&self, layout: TyAndLayout<'tcx>) -> bool { + !(layout.is_zst() + || self.is_backend_immediate(layout) + || self.is_backend_scalar_pair(layout)) + } + /// A type that can be used in a [`super::BuilderMethods::load`] + /// [`super::BuilderMethods::store`] pair to implement a *typed* copy, /// such as a MIR `*_0 = *_1`. diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs new file mode 100644 index 00000000000..ba2e2a1e353 --- /dev/null +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -0,0 +1,193 @@ +use crate::interpret::{self, HasStaticRootDefId, ImmTy, Immediate, InterpCx, PointerArithmetic}; +use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult}; +use rustc_middle::mir::*; +use rustc_middle::query::TyCtxtAt; +use rustc_middle::ty; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_span::def_id::DefId; + +/// Macro for machine-specific `InterpError` without allocation. +/// (These will never be shown to the user, but they help diagnose ICEs.) +pub macro throw_machine_stop_str($($tt:tt)*) {{ + // We make a new local type for it. The type itself does not carry any information, + // but its vtable (for the `MachineStopType` trait) does. + #[derive(Debug)] + struct Zst; + // Printing this type shows the desired string. + impl std::fmt::Display for Zst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, $($tt)*) + } + } + + impl rustc_middle::mir::interpret::MachineStopType for Zst { + fn diagnostic_message(&self) -> rustc_errors::DiagMessage { + self.to_string().into() + } + + fn add_args( + self: Box<Self>, + _: &mut dyn FnMut(rustc_errors::DiagArgName, rustc_errors::DiagArgValue), + ) {} + } + throw_machine_stop!(Zst) +}} + +pub struct DummyMachine; + +impl HasStaticRootDefId for DummyMachine { + fn static_def_id(&self) -> Option<rustc_hir::def_id::LocalDefId> { + None + } +} + +impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { + interpret::compile_time_machine!(<'mir, 'tcx>); + type MemoryKind = !; + const PANIC_ON_ALLOC_FAIL: bool = true; + + #[inline(always)] + fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + false // no reason to enforce alignment + } + + fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { + false + } + + fn before_access_global( + _tcx: TyCtxtAt<'tcx>, + _machine: &Self, + _alloc_id: AllocId, + alloc: ConstAllocation<'tcx>, + _static_def_id: Option<DefId>, + is_write: bool, + ) -> InterpResult<'tcx> { + if is_write { + throw_machine_stop_str!("can't write to global"); + } + + // If the static allocation is mutable, then we can't const prop it as its content + // might be different at runtime. + if alloc.inner().mutability.is_mut() { + throw_machine_stop_str!("can't access mutable globals in ConstProp"); + } + + Ok(()) + } + + fn find_mir_or_eval_fn( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _instance: ty::Instance<'tcx>, + _abi: rustc_target::spec::abi::Abi, + _args: &[interpret::FnArg<'tcx, Self::Provenance>], + _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, + _target: Option<BasicBlock>, + _unwind: UnwindAction, + ) -> interpret::InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { + unimplemented!() + } + + fn panic_nounwind( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _msg: &str, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn call_intrinsic( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _instance: ty::Instance<'tcx>, + _args: &[interpret::OpTy<'tcx, Self::Provenance>], + _destination: &interpret::MPlaceTy<'tcx, Self::Provenance>, + _target: Option<BasicBlock>, + _unwind: UnwindAction, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn assert_panic( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _msg: &rustc_middle::mir::AssertMessage<'tcx>, + _unwind: UnwindAction, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn binary_ptr_op( + ecx: &InterpCx<'mir, 'tcx, Self>, + bin_op: BinOp, + left: &interpret::ImmTy<'tcx, Self::Provenance>, + right: &interpret::ImmTy<'tcx, Self::Provenance>, + ) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> { + use rustc_middle::mir::BinOp::*; + Ok(match bin_op { + Eq | Ne | Lt | Le | Gt | Ge => { + // Types can differ, e.g. fn ptrs with different `for`. + assert_eq!(left.layout.abi, right.layout.abi); + let size = ecx.pointer_size(); + // Just compare the bits. ScalarPairs are compared lexicographically. + // We thus always compare pairs and simply fill scalars up with 0. + // If the pointer has provenance, `to_bits` will return `Err` and we bail out. + let left = match **left { + Immediate::Scalar(l) => (l.to_bits(size)?, 0), + Immediate::ScalarPair(l1, l2) => (l1.to_bits(size)?, l2.to_bits(size)?), + Immediate::Uninit => panic!("we should never see uninit data here"), + }; + let right = match **right { + Immediate::Scalar(r) => (r.to_bits(size)?, 0), + Immediate::ScalarPair(r1, r2) => (r1.to_bits(size)?, r2.to_bits(size)?), + Immediate::Uninit => panic!("we should never see uninit data here"), + }; + let res = match bin_op { + Eq => left == right, + Ne => left != right, + Lt => left < right, + Le => left <= right, + Gt => left > right, + Ge => left >= right, + _ => bug!(), + }; + (ImmTy::from_bool(res, *ecx.tcx), false) + } + + // Some more operations are possible with atomics. + // The return value always has the provenance of the *left* operand. + Add | Sub | BitOr | BitAnd | BitXor => { + throw_machine_stop_str!("pointer arithmetic is not handled") + } + + _ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op), + }) + } + + fn expose_ptr( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _ptr: interpret::Pointer<Self::Provenance>, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + + fn init_frame_extra( + _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _frame: interpret::Frame<'mir, 'tcx, Self::Provenance>, + ) -> interpret::InterpResult< + 'tcx, + interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, + > { + unimplemented!() + } + + fn stack<'a>( + _ecx: &'a InterpCx<'mir, 'tcx, Self>, + ) -> &'a [interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] { + // Return an empty stack instead of panicking, as `cur_span` uses it to evaluate constants. + &[] + } + + fn stack_mut<'a>( + _ecx: &'a mut InterpCx<'mir, 'tcx, Self>, + ) -> &'a mut Vec<interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> { + unimplemented!() + } +} diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index d0d6adbfad0..8efc67bcb0c 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -2,17 +2,20 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::InterpErrorInfo; -use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::query::{Key, TyCtxtAt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_target::abi::VariantIdx; -use crate::interpret::format_interp_error; +use crate::interpret::{format_interp_error, InterpCx}; +mod dummy_machine; mod error; mod eval_queries; mod fn_queries; mod machine; mod valtrees; +pub use dummy_machine::*; pub use error::*; pub use eval_queries::*; pub use fn_queries::*; @@ -75,3 +78,21 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( Some(mir::DestructuredConstant { variant, fields }) } + +/// Computes the tag (if any) for a given type and variant. +#[instrument(skip(tcx), level = "debug")] +pub fn tag_for_variant_provider<'tcx>( + tcx: TyCtxt<'tcx>, + (ty, variant_index): (Ty<'tcx>, VariantIdx), +) -> Option<ty::ScalarInt> { + assert!(ty.is_enum()); + + let ecx = InterpCx::new( + tcx, + ty.default_span(tcx), + ty::ParamEnv::reveal_all(), + crate::const_eval::DummyMachine, + ); + + ecx.tag_for_variant(ty, variant_index).unwrap().map(|(tag, _tag_field)| tag) +} diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index d3428d27d52..d91ad3fcab1 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -98,7 +98,7 @@ fn const_to_valtree_inner<'tcx>( Ok(ty::ValTree::Leaf(val.assert_int())) } - ty::RawPtr(_) => { + ty::RawPtr(_, _) => { // Not all raw pointers are allowed, as we cannot properly test them for // equality at compile-time (see `ptr_guaranteed_cmp`). // However we allow those that are just integers in disguise. @@ -278,7 +278,7 @@ pub fn valtree_to_const_value<'tcx>( assert!(valtree.unwrap_branch().is_empty()); mir::ConstValue::ZeroSized } - ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char | ty::RawPtr(_) => { + ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char | ty::RawPtr(_, _) => { match valtree { ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)), ty::ValTree::Branch(_) => bug!( diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 2cebea9d145..bbf11f169f9 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -6,7 +6,7 @@ use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::mir::CastKind; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout}; -use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut}; +use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_target::abi::Integer; use rustc_type_ir::TyKind::*; @@ -230,7 +230,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { src: &ImmTy<'tcx, M::Provenance>, cast_to: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { - assert_matches!(src.layout.ty.kind(), ty::RawPtr(_) | ty::FnPtr(_)); + assert_matches!(src.layout.ty.kind(), ty::RawPtr(_, _) | ty::FnPtr(_)); assert!(cast_to.ty.is_integral()); let scalar = src.to_scalar(); @@ -248,7 +248,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { cast_to: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { assert!(src.layout.ty.is_integral()); - assert_matches!(cast_to.ty.kind(), ty::RawPtr(_)); + assert_matches!(cast_to.ty.kind(), ty::RawPtr(_, _)); // First cast to usize. let scalar = src.to_scalar(); @@ -435,10 +435,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx> { trace!("Unsizing {:?} of type {} into {}", *src, src.layout.ty, cast_ty.ty); match (&src.layout.ty.kind(), &cast_ty.ty.kind()) { - (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. })) - | (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => { - self.unsize_into_ptr(src, dest, *s, *c) - } + (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _)) + | (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, *s, *c), (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); // implies same number of fields diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 6d4f6d0cb3c..704f597cfdb 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -2,7 +2,7 @@ use rustc_middle::mir; use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, ScalarInt, Ty}; use rustc_target::abi::{self, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; @@ -28,78 +28,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { throw_ub!(UninhabitedEnumVariantWritten(variant_index)) } - match dest.layout().variants { - abi::Variants::Single { index } => { - assert_eq!(index, variant_index); - } - abi::Variants::Multiple { - tag_encoding: TagEncoding::Direct, - tag: tag_layout, - tag_field, - .. - } => { + match self.tag_for_variant(dest.layout().ty, variant_index)? { + Some((tag, tag_field)) => { // No need to validate that the discriminant here because the - // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. - - let discr_val = dest - .layout() - .ty - .discriminant_for_variant(*self.tcx, variant_index) - .unwrap() - .val; - - // raw discriminants for enums are isize or bigger during - // their computation, but the in-memory tag is the smallest possible - // representation - let size = tag_layout.size(self); - let tag_val = size.truncate(discr_val); - + // `TyAndLayout::for_variant()` call earlier already checks the + // variant is valid. let tag_dest = self.project_field(dest, tag_field)?; - self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?; + self.write_scalar(tag, &tag_dest) } - abi::Variants::Multiple { - tag_encoding: - TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, - tag: tag_layout, - tag_field, - .. - } => { - // No need to validate that the discriminant here because the - // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. - - if variant_index != untagged_variant { - let variants_start = niche_variants.start().as_u32(); - let variant_index_relative = variant_index - .as_u32() - .checked_sub(variants_start) - .expect("overflow computing relative variant idx"); - // We need to use machine arithmetic when taking into account `niche_start`: - // tag_val = variant_index_relative + niche_start_val - let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?; - let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); - let variant_index_relative_val = - ImmTy::from_uint(variant_index_relative, tag_layout); - let tag_val = self.wrapping_binary_op( - mir::BinOp::Add, - &variant_index_relative_val, - &niche_start_val, - )?; - // Write result. - let niche_dest = self.project_field(dest, tag_field)?; - self.write_immediate(*tag_val, &niche_dest)?; - } else { - // The untagged variant is implicitly encoded simply by having a value that is - // outside the niche variants. But what if the data stored here does not - // actually encode this variant? That would be bad! So let's double-check... - let actual_variant = self.read_discriminant(&dest.to_op(self)?)?; - if actual_variant != variant_index { - throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty }); - } + None => { + // No need to write the tag here, because an untagged variant is + // implicitly encoded. For `Niche`-optimized enums, this works by + // simply by having a value that is outside the niche variants. + // But what if the data stored here does not actually encode + // this variant? That would be bad! So let's double-check... + let actual_variant = self.read_discriminant(&dest.to_op(self)?)?; + if actual_variant != variant_index { + throw_ub!(InvalidNichedEnumVariantWritten { enum_ty: dest.layout().ty }); } + Ok(()) } } - - Ok(()) } /// Read discriminant, return the runtime value as well as the variant index. @@ -277,4 +226,79 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; Ok(ImmTy::from_scalar(discr_value, discr_layout)) } + + /// Computes how to write the tag of a given variant of enum `ty`: + /// - `None` means that nothing needs to be done as the variant is encoded implicitly + /// - `Some((val, field_idx))` means that the given integer value needs to be stored at the + /// given field index. + pub(crate) fn tag_for_variant( + &self, + ty: Ty<'tcx>, + variant_index: VariantIdx, + ) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> { + match self.layout_of(ty)?.variants { + abi::Variants::Single { index } => { + assert_eq!(index, variant_index); + Ok(None) + } + + abi::Variants::Multiple { + tag_encoding: TagEncoding::Direct, + tag: tag_layout, + tag_field, + .. + } => { + // raw discriminants for enums are isize or bigger during + // their computation, but the in-memory tag is the smallest possible + // representation + let discr = self.discriminant_for_variant(ty, variant_index)?; + let discr_size = discr.layout.size; + let discr_val = discr.to_scalar().to_bits(discr_size)?; + let tag_size = tag_layout.size(self); + let tag_val = tag_size.truncate(discr_val); + let tag = ScalarInt::try_from_uint(tag_val, tag_size).unwrap(); + Ok(Some((tag, tag_field))) + } + + abi::Variants::Multiple { + tag_encoding: TagEncoding::Niche { untagged_variant, .. }, + .. + } if untagged_variant == variant_index => { + // The untagged variant is implicitly encoded simply by having a + // value that is outside the niche variants. + Ok(None) + } + + abi::Variants::Multiple { + tag_encoding: + TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start }, + tag: tag_layout, + tag_field, + .. + } => { + assert!(variant_index != untagged_variant); + let variants_start = niche_variants.start().as_u32(); + let variant_index_relative = variant_index + .as_u32() + .checked_sub(variants_start) + .expect("overflow computing relative variant idx"); + // We need to use machine arithmetic when taking into account `niche_start`: + // tag_val = variant_index_relative + niche_start_val + let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?; + let niche_start_val = ImmTy::from_uint(niche_start, tag_layout); + let variant_index_relative_val = + ImmTy::from_uint(variant_index_relative, tag_layout); + let tag = self + .wrapping_binary_op( + mir::BinOp::Add, + &variant_index_relative_val, + &niche_start_val, + )? + .to_scalar() + .try_to_int() + .unwrap(); + Ok(Some((tag, tag_field))) + } + } + } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 748c7dce5a3..a8478f721c7 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -21,8 +21,8 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::Size; use super::{ - util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, MPlaceTy, Machine, OpTy, - Pointer, + memory::MemoryKind, util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, + MPlaceTy, Machine, OpTy, Pointer, }; use crate::fluent_generated as fluent; @@ -79,7 +79,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -414,6 +414,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = self.raw_eq_intrinsic(&args[0], &args[1])?; self.write_scalar(result, dest)?; } + sym::typed_swap => { + self.typed_swap_intrinsic(&args[0], &args[1])?; + } sym::vtable_size => { let ptr = self.read_pointer(&args[0])?; @@ -607,6 +610,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.mem_copy(src, dst, size, nonoverlapping) } + /// Does a *typed* swap of `*left` and `*right`. + fn typed_swap_intrinsic( + &mut self, + left: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + right: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + ) -> InterpResult<'tcx> { + let left = self.deref_pointer(left)?; + let right = self.deref_pointer(right)?; + debug_assert_eq!(left.layout, right.layout); + let kind = MemoryKind::Stack; + let temp = self.allocate(left.layout, kind)?; + self.copy_op(&left, &temp)?; + self.copy_op(&right, &left)?; + self.copy_op(&temp, &right)?; + self.deallocate_ptr(temp.ptr(), None, kind)?; + Ok(()) + } + pub(crate) fn write_bytes_intrinsic( &mut self, dst: &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 a6ca4b2e0de..9b1d9cf932b 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1159,11 +1159,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; // Side-step AllocRef and directly access the underlying bytes more efficiently. - // (We are staying inside the bounds here so all is good.) + // (We are staying inside the bounds here and all bytes do get overwritten so all is good.) let alloc_id = alloc_ref.alloc_id; let bytes = alloc_ref .alloc - .get_bytes_mut(&alloc_ref.tcx, alloc_ref.range) + .get_bytes_unchecked_for_overwrite(&alloc_ref.tcx, alloc_ref.range) .map_err(move |e| e.to_interp_error(alloc_id))?; // `zip` would stop when the first iterator ends; we want to definitely // cover all of `bytes`. @@ -1184,6 +1184,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.mem_copy_repeatedly(src, dest, size, 1, nonoverlapping) } + /// Performs `num_copies` many copies of `size` many bytes from `src` to `dest + i*size` (where + /// `i` is the index of the copy). + /// + /// Either `nonoverlapping` must be true or `num_copies` must be 1; doing repeated copies that + /// may overlap is not supported. pub fn mem_copy_repeatedly( &mut self, src: Pointer<Option<M::Provenance>>, @@ -1245,8 +1250,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (dest_alloc_id, dest_prov), dest_range, )?; + // Yes we do overwrite all bytes in `dest_bytes`. let dest_bytes = dest_alloc - .get_bytes_mut_ptr(&tcx, dest_range) + .get_bytes_unchecked_for_overwrite_ptr(&tcx, dest_range) .map_err(|e| e.to_interp_error(dest_alloc_id))? .as_mut_ptr(); @@ -1280,6 +1286,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } } + if num_copies > 1 { + assert!(nonoverlapping, "multi-copy only supported in non-overlapping mode"); + } let size_in_bytes = size.bytes_usize(); // For particularly large arrays (where this is perf-sensitive) it's common that @@ -1292,6 +1301,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } else if src_alloc_id == dest_alloc_id { let mut dest_ptr = dest_bytes; for _ in 0..num_copies { + // Here we rely on `src` and `dest` being non-overlapping if there is more than + // one copy. ptr::copy(src_bytes, dest_ptr, size_in_bytes); dest_ptr = dest_ptr.add(size_in_bytes); } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 54bac70da38..9114ffff6fd 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -258,17 +258,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = layout.offset_of_subfield(self, fields.iter()).bytes(); Scalar::from_target_usize(val, self) } - mir::NullOp::UbCheck(kind) => { - // We want to enable checks for library UB, because the interpreter doesn't - // know about those on its own. - // But we want to disable checks for language UB, because the interpreter - // has its own better checks for that. - let should_check = match kind { - mir::UbKind::LibraryUb => self.tcx.sess.opts.debug_assertions, - mir::UbKind::LanguageUb => false, - }; - Scalar::from_bool(should_check) - } + mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.opts.debug_assertions), }; self.write_scalar(val, &dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 82fb7ff1840..c0e27e86d50 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -375,7 +375,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // We cannot use `builtin_deref` here since we need to reject `Box<T, MyAlloc>`. Ok(Some(match ty.kind() { ty::Ref(_, ty, _) => *ty, - ty::RawPtr(mt) => mt.ty, + ty::RawPtr(ty, _) => *ty, // We only accept `Box` with the default allocator. _ if ty.is_box_global(*self.tcx) => ty.boxed_ty(), _ => return Ok(None), diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 1e7ee208af1..633caf8d092 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -40,6 +40,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { const_eval::provide(providers); + providers.tag_for_variant = const_eval::tag_for_variant_provider; providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider; providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider; providers.eval_static_initializer = const_eval::eval_static_initializer_provider; 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 a93e8138aa4..da8e28d0298 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -558,7 +558,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_), + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, _, ) => {} Rvalue::ShallowInitBox(_, _) => {} diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 68270dab284..1664475f52a 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -4,6 +4,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; use rustc_infer::traits::Reveal; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -84,7 +85,7 @@ impl<'tcx> MirPass<'tcx> for Validator { cfg_checker.check_cleanup_control_flow(); // Also run the TypeChecker. - for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body) { + for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body, body) { cfg_checker.fail(location, msg); } @@ -345,11 +346,21 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.fail(location, format!("explicit `{kind:?}` is forbidden")); } } + StatementKind::Coverage(coverage) => { + let kind = &coverage.kind; + if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) + && let CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. } = kind + { + self.fail( + location, + format!("{kind:?} should have been removed after analysis"), + ); + } + } StatementKind::Assign(..) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Intrinsic(_) - | StatementKind::Coverage(_) | StatementKind::ConstEvalCounter | StatementKind::PlaceMention(..) | StatementKind::Nop => {} @@ -530,19 +541,25 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { /// A faster version of the validation pass that only checks those things which may break when /// instantiating any generic parameters. +/// +/// `caller_body` is used to detect cycles in MIR inlining and MIR validation before +/// `optimized_mir` is available. pub fn validate_types<'tcx>( tcx: TyCtxt<'tcx>, mir_phase: MirPhase, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, + caller_body: &Body<'tcx>, ) -> Vec<(Location, String)> { - let mut type_checker = TypeChecker { body, tcx, param_env, mir_phase, failures: Vec::new() }; + let mut type_checker = + TypeChecker { body, caller_body, tcx, param_env, mir_phase, failures: Vec::new() }; type_checker.visit_body(body); type_checker.failures } struct TypeChecker<'a, 'tcx> { body: &'a Body<'tcx>, + caller_body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, mir_phase: MirPhase, @@ -694,8 +711,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } &ty::Coroutine(def_id, args) => { let f_ty = if let Some(var) = parent_ty.variant_index { - let gen_body = if def_id == self.body.source.def_id() { - self.body + // If we're currently validating an inlined copy of this body, + // then it will no longer be parameterized over the original + // args of the coroutine. Otherwise, we prefer to use this body + // since we may be in the process of computing this MIR in the + // first place. + let gen_body = if def_id == self.caller_body.source.def_id() { + self.caller_body } else { self.tcx.optimized_mir(def_id) }; @@ -1157,7 +1179,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Repeat(_, _) | Rvalue::ThreadLocalRef(_) | Rvalue::AddressOf(_, _) - | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbCheck(_), _) + | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _) | Rvalue::Discriminant(_) => {} } self.super_rvalue(rvalue, location); diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 2b80623ab45..f3db7d4cd42 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -33,7 +33,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnPtr(_) | ty::Never diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 32202ac3ede..eab6d8168ca 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -20,6 +20,7 @@ //! | ----------------------- | ------------------- | ------------------------------- | //! | `Lrc<T>` | `rc::Rc<T>` | `sync::Arc<T>` | //! |` Weak<T>` | `rc::Weak<T>` | `sync::Weak<T>` | +//! | `LRef<'a, T>` [^2] | `&'a mut T` | `&'a T` | //! | | | | //! | `AtomicBool` | `Cell<bool>` | `atomic::AtomicBool` | //! | `AtomicU32` | `Cell<u32>` | `atomic::AtomicU32` | @@ -38,7 +39,7 @@ //! of a `RefCell`. This is appropriate when interior mutability is not //! required. //! -//! [^2] `MTLockRef` is a typedef. +//! [^2] `MTRef`, `MTLockRef` are type aliases. pub use crate::marker::*; use std::collections::HashMap; @@ -208,7 +209,7 @@ cfg_match! { use std::cell::RefCell as InnerRwLock; - pub type MTLockRef<'a, T> = &'a mut MTLock<T>; + pub type LRef<'a, T> = &'a mut T; #[derive(Debug, Default)] pub struct MTLock<T>(T); @@ -274,7 +275,7 @@ cfg_match! { pub use std::sync::Arc as Lrc; pub use std::sync::Weak as Weak; - pub type MTLockRef<'a, T> = &'a MTLock<T>; + pub type LRef<'a, T> = &'a T; #[derive(Debug, Default)] pub struct MTLock<T>(Lock<T>); @@ -314,6 +315,8 @@ cfg_match! { } } +pub type MTLockRef<'a, T> = LRef<'a, MTLock<T>>; + #[derive(Default)] #[cfg_attr(parallel_compiler, repr(align(64)))] pub struct CacheAligned<T>(pub T); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 938f9f0beaa..716e31080dd 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -890,7 +890,7 @@ pub fn version_at_macro_invocation( let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); let opts = config::Options::default(); let sysroot = filesearch::materialize_sysroot(opts.maybe_sysroot.clone()); - let target = config::build_target_config(early_dcx, &opts, None, &sysroot); + let target = config::build_target_config(early_dcx, &opts, &sysroot); get_codegen_backend(early_dcx, &sysroot, backend_name, &target).print_version(); } @@ -1100,7 +1100,7 @@ pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) -> let opts = config::Options::default(); let sysroot = filesearch::materialize_sysroot(opts.maybe_sysroot.clone()); - let target = config::build_target_config(early_dcx, &opts, None, &sysroot); + let target = config::build_target_config(early_dcx, &opts, &sysroot); get_codegen_backend(early_dcx, &sysroot, backend_name, &target).print_passes(); return true; diff --git a/compiler/rustc_driver_impl/src/signal_handler.rs b/compiler/rustc_driver_impl/src/signal_handler.rs index deca1082221..441219eec90 100644 --- a/compiler/rustc_driver_impl/src/signal_handler.rs +++ b/compiler/rustc_driver_impl/src/signal_handler.rs @@ -1,6 +1,7 @@ //! Signal handler for rustc //! Primarily used to extract a backtrace from stack overflow +use rustc_interface::util::{DEFAULT_STACK_SIZE, STACK_SIZE}; use std::alloc::{alloc, Layout}; use std::{fmt, mem, ptr}; @@ -100,7 +101,10 @@ extern "C" fn print_stack_trace(_: libc::c_int) { written += 1; } raw_errln!("note: we would appreciate a report at https://github.com/rust-lang/rust"); - written += 1; + // get the current stack size WITHOUT blocking and double it + let new_size = STACK_SIZE.get().copied().unwrap_or(DEFAULT_STACK_SIZE) * 2; + raw_errln!("help: you can increase rustc's stack size by setting RUST_MIN_STACK={new_size}"); + written += 2; if written > 24 { // We probably just scrolled the earlier "we got SIGSEGV" message off the terminal raw_errln!("note: backtrace dumped due to SIGSEGV! resuming signal"); diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 1f0488130b2..fdd1a87cae8 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -33,6 +33,9 @@ expand_duplicate_matcher_binding = duplicate matcher binding expand_expected_comma_in_list = expected token: `,` +expand_expected_paren_or_brace = + expected `(` or `{"{"}`, found `{$token}` + expand_explain_doc_comment_inner = inner doc comments expand to `#![doc = "..."]`, which is what this macro attempted to match diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index e71047f94fa..30559871b4e 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -1,6 +1,6 @@ use crate::base::ExtCtxt; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, PatKind, UnOp}; +use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp}; use rustc_ast::{attr, token, util::literal}; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -524,7 +524,7 @@ impl<'a> ExtCtxt<'a> { } pub fn expr_match(&self, span: Span, arg: P<ast::Expr>, arms: ThinVec<ast::Arm>) -> P<Expr> { - self.expr(span, ast::ExprKind::Match(arg, arms)) + self.expr(span, ast::ExprKind::Match(arg, arms, MatchKind::Prefix)) } pub fn expr_if( diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index fe901603c73..21ce5e1d81e 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -448,3 +448,11 @@ pub struct InvalidFragmentSpecifier { pub fragment: Ident, pub help: String, } + +#[derive(Diagnostic)] +#[diag(expand_expected_paren_or_brace)] +pub struct ExpectedParenOrBrace<'a> { + #[primary_span] + pub span: Span, + pub token: Cow<'a, str>, +} diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index ac5136539c3..a31be05ccc4 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -392,12 +392,7 @@ pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize { #[derive(Debug, Clone)] pub(crate) enum NamedMatch { MatchedSeq(Vec<NamedMatch>), - - // A metavar match of type `tt`. - MatchedTokenTree(rustc_ast::tokenstream::TokenTree), - - // A metavar match of any type other than `tt`. - MatchedNonterminal(Lrc<(Nonterminal, rustc_span::Span)>), + MatchedSingle(ParseNtResult<Lrc<(Nonterminal, Span)>>), } /// Performs a token equality check, ignoring syntax context (that is, an unhygienic comparison) @@ -691,11 +686,11 @@ impl TtParser { } Ok(nt) => nt, }; - let m = match nt { - ParseNtResult::Nt(nt) => MatchedNonterminal(Lrc::new((nt, span))), - ParseNtResult::Tt(tt) => MatchedTokenTree(tt), - }; - mp.push_match(next_metavar, seq_depth, m); + mp.push_match( + next_metavar, + seq_depth, + MatchedSingle(nt.map_nt(|nt| (Lrc::new((nt, span))))), + ); mp.idx += 1; } else { unreachable!() diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 3f29d7f7465..7099f1b0d35 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -5,7 +5,7 @@ use crate::mbe; use crate::mbe::diagnostics::{annotate_doc_comment, parse_failure_msg}; use crate::mbe::macro_check; use crate::mbe::macro_parser::{Error, ErrorReported, Failure, Success, TtParser}; -use crate::mbe::macro_parser::{MatchedSeq, MatchedTokenTree, MatcherLoc}; +use crate::mbe::macro_parser::{MatcherLoc, NamedMatch::*}; use crate::mbe::transcribe::transcribe; use ast::token::IdentIsRaw; @@ -22,7 +22,7 @@ use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, }; use rustc_lint_defs::BuiltinLintDiag; -use rustc_parse::parser::{Parser, Recovery}; +use rustc_parse::parser::{ParseNtResult, Parser, Recovery}; use rustc_session::parse::ParseSess; use rustc_session::Session; use rustc_span::edition::Edition; @@ -479,7 +479,7 @@ pub fn compile_declarative_macro( MatchedSeq(s) => s .iter() .map(|m| { - if let MatchedTokenTree(tt) = m { + if let MatchedSingle(ParseNtResult::Tt(tt)) = m { let tt = mbe::quoted::parse( &TokenStream::new(vec![tt.clone()]), true, @@ -505,7 +505,7 @@ pub fn compile_declarative_macro( MatchedSeq(s) => s .iter() .map(|m| { - if let MatchedTokenTree(tt) = m { + if let MatchedSingle(ParseNtResult::Tt(tt)) = m { return mbe::quoted::parse( &TokenStream::new(vec![tt.clone()]), false, diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 5fd3716743b..06c1612ddba 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -194,9 +194,11 @@ fn parse_tree<'a>( } Delimiter::Parenthesis => {} _ => { - let tok = pprust::token_kind_to_string(&token::OpenDelim(delim)); - let msg = format!("expected `(` or `{{`, found `{tok}`"); - sess.dcx().span_err(delim_span.entire(), msg); + let token = pprust::token_kind_to_string(&token::OpenDelim(delim)); + sess.dcx().emit_err(errors::ExpectedParenOrBrace { + span: delim_span.entire(), + token, + }); } } } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 8fcd468e34b..dad83984c8b 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -3,14 +3,14 @@ use crate::errors::{ CountRepetitionMisplaced, MetaVarExprUnrecognizedVar, MetaVarsDifSeqMatchers, MustRepeatOnce, NoSyntaxVarsExprRepeat, VarStillRepeating, }; -use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch}; +use crate::mbe::macro_parser::{NamedMatch, NamedMatch::*}; use crate::mbe::{self, KleeneOp, MetaVarExpr}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::Diag; -use rustc_errors::{pluralize, PResult}; +use rustc_errors::{pluralize, Diag, PResult}; +use rustc_parse::parser::ParseNtResult; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; use rustc_span::{with_metavar_spans, Span, SyntaxContext}; @@ -250,26 +250,25 @@ pub(super) fn transcribe<'a>( // the meta-var. let ident = MacroRulesNormalizedIdent::new(original_ident); if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) { - match cur_matched { - MatchedTokenTree(tt) => { + let tt = match cur_matched { + MatchedSingle(ParseNtResult::Tt(tt)) => { // `tt`s are emitted into the output stream directly as "raw tokens", // without wrapping them into groups. - let tt = maybe_use_metavar_location(cx, &stack, sp, tt, &mut marker); - result.push(tt); + maybe_use_metavar_location(cx, &stack, sp, tt, &mut marker) } - MatchedNonterminal(nt) => { + MatchedSingle(ParseNtResult::Nt(nt)) => { // Other variables are emitted into the output stream as groups with // `Delimiter::Invisible` to maintain parsing priorities. // `Interpolated` is currently used for such groups in rustc parser. marker.visit_span(&mut sp); - result - .push(TokenTree::token_alone(token::Interpolated(nt.clone()), sp)); + TokenTree::token_alone(token::Interpolated(nt.clone()), sp) } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. return Err(cx.dcx().create_err(VarStillRepeating { span: sp, ident })); } - } + }; + result.push(tt) } else { // If we aren't able to match the meta-var, we push it back into the result but // with modified syntax context. (I believe this supports nested macros). @@ -424,7 +423,7 @@ fn lookup_cur_matched<'a>( interpolations.get(&ident).map(|mut matched| { for &(idx, _) in repeats { match matched { - MatchedTokenTree(_) | MatchedNonterminal(_) => break, + MatchedSingle(_) => break, MatchedSeq(ads) => matched = ads.get(idx).unwrap(), } } @@ -514,7 +513,7 @@ fn lockstep_iter_size( let name = MacroRulesNormalizedIdent::new(*name); match lookup_cur_matched(name, interpolations, repeats) { Some(matched) => match matched { - MatchedTokenTree(_) | MatchedNonterminal(_) => LockstepIterSize::Unconstrained, + MatchedSingle(_) => LockstepIterSize::Unconstrained, MatchedSeq(ads) => LockstepIterSize::Constraint(ads.len(), name), }, _ => LockstepIterSize::Unconstrained, @@ -557,7 +556,7 @@ fn count_repetitions<'a>( // (or at the top-level of `matched` if no depth is given). fn count<'a>(depth_curr: usize, depth_max: usize, matched: &NamedMatch) -> PResult<'a, usize> { match matched { - MatchedTokenTree(_) | MatchedNonterminal(_) => Ok(1), + MatchedSingle(_) => Ok(1), MatchedSeq(named_matches) => { if depth_curr == depth_max { Ok(named_matches.len()) @@ -571,7 +570,7 @@ fn count_repetitions<'a>( /// Maximum depth fn depth(counter: usize, matched: &NamedMatch) -> usize { match matched { - MatchedTokenTree(_) | MatchedNonterminal(_) => counter, + MatchedSingle(_) => counter, MatchedSeq(named_matches) => { let rslt = counter + 1; if let Some(elem) = named_matches.first() { depth(rslt, elem) } else { rslt } @@ -599,7 +598,7 @@ fn count_repetitions<'a>( } } - if let MatchedTokenTree(_) | MatchedNonterminal(_) = matched { + if let MatchedSingle(_) = matched { return Err(cx.dcx().create_err(CountRepetitionMisplaced { span: sp.entire() })); } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index d4d7833cb21..22cf50fce7f 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -597,9 +597,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!( - rustc_never_type_mode, Normal, template!(NameValueStr: "fallback_to_unit|fallback_to_niko|fallback_to_never|no_fallback"), ErrorFollowing, + rustc_never_type_options, + Normal, + template!(List: r#"/*opt*/ fallback = "unit|niko|never|no""#), + ErrorFollowing, EncodeCrossCrate::No, - "`rustc_never_type_fallback` is used to experiment with never type fallback and work on \ + "`rustc_never_type_options` is used to experiment with never type fallback and work on \ never type stabilization, and will never be stable" ), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a3b13c4d907..8d72f4924d6 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -436,6 +436,8 @@ declare_features! ( (unstable, deprecated_safe, "1.61.0", Some(94978)), /// Allows having using `suggestion` in the `#[deprecated]` attribute. (unstable, deprecated_suggestion, "1.61.0", Some(94785)), + /// Allows deref patterns. + (incomplete, deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121)), /// Controls errors in trait implementations. (unstable, do_not_recommend, "1.67.0", Some(51992)), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. @@ -557,6 +559,8 @@ declare_features! ( (unstable, offset_of_nested, "1.77.0", Some(120140)), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882)), + /// Allows postfix match `expr.match { ... }` + (unstable, postfix_match, "CURRENT_RUSTC_VERSION", Some(121618)), /// Allows macro attributes on expressions, statements and non-inline modules. (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 1810193c16b..e8cecb1930f 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -810,6 +810,8 @@ pub enum LifetimeRes { param: NodeId, /// Id of the introducing place. See `Param`. binder: NodeId, + /// Kind of elided lifetime + kind: hir::MissingLifetimeKind, }, /// This variant is used for anonymous lifetimes that we did not resolve during /// late resolution. Those lifetimes will be inferred by typechecking. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ca5d2930e82..cc0ab05d422 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -428,10 +428,6 @@ pub enum TraitBoundModifier { MaybeConst, } -/// The AST represents all type param bounds as types. -/// `typeck::collect::compute_bounds` matches these against -/// the "special" built-in traits (see `middle::lang_items`) and -/// detects `Copy`, `Send` and `Sync`. #[derive(Clone, Copy, Debug, HashStable_Generic)] pub enum GenericBound<'hir> { Trait(PolyTraitRef<'hir>, TraitBoundModifier), @@ -456,6 +452,18 @@ impl GenericBound<'_> { pub type GenericBounds<'hir> = &'hir [GenericBound<'hir>]; +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable_Generic, Debug)] +pub enum MissingLifetimeKind { + /// An explicit `'_`. + Underscore, + /// An elided lifetime `&' ty`. + Ampersand, + /// An elided lifetime in brackets with written brackets. + Comma, + /// An elided lifetime with elided brackets. + Brackets, +} + #[derive(Copy, Clone, Debug, HashStable_Generic)] pub enum LifetimeParamKind { // Indicates that the lifetime definition was explicitly declared (e.g., in @@ -464,7 +472,7 @@ pub enum LifetimeParamKind { // Indication that the lifetime was elided (e.g., in both cases in // `fn foo(x: &u8) -> &'_ u8 { x }`). - Elided, + Elided(MissingLifetimeKind), // Indication that the lifetime name was somehow in error. Error, @@ -512,7 +520,7 @@ impl<'hir> GenericParam<'hir> { /// /// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information. pub fn is_elided_lifetime(&self) -> bool { - matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided }) + matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(_) }) } } @@ -1003,7 +1011,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, - Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it), + Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), Slice(before, slice, after) => { @@ -1030,7 +1038,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} - Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it), + Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), Slice(before, slice, after) => { @@ -1173,6 +1181,9 @@ pub enum PatKind<'hir> { /// A `box` pattern. Box(&'hir Pat<'hir>), + /// A `deref` pattern (currently `deref!()` macro-based syntax). + Deref(&'hir Pat<'hir>), + /// A reference pattern (e.g., `&mut (a, b)`). Ref(&'hir Pat<'hir>, Mutability), @@ -1209,7 +1220,7 @@ pub struct Stmt<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum StmtKind<'hir> { /// A local (`let`) binding. - Let(&'hir Local<'hir>), + Let(&'hir LetStmt<'hir>), /// An item binding. Item(ItemId), @@ -1223,7 +1234,7 @@ pub enum StmtKind<'hir> { /// Represents a `let` statement (i.e., `let <pat>:<ty> = <init>;`). #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub struct Local<'hir> { +pub struct LetStmt<'hir> { pub pat: &'hir Pat<'hir>, /// Type annotation, if any (otherwise the type will be inferred). pub ty: Option<&'hir Ty<'hir>>, @@ -1253,13 +1264,13 @@ pub struct Arm<'hir> { pub body: &'hir Expr<'hir>, } -/// Represents a `let <pat>[: <ty>] = <expr>` expression (not a [`Local`]), occurring in an `if-let` +/// Represents a `let <pat>[: <ty>] = <expr>` expression (not a [`LetStmt`]), occurring in an `if-let` /// or `let-else`, evaluating to a boolean. Typically the pattern is refutable. /// /// In an `if let`, imagine it as `if (let <pat> = <expr>) { ... }`; in a let-else, it is part of /// the desugaring to if-let. Only let-else supports the type annotation at present. #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub struct Let<'hir> { +pub struct LetExpr<'hir> { pub span: Span, pub pat: &'hir Pat<'hir>, pub ty: Option<&'hir Ty<'hir>>, @@ -1845,14 +1856,14 @@ pub enum ExprKind<'hir> { /// Wraps the expression in a terminating scope. /// This makes it semantically equivalent to `{ let _t = expr; _t }`. /// - /// This construct only exists to tweak the drop order in HIR lowering. + /// This construct only exists to tweak the drop order in AST lowering. /// An example of that is the desugaring of `for` loops. DropTemps(&'hir Expr<'hir>), /// A `let $pat = $expr` expression. /// - /// These are not `Local` and only occur as expressions. + /// These are not [`LetStmt`] and only occur as expressions. /// The `let Some(x) = foo()` in `if let Some(x) = foo()` is an example of `Let(..)`. - Let(&'hir Let<'hir>), + Let(&'hir LetExpr<'hir>), /// An `if` block, with an optional else block. /// /// I.e., `if <expr> { <expr> } else { <expr> }`. @@ -2004,6 +2015,8 @@ pub enum LocalSource { pub enum MatchSource { /// A `match _ { .. }`. Normal, + /// A `expr.match { .. }`. + Postfix, /// A desugared `for _ in _ { .. }` loop. ForLoopDesugar, /// A desugared `?` operator. @@ -2020,6 +2033,7 @@ impl MatchSource { use MatchSource::*; match self { Normal => "match", + Postfix => ".match", ForLoopDesugar => "for", TryDesugar(_) => "?", AwaitDesugar => ".await", @@ -2278,7 +2292,7 @@ pub enum ImplItemKind<'hir> { /// Bind a type to an associated type (i.e., `A = Foo`). /// /// Bindings like `A: Debug` are represented as a special type `A = -/// $::Debug` that is understood by the astconv code. +/// $::Debug` that is understood by the HIR ty lowering code. /// /// FIXME(alexreg): why have a separate type for the binding case, /// wouldn't it be better to make the `ty` field an enum like the @@ -3515,7 +3529,7 @@ pub enum Node<'hir> { PatField(&'hir PatField<'hir>), Arm(&'hir Arm<'hir>), Block(&'hir Block<'hir>), - Local(&'hir Local<'hir>), + LetStmt(&'hir LetStmt<'hir>), /// `Ctor` refers to the constructor of an enum variant or struct. Only tuple or unit variants /// with synthesized constructors. Ctor(&'hir VariantData<'hir>), @@ -3571,7 +3585,7 @@ impl<'hir> Node<'hir> { | Node::Ctor(..) | Node::Pat(..) | Node::Arm(..) - | Node::Local(..) + | Node::LetStmt(..) | Node::Crate(..) | Node::Ty(..) | Node::TraitRef(..) @@ -3743,7 +3757,7 @@ impl<'hir> Node<'hir> { expect_pat_field, &'hir PatField<'hir>, Node::PatField(n), n; expect_arm, &'hir Arm<'hir>, Node::Arm(n), n; expect_block, &'hir Block<'hir>, Node::Block(n), n; - expect_local, &'hir Local<'hir>, Node::Local(n), n; + expect_let_stmt, &'hir LetStmt<'hir>, Node::LetStmt(n), n; expect_ctor, &'hir VariantData<'hir>, Node::Ctor(n), n; expect_lifetime, &'hir Lifetime, Node::Lifetime(n), n; expect_generic_param, &'hir GenericParam<'hir>, Node::GenericParam(n), n; @@ -3773,7 +3787,7 @@ mod size_asserts { static_assert_size!(ImplItemKind<'_>, 40); static_assert_size!(Item<'_>, 88); static_assert_size!(ItemKind<'_>, 56); - static_assert_size!(Local<'_>, 64); + static_assert_size!(LetStmt<'_>, 64); static_assert_size!(Param<'_>, 32); static_assert_size!(Pat<'_>, 72); static_assert_size!(Path<'_>, 40); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 186bb234a45..8c44f21a57b 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -320,7 +320,7 @@ pub trait Visitor<'v>: Sized { fn visit_foreign_item(&mut self, i: &'v ForeignItem<'v>) -> Self::Result { walk_foreign_item(self, i) } - fn visit_local(&mut self, l: &'v Local<'v>) -> Self::Result { + fn visit_local(&mut self, l: &'v LetStmt<'v>) -> Self::Result { walk_local(self, l) } fn visit_block(&mut self, b: &'v Block<'v>) -> Self::Result { @@ -606,7 +606,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( V::Result::output() } -pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) -> V::Result { +pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v LetStmt<'v>) -> V::Result { // Intentionally visiting the expr first - the initialization expr // dominates the local's definition. visit_opt!(visitor, visit_expr, local.init); @@ -660,7 +660,9 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: PatKind::Tuple(tuple_elements, _) => { walk_list!(visitor, visit_pat, tuple_elements); } - PatKind::Box(ref subpattern) | PatKind::Ref(ref subpattern, _) => { + PatKind::Box(ref subpattern) + | PatKind::Deref(ref subpattern) + | PatKind::Ref(ref subpattern, _) => { try_visit!(visitor.visit_pat(subpattern)); } PatKind::Binding(_, _hir_id, ident, ref optional_subpattern) => { @@ -753,7 +755,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) ExprKind::DropTemps(ref subexpression) => { try_visit!(visitor.visit_expr(subexpression)); } - ExprKind::Let(Let { span: _, pat, ty, init, is_recovered: _ }) => { + ExprKind::Let(LetExpr { span: _, pat, ty, init, is_recovered: _ }) => { // match the visit order in walk_local try_visit!(visitor.visit_expr(init)); try_visit!(visitor.visit_pat(pat)); diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 5118bf5c3b7..dbf86f5cf74 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -214,6 +214,7 @@ language_item_table! { FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0); + FusedIterator, sym::fused_iterator, fused_iterator_trait, Target::Trait, GenericRequirement::Exact(0); Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); AsyncIterator, sym::async_iterator, async_iterator_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index b69f679880d..d5465bb5dd5 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -1,5 +1,5 @@ -//! Bounds are restrictions applied to some types after they've been converted into the -//! `ty` form from the HIR. +//! Bounds are restrictions applied to some types after they've been lowered from the HIR to the +//! [`rustc_middle::ty`] form. use rustc_hir::LangItem; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -42,7 +42,7 @@ impl<'tcx> Bounds<'tcx> { tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, span: Span, - polarity: ty::ImplPolarity, + polarity: ty::PredicatePolarity, ) { self.push_trait_bound_inner(tcx, trait_ref, span, polarity); } @@ -52,7 +52,7 @@ impl<'tcx> Bounds<'tcx> { tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, span: Span, - polarity: ty::ImplPolarity, + polarity: ty::PredicatePolarity, ) { self.clauses.push(( trait_ref diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d1fed13ee9f..1286a724e95 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -382,8 +382,8 @@ fn check_opaque_meets_bounds<'tcx>( Ok(()) => {} Err(ty_err) => { // Some types may be left "stranded" if they can't be reached - // from an astconv'd bound but they're mentioned in the HIR. This - // will happen, e.g., when a nested opaque is inside of a non- + // from a lowered rustc_middle bound but they're mentioned in the HIR. + // This will happen, e.g., when a nested opaque is inside of a non- // existent associated type, like `impl Trait<Missing = impl Trait>`. // See <tests/ui/impl-trait/stranded-opaque.rs>. let ty_err = ty_err.to_string(tcx); @@ -499,7 +499,7 @@ fn is_enum_of_nonnullable_ptr<'tcx>( fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) { if tcx.codegen_fn_attrs(def_id).import_linkage.is_some() { if match tcx.type_of(def_id).instantiate_identity().kind() { - ty::RawPtr(_) => false, + ty::RawPtr(_, _) => false, ty::Adt(adt_def, args) => !is_enum_of_nonnullable_ptr(tcx, *adt_def, *args), _ => true, } { @@ -934,10 +934,13 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { // No: char, "fat" pointers, compound types match e.kind() { ty::Param(_) => (), // pass struct<T>(T, T, T, T) through, let monomorphization catch errors - ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_) => (), // struct(u8, u8, u8, u8) is ok + ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) => (), // struct(u8, u8, u8, u8) is ok ty::Array(t, _) if matches!(t.kind(), ty::Param(_)) => (), // pass struct<T>([T; N]) through, let monomorphization catch errors ty::Array(t, _clen) - if matches!(t.kind(), ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_)) => + if matches!( + t.kind(), + ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) + ) => { /* struct([f32; 4]) is ok */ } _ => { struct_span_code_err!( diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index ec9e928ce59..50f88eb970a 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -639,10 +639,9 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( } } - if !unnormalized_trait_sig.output().references_error() { - debug_assert!( - !collector.types.is_empty(), - "expect >0 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`" + if !unnormalized_trait_sig.output().references_error() && collector.types.is_empty() { + tcx.dcx().delayed_bug( + "expect >0 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`", ); } @@ -746,7 +745,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( // We may not collect all RPITITs that we see in the HIR for a trait signature // because an RPITIT was located within a missing item. Like if we have a sig - // returning `-> Missing<impl Sized>`, that gets converted to `-> [type error]`, + // returning `-> Missing<impl Sized>`, that gets converted to `-> {type error}`, // and when walking through the signature we end up never collecting the def id // of the `impl Sized`. Insert that here, so we don't ICE later. for assoc_item in tcx.associated_types_for_impl_traits_in_associated_fn(trait_m.def_id) { @@ -1305,7 +1304,7 @@ fn compare_number_of_generics<'tcx>( .iter() .filter(|p| match p.kind { hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Elided, + kind: hir::LifetimeParamKind::Elided(_), } => { // A fn can have an arbitrary number of extra elided lifetimes for the // same signature. diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 07054c184f4..f482ae4f5fa 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -127,8 +127,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::variant_count | sym::is_val_statically_known | sym::ptr_mask - | sym::check_language_ub - | sym::check_library_ub + | sym::ub_checks | sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic @@ -191,7 +190,7 @@ pub fn check_intrinsic_type( ty::BoundRegion { var: ty::BoundVar::from_u32(2), kind: ty::BrEnv }, ); let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]); - (Ty::new_ref(tcx, env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty) + (Ty::new_ref(tcx, env_region, va_list_ty, mutbl), va_list_ty) }) }; @@ -240,15 +239,9 @@ pub fn check_intrinsic_type( sym::prefetch_read_data | sym::prefetch_write_data | sym::prefetch_read_instruction - | sym::prefetch_write_instruction => ( - 1, - 0, - vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.i32, - ], - Ty::new_unit(tcx), - ), + | sym::prefetch_write_instruction => { + (1, 0, vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.i32], Ty::new_unit(tcx)) + } sym::needs_drop => (1, 0, vec![], tcx.types.bool), sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), @@ -257,28 +250,22 @@ pub fn check_intrinsic_type( sym::arith_offset => ( 1, 0, - vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.isize, - ], - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.isize], + Ty::new_imm_ptr(tcx, param(0)), ), sym::ptr_mask => ( 1, 0, - vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.usize, - ], - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + vec![Ty::new_imm_ptr(tcx, param(0)), tcx.types.usize], + Ty::new_imm_ptr(tcx, param(0)), ), sym::copy | sym::copy_nonoverlapping => ( 1, 0, vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), + Ty::new_imm_ptr(tcx, param(0)), + Ty::new_mut_ptr(tcx, param(0)), tcx.types.usize, ], Ty::new_unit(tcx), @@ -287,8 +274,8 @@ pub fn check_intrinsic_type( 1, 0, vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + Ty::new_mut_ptr(tcx, param(0)), + Ty::new_imm_ptr(tcx, param(0)), tcx.types.usize, ], Ty::new_unit(tcx), @@ -300,11 +287,7 @@ pub fn check_intrinsic_type( sym::write_bytes | sym::volatile_set_memory => ( 1, 0, - vec![ - Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), - tcx.types.u8, - tcx.types.usize, - ], + vec![Ty::new_mut_ptr(tcx, param(0)), tcx.types.u8, tcx.types.usize], Ty::new_unit(tcx), ), @@ -500,6 +483,8 @@ pub fn check_intrinsic_type( (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx)) } + sym::typed_swap => (1, 1, vec![Ty::new_mut_ptr(tcx, param(0)); 2], Ty::new_unit(tcx)), + sym::discriminant_value => { let assoc_items = tcx.associated_item_def_ids( tcx.require_lang_item(hir::LangItem::DiscriminantKind, None), @@ -585,7 +570,7 @@ pub fn check_intrinsic_type( (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize) } - sym::check_language_ub | sym::check_library_ub => (0, 1, Vec::new(), tcx.types.bool), + sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool), sym::simd_eq | sym::simd_ne diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 9de660407d7..df4db3ec3fb 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -6,7 +6,9 @@ use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::Symbol; use rustc_target::abi::FieldIdx; -use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType}; +use rustc_target::asm::{ + InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, +}; pub struct InlineAsmCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -62,9 +64,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), ty::FnPtr(_) => Some(asm_ty_isize), - ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => { - Some(asm_ty_isize) - } + ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize), ty::Adt(adt, args) if adt.repr().simd() => { let fields = &adt.non_enum_variant().fields; let elem_ty = fields[FieldIdx::from_u32(0)].ty(self.tcx, args); @@ -253,8 +253,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } // Check whether a modifier is suggested for using this type. - if let Some((suggested_modifier, suggested_result)) = - reg_class.suggest_modifier(asm_arch, asm_ty) + if let Some(ModifierInfo { + modifier: suggested_modifier, + result: suggested_result, + size: suggested_size, + }) = reg_class.suggest_modifier(asm_arch, asm_ty) { // Search for any use of this operand without a modifier and emit // the suggestion for them. @@ -268,8 +271,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } } if !spans.is_empty() { - let (default_modifier, default_result) = - reg_class.default_modifier(asm_arch).unwrap(); + let ModifierInfo { + modifier: default_modifier, + result: default_result, + size: default_size, + } = reg_class.default_modifier(asm_arch).unwrap(); self.tcx.node_span_lint( lint::builtin::ASM_SUB_REGISTER, expr.hir_id, @@ -278,10 +284,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { |lint| { lint.span_label(expr.span, "for this argument"); lint.help(format!( - "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`", + "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", )); lint.help(format!( - "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`", + "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", )); }, ); diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 4c4ff28808e..8760901b71b 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -431,7 +431,7 @@ fn fn_sig_suggestion<'tcx>( let asyncness = if tcx.asyncness(assoc.def_id).is_async() { output = if let ty::Alias(_, alias_ty) = *output.kind() { - tcx.explicit_item_bounds(alias_ty.def_id) + tcx.explicit_item_super_predicates(alias_ty.def_id) .iter_instantiated_copied(tcx, alias_ty.args) .find_map(|(bound, _)| bound.as_projection_clause()?.no_bound_vars()?.term.ty()) .unwrap_or_else(|| { diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 3c26729eff8..dcabac6d780 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Arm, Block, Expr, Local, Pat, PatKind, Stmt}; +use rustc_hir::{Arm, Block, Expr, LetStmt, Pat, PatKind, Stmt}; use rustc_index::Idx; use rustc_middle::middle::region::*; use rustc_middle::ty::TyCtxt; @@ -123,7 +123,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h for (i, statement) in blk.stmts.iter().enumerate() { match statement.kind { - hir::StmtKind::Let(hir::Local { els: Some(els), .. }) => { + hir::StmtKind::Let(LetStmt { els: Some(els), .. }) => { // Let-else has a special lexical structure for variables. // First we take a checkpoint of the current scope context here. let mut prev_cx = visitor.cx; @@ -668,7 +668,7 @@ fn resolve_local<'tcx>( | PatKind::TupleStruct(_, subpats, _) | PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(p)), - PatKind::Box(subpat) => is_binding_pat(subpat), + PatKind::Box(subpat) | PatKind::Deref(subpat) => is_binding_pat(subpat), PatKind::Ref(_, _) | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..) @@ -760,7 +760,7 @@ impl<'tcx> RegionResolutionVisitor<'tcx> { fn enter_node_scope_with_dtor(&mut self, id: hir::ItemLocalId) { // If node was previously marked as a terminating scope during the - // recursive visit of its parent node in the AST, then we need to + // recursive visit of its parent node in the HIR, then we need to // account for the destruction scope representing the scope of // the destructors that run immediately after it completes. if self.terminating_scopes.contains(&id) { @@ -855,7 +855,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { resolve_expr(self, ex); } - fn visit_local(&mut self, l: &'tcx Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) { resolve_local(self, Some(l.pat), l.init) } } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index b5377e40bfd..4fd7c870fc7 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -272,7 +272,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<() } Some(ty::ImplPolarity::Negative) => { let ast::ImplPolarity::Negative(span) = impl_.polarity else { - bug!("impl_polarity query disagrees with impl's polarity in AST"); + bug!("impl_polarity query disagrees with impl's polarity in HIR"); }; // FIXME(#27579): what amount of WF checking do we need for neg impls? if let hir::Defaultness::Default { .. } = impl_.defaultness { @@ -954,7 +954,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), hir_ty.span, "using function pointers as const generic parameters is forbidden", ), - ty::RawPtr(_) => tcx.dcx().struct_span_err( + ty::RawPtr(_, _) => tcx.dcx().struct_span_err( hir_ty.span, "using raw pointers as const generic parameters is forbidden", ), @@ -1322,7 +1322,7 @@ fn check_impl<'tcx>( trait_ref, ); let trait_pred = - ty::TraitPredicate { trait_ref, polarity: ty::ImplPolarity::Positive }; + ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive }; let mut obligations = traits::wf::trait_obligations( wfcx.infcx, wfcx.param_env, @@ -1866,7 +1866,7 @@ fn check_variances_for_type_defn<'tcx>( .iter() .filter_map(|predicate| match predicate { hir::WherePredicate::BoundPredicate(predicate) => { - match icx.to_ty(predicate.bounded_ty).kind() { + match icx.lower_ty(predicate.bounded_ty).kind() { ty::Param(data) => Some(Parameter(data.index)), _ => None, } diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 8d8b13d6cb3..5e404847656 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -195,7 +195,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() { Ok(()) } - (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => Ok(()), + (&RawPtr(_, a_mutbl), &RawPtr(_, b_mutbl)) if a_mutbl == b_mutbl => Ok(()), (&Adt(def_a, args_a), &Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => { if def_a != def_b { let source_path = tcx.def_path_str(def_a.did()); @@ -351,14 +351,17 @@ pub fn coerce_unsized_info<'tcx>( check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty)) } - (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => { - let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; - check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty)) - } + (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => check_mutbl( + ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }, + ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }, + &|ty| Ty::new_imm_ptr(tcx, ty), + ), - (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => { - check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty)) - } + (&ty::RawPtr(ty_a, mutbl_a), &ty::RawPtr(ty_b, mutbl_b)) => check_mutbl( + ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }, + ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }, + &|ty| Ty::new_imm_ptr(tcx, ty), + ), (&ty::Adt(def_a, args_a), &ty::Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => @@ -551,7 +554,7 @@ fn infringing_fields_error( } if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, .. })) = error_predicate.kind().skip_binder() { diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 32f31201254..067878091a7 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -162,7 +162,7 @@ impl<'tcx> InherentCollect<'tcx> { | ty::Str | ty::Array(..) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::Never | ty::FnPtr(_) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index b46a67d08eb..ca8a635ab5e 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -323,7 +323,7 @@ fn emit_orphan_check_error<'tcx>( let is_foreign = !trait_ref.def_id.is_local() && matches!(is_target_ty, IsFirstInputType::No); - match &ty.kind() { + match *ty.kind() { ty::Slice(_) => { push_to_foreign_or_name( is_foreign, @@ -354,14 +354,14 @@ fn emit_orphan_check_error<'tcx>( ty::Alias(ty::Opaque, ..) => { opaque.push(errors::OnlyCurrentTraitsOpaque { span }) } - ty::RawPtr(ptr_ty) => { + ty::RawPtr(ptr_ty, mutbl) => { if !self_ty.has_param() { - let mut_key = ptr_ty.mutbl.prefix_str(); + let mut_key = mutbl.prefix_str(); sugg = Some(errors::OnlyCurrentTraitsPointerSugg { wrapper_span: self_ty_span, struct_span: full_impl_span.shrink_to_lo(), mut_key, - ptr_ty: ptr_ty.ty, + ptr_ty, }); } pointer.push(errors::OnlyCurrentTraitsPointer { span, pointer: ty }); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 5cd6862786b..a705d3bc107 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -40,9 +40,9 @@ use std::cell::Cell; use std::iter; use std::ops::Bound; -use crate::astconv::AstConv; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::errors; +use crate::hir_ty_lowering::HirTyLowerer; pub use type_of::test_opaque_hidden_types; mod generics_of; @@ -61,6 +61,9 @@ pub fn provide(providers: &mut Providers) { type_alias_is_lazy: type_of::type_alias_is_lazy, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, + item_super_predicates: item_bounds::item_super_predicates, + explicit_item_super_predicates: item_bounds::explicit_item_super_predicates, + item_non_self_assumptions: item_bounds::item_non_self_assumptions, generics_of: generics_of::generics_of, predicates_of: predicates_of::predicates_of, predicates_defined_on, @@ -85,13 +88,12 @@ pub fn provide(providers: &mut Providers) { /////////////////////////////////////////////////////////////////////////// -/// Context specific to some particular item. This is what implements -/// [`AstConv`]. +/// Context specific to some particular item. This is what implements [`HirTyLowerer`]. /// /// # `ItemCtxt` vs `FnCtxt` /// /// `ItemCtxt` is primarily used to type-check item signatures and lower them -/// from HIR to their [`ty::Ty`] representation, which is exposed using [`AstConv`]. +/// from HIR to their [`ty::Ty`] representation, which is exposed using [`HirTyLowerer`]. /// It's also used for the bodies of items like structs where the body (the fields) /// are just signatures. /// @@ -108,11 +110,11 @@ pub fn provide(providers: &mut Providers) { /// `ItemCtxt` has information about the predicates that are defined /// on the trait. Unfortunately, this predicate information is /// available in various different forms at various points in the -/// process. So we can't just store a pointer to e.g., the AST or the +/// process. So we can't just store a pointer to e.g., the HIR or the /// parsed ty form, we have to be more flexible. To this end, the /// `ItemCtxt` is parameterized by a `DefId` that it uses to satisfy -/// `get_type_parameter_bounds` requests, drawing the information from -/// the AST (`hir::Generics`), recursively. +/// `probe_ty_param_bounds` requests, drawing the information from +/// the HIR (`hir::Generics`), recursively. pub struct ItemCtxt<'tcx> { tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, @@ -274,7 +276,7 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { } fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { - convert_item(self.tcx, item.item_id()); + lower_item(self.tcx, item.item_id()); reject_placeholder_type_signatures_in_item(self.tcx, item); intravisit::walk_item(self, item); } @@ -312,12 +314,12 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { } fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { - convert_trait_item(self.tcx, trait_item.trait_item_id()); + lower_trait_item(self.tcx, trait_item.trait_item_id()); intravisit::walk_trait_item(self, trait_item); } fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { - convert_impl_item(self.tcx, impl_item.impl_item_id()); + lower_impl_item(self.tcx, impl_item.impl_item_id()); intravisit::walk_impl_item(self, impl_item); } } @@ -341,8 +343,8 @@ impl<'tcx> ItemCtxt<'tcx> { ItemCtxt { tcx, item_def_id, tainted_by_errors: Cell::new(None) } } - pub fn to_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { - self.astconv().ast_ty_to_ty(ast_ty) + pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + self.lowerer().lower_ty(hir_ty) } pub fn hir_id(&self) -> hir::HirId { @@ -361,7 +363,7 @@ impl<'tcx> ItemCtxt<'tcx> { } } -impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { +impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -370,23 +372,14 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { self.item_def_id.to_def_id() } - fn get_type_parameter_bounds( - &self, - span: Span, - def_id: LocalDefId, - assoc_name: Ident, - ) -> ty::GenericPredicates<'tcx> { - self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name)) + fn allow_infer(&self) -> bool { + false } fn re_infer(&self, _: Option<&ty::GenericParamDef>, _: Span) -> Option<ty::Region<'tcx>> { None } - fn allow_ty_infer(&self) -> bool { - false - } - fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { Ty::new_error_with_message(self.tcx(), span, "bad placeholder type") } @@ -401,7 +394,16 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { ty::Const::new_error_with_message(self.tcx(), ty, span, "bad placeholder constant") } - fn projected_ty_from_poly_trait_ref( + fn probe_ty_param_bounds( + &self, + span: Span, + def_id: LocalDefId, + assoc_name: Ident, + ) -> ty::GenericPredicates<'tcx> { + self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name)) + } + + fn lower_assoc_ty( &self, span: Span, item_def_id: DefId, @@ -409,7 +411,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { - let item_args = self.astconv().create_args_for_associated_item( + let item_args = self.lowerer().lower_generic_args_of_assoc_item( span, item_def_id, item_segment, @@ -494,10 +496,6 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { ty.ty_adt_def() } - fn set_tainted_by_errors(&self, err: ErrorGuaranteed) { - self.tainted_by_errors.set(Some(err)); - } - fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) { // There's no place to record types from signatures? } @@ -505,6 +503,10 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { fn infcx(&self) -> Option<&InferCtxt<'tcx>> { None } + + fn set_tainted_by_errors(&self, err: ErrorGuaranteed) { + self.tainted_by_errors.set(Some(err)); + } } /// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present. @@ -544,9 +546,10 @@ fn get_new_lifetime_name<'tcx>( (1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap() } -fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { +#[instrument(level = "debug", skip_all)] +fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { let it = tcx.hir().item(item_id); - debug!("convert: item {} with id {}", it.ident, it.hir_id()); + debug!(item = %it.ident, id = %it.hir_id()); let def_id = item_id.owner_id.def_id; match &it.kind { @@ -588,7 +591,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); - convert_enum_variant_types(tcx, def_id.to_def_id()); + lower_enum_variant_types(tcx, def_id.to_def_id()); } hir::ItemKind::Impl { .. } => { tcx.ensure().generics_of(def_id); @@ -622,7 +625,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } if let Some(ctor_def_id) = struct_def.ctor_def_id() { - convert_variant_ctor(tcx, ctor_def_id); + lower_variant_ctor(tcx, ctor_def_id); } } @@ -633,7 +636,9 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { tcx.ensure().generics_of(def_id); tcx.ensure().predicates_of(def_id); tcx.ensure().explicit_item_bounds(def_id); + tcx.ensure().explicit_item_super_predicates(def_id); tcx.ensure().item_bounds(def_id); + tcx.ensure().item_super_predicates(def_id); } hir::ItemKind::TyAlias(..) => { @@ -663,7 +668,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { } } -fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { +fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { let trait_item = tcx.hir().trait_item(trait_item_id); let def_id = trait_item_id.owner_id; tcx.ensure().generics_of(def_id); @@ -689,6 +694,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Type(_, Some(_)) => { tcx.ensure().item_bounds(def_id); + tcx.ensure().item_super_predicates(def_id); tcx.ensure().type_of(def_id); // Account for `type T = _;`. let mut visitor = HirPlaceholderCollector::default(); @@ -698,6 +704,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Type(_, None) => { tcx.ensure().item_bounds(def_id); + tcx.ensure().item_super_predicates(def_id); // #74612: Visit and try to find bad placeholders // even if there is no concrete type. let mut visitor = HirPlaceholderCollector::default(); @@ -710,7 +717,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { tcx.ensure().predicates_of(def_id); } -fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { +fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { let def_id = impl_item_id.owner_id; tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); @@ -739,13 +746,13 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { } } -fn convert_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) { +fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); } -fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { +fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { let def = tcx.adt_def(def_id); let repr_type = def.repr().discr_type(); let initial = repr_type.initial_discriminant(tcx); @@ -778,10 +785,9 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { tcx.ensure().predicates_of(f.did); } - // Convert the ctor, if any. This also registers the variant as - // an item. + // Lower the ctor, if any. This also registers the variant as an item. if let Some(ctor_def_id) = variant.ctor_def_id() { - convert_variant_ctor(tcx, ctor_def_id.expect_local()); + lower_variant_ctor(tcx, ctor_def_id.expect_local()); } } } @@ -968,7 +974,7 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { } } -fn convert_variant( +fn lower_variant( tcx: TyCtxt<'_>, variant_did: Option<LocalDefId>, ident: Ident, @@ -1053,7 +1059,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { }; distance_from_explicit += 1; - convert_variant( + lower_variant( tcx, Some(v.def_id), v.ident, @@ -1073,7 +1079,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> { ItemKind::Struct(..) => AdtKind::Struct, _ => AdtKind::Union, }; - let variants = std::iter::once(convert_variant( + let variants = std::iter::once(lower_variant( tcx, None, item.ident, @@ -1277,7 +1283,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<ty::PolyFnSig< if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = tcx.parent_hir_node(hir_id) && i.of_trait.is_some() { - icx.astconv().ty_of_fn( + icx.lowerer().lower_fn_ty( hir_id, sig.header.unsafety, sig.header.abi, @@ -1294,9 +1300,14 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<ty::PolyFnSig< kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _), generics, .. - }) => { - icx.astconv().ty_of_fn(hir_id, header.unsafety, header.abi, decl, Some(generics), None) - } + }) => icx.lowerer().lower_fn_ty( + hir_id, + header.unsafety, + header.abi, + decl, + Some(generics), + None, + ), ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { let abi = tcx.hir().get_foreign_abi(hir_id); @@ -1364,7 +1375,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( // recursive function definition to leak out into the fn sig. let mut should_recover = false; - if let Some(ret_ty) = ret_ty.make_suggestable(tcx, false) { + if let Some(ret_ty) = ret_ty.make_suggestable(tcx, false, None) { diag.span_suggestion( ty.span, "replace with the correct return type", @@ -1404,7 +1415,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( )) } } - None => icx.astconv().ty_of_fn( + None => icx.lowerer().lower_fn_ty( hir_id, sig.header.unsafety, sig.header.abi, @@ -1442,7 +1453,7 @@ fn suggest_impl_trait<'tcx>( let ty::Tuple(types) = *args_tuple.kind() else { return None; }; - let types = types.make_suggestable(tcx, false)?; + let types = types.make_suggestable(tcx, false, None)?; let maybe_ret = if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") }; Some(format!( @@ -1500,7 +1511,7 @@ fn suggest_impl_trait<'tcx>( // FIXME(compiler-errors): We may benefit from resolving regions here. if ocx.select_where_possible().is_empty() && let item_ty = infcx.resolve_vars_if_possible(item_ty) - && let Some(item_ty) = item_ty.make_suggestable(tcx, false) + && let Some(item_ty) = item_ty.make_suggestable(tcx, false, None) && let Some(sugg) = formatter( tcx, infcx.resolve_vars_if_possible(args), @@ -1522,19 +1533,19 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai impl_ .of_trait .as_ref() - .map(|ast_trait_ref| { - let selfty = tcx.type_of(def_id).instantiate_identity(); + .map(|hir_trait_ref| { + let self_ty = tcx.type_of(def_id).instantiate_identity(); let trait_ref = if let Some(ErrorGuaranteed { .. }) = check_impl_constness( tcx, tcx.is_const_trait_impl_raw(def_id.to_def_id()), - ast_trait_ref, + hir_trait_ref, ) { // we have a const impl, but for a trait without `#[const_trait]`, so // without the host param. If we continue with the HIR trait ref, we get // ICEs for generic arg count mismatch. We do a little HIR editing to - // make astconv happy. - let mut path_segments = ast_trait_ref.path.segments.to_vec(); + // make HIR ty lowering happy. + let mut path_segments = hir_trait_ref.path.segments.to_vec(); let last_segment = path_segments.len() - 1; let mut args = *path_segments[last_segment].args(); let last_arg = args.args.len() - 1; @@ -1542,19 +1553,19 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai args.args = &args.args[..args.args.len() - 1]; path_segments[last_segment].args = Some(tcx.hir_arena.alloc(args)); let path = hir::Path { - span: ast_trait_ref.path.span, - res: ast_trait_ref.path.res, + span: hir_trait_ref.path.span, + res: hir_trait_ref.path.res, segments: tcx.hir_arena.alloc_slice(&path_segments), }; - let trait_ref = tcx.hir_arena.alloc(hir::TraitRef { path: tcx.hir_arena.alloc(path), hir_ref_id: ast_trait_ref.hir_ref_id }); - icx.astconv().instantiate_mono_trait_ref(trait_ref, selfty) + let trait_ref = tcx.hir_arena.alloc(hir::TraitRef { path: tcx.hir_arena.alloc(path), hir_ref_id: hir_trait_ref.hir_ref_id }); + icx.lowerer().lower_impl_trait_ref(trait_ref, self_ty) } else { - icx.astconv().instantiate_mono_trait_ref(ast_trait_ref, selfty) + icx.lowerer().lower_impl_trait_ref(hir_trait_ref, self_ty) }; ty::ImplTraitHeader { trait_ref: ty::EarlyBinder::bind(trait_ref), unsafety: impl_.unsafety, - polarity: polarity_of_impl(tcx, def_id, impl_, item.span) + polarity: polarity_of_impl(tcx, def_id, impl_, item.span) } }) } @@ -1562,20 +1573,20 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai fn check_impl_constness( tcx: TyCtxt<'_>, is_const: bool, - ast_trait_ref: &hir::TraitRef<'_>, + hir_trait_ref: &hir::TraitRef<'_>, ) -> Option<ErrorGuaranteed> { if !is_const { return None; } - let trait_def_id = ast_trait_ref.trait_def_id()?; + let trait_def_id = hir_trait_ref.trait_def_id()?; if tcx.has_attr(trait_def_id, sym::const_trait) { return None; } let trait_name = tcx.item_name(trait_def_id).to_string(); Some(tcx.dcx().emit_err(errors::ConstImplForNonConstTrait { - trait_ref_span: ast_trait_ref.path.span, + trait_ref_span: hir_trait_ref.path.span, trait_name, local_trait_span: trait_def_id.as_local().map(|_| tcx.def_span(trait_def_id).shrink_to_lo()), @@ -1671,19 +1682,19 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( }; let hir_id = tcx.local_def_id_to_hir_id(def_id); let fty = - ItemCtxt::new(tcx, def_id).astconv().ty_of_fn(hir_id, unsafety, abi, decl, None, None); + ItemCtxt::new(tcx, def_id).lowerer().lower_fn_ty(hir_id, unsafety, abi, decl, None, None); // Feature gate SIMD types in FFI, since I am not sure that the // ABIs are handled at all correctly. -huonw if abi != abi::Abi::RustIntrinsic && !tcx.features().simd_ffi { - let check = |ast_ty: &hir::Ty<'_>, ty: Ty<'_>| { + let check = |hir_ty: &hir::Ty<'_>, ty: Ty<'_>| { if ty.is_simd() { let snip = tcx .sess .source_map() - .span_to_snippet(ast_ty.span) + .span_to_snippet(hir_ty.span) .map_or_else(|_| String::new(), |s| format!(" `{s}`")); - tcx.dcx().emit_err(errors::SIMDFFIHighlyExperimental { span: ast_ty.span, snip }); + tcx.dcx().emit_err(errors::SIMDFFIHighlyExperimental { span: hir_ty.span, snip }); } }; for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) { diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index bc6abc53cad..4d6a02f50bf 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -218,8 +218,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { Deny, } - let no_generics = hir::Generics::empty(); - let ast_generics = node.generics().unwrap_or(no_generics); + let hir_generics = node.generics().unwrap_or(hir::Generics::empty()); let (opt_self, allow_defaults) = match node { Node::Item(item) => { match item.kind { @@ -275,13 +274,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { generics.parent_count + generics.params.len() }); - let mut params: Vec<_> = Vec::with_capacity(ast_generics.params.len() + has_self as usize); + let mut params: Vec<_> = Vec::with_capacity(hir_generics.params.len() + has_self as usize); if let Some(opt_self) = opt_self { params.push(opt_self); } - let early_lifetimes = super::early_bound_lifetimes_from_generics(tcx, ast_generics); + let early_lifetimes = super::early_bound_lifetimes_from_generics(tcx, hir_generics); params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef { name: param.name.ident().name, index: own_start + i as u32, @@ -302,7 +301,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \ `struct`, `enum`, `type`, or `trait` definitions"; - params.extend(ast_generics.params.iter().filter_map(|param| match param.kind { + params.extend(hir_generics.params.iter().filter_map(|param| match param.kind { GenericParamKind::Lifetime { .. } => None, GenericParamKind::Type { default, synthetic, .. } => { if default.is_some() { diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 78c390d0924..f1b14adcb7a 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -1,5 +1,6 @@ use super::ItemCtxt; -use crate::astconv::{AstConv, PredicateFilter}; +use crate::hir_ty_lowering::{HirTyLowerer, PredicateFilter}; +use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_infer::traits::util; use rustc_middle::ty::GenericArgs; @@ -17,8 +18,9 @@ use rustc_span::Span; fn associated_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, assoc_item_def_id: LocalDefId, - ast_bounds: &'tcx [hir::GenericBound<'tcx>], + hir_bounds: &'tcx [hir::GenericBound<'tcx>], span: Span, + filter: PredicateFilter, ) -> &'tcx [(ty::Clause<'tcx>, Span)] { let item_ty = Ty::new_projection( tcx, @@ -27,9 +29,9 @@ fn associated_type_bounds<'tcx>( ); let icx = ItemCtxt::new(tcx, assoc_item_def_id); - let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds, PredicateFilter::All); + let mut bounds = icx.lowerer().lower_mono_bounds(item_ty, hir_bounds, filter); // Associated types are implicitly sized unless a `?Sized` bound is found - icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span); + icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); let trait_def_id = tcx.local_parent(assoc_item_def_id); let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); @@ -60,15 +62,16 @@ fn associated_type_bounds<'tcx>( fn opaque_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId, - ast_bounds: &'tcx [hir::GenericBound<'tcx>], + hir_bounds: &'tcx [hir::GenericBound<'tcx>], item_ty: Ty<'tcx>, span: Span, + filter: PredicateFilter, ) -> &'tcx [(ty::Clause<'tcx>, Span)] { ty::print::with_reduced_queries!({ let icx = ItemCtxt::new(tcx, opaque_def_id); - let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds, PredicateFilter::All); + let mut bounds = icx.lowerer().lower_mono_bounds(item_ty, hir_bounds, filter); // Opaque types are implicitly sized unless a `?Sized` bound is found - icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span); + icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span); debug!(?bounds); tcx.arena.alloc_from_iter(bounds.clauses()) @@ -79,6 +82,21 @@ pub(super) fn explicit_item_bounds( tcx: TyCtxt<'_>, def_id: LocalDefId, ) -> ty::EarlyBinder<&'_ [(ty::Clause<'_>, Span)]> { + explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::All) +} + +pub(super) fn explicit_item_super_predicates( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> ty::EarlyBinder<&'_ [(ty::Clause<'_>, Span)]> { + explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::SelfOnly) +} + +pub(super) fn explicit_item_bounds_with_filter( + tcx: TyCtxt<'_>, + def_id: LocalDefId, + filter: PredicateFilter, +) -> ty::EarlyBinder<&'_ [(ty::Clause<'_>, Span)]> { match tcx.opt_rpitit_info(def_id.to_def_id()) { // RPITIT's bounds are the same as opaque type bounds, but with // a projection self type. @@ -95,6 +113,7 @@ pub(super) fn explicit_item_bounds( ty::GenericArgs::identity_for_item(tcx, def_id), ), item.span, + filter, )); } Some(ty::ImplTraitInTraitData::Impl { .. }) => span_bug!( @@ -109,7 +128,7 @@ pub(super) fn explicit_item_bounds( kind: hir::TraitItemKind::Type(bounds, _), span, .. - }) => associated_type_bounds(tcx, def_id, bounds, *span), + }) => associated_type_bounds(tcx, def_id, bounds, *span, filter), hir::Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: false, .. }), span, @@ -117,10 +136,10 @@ pub(super) fn explicit_item_bounds( }) => { let args = GenericArgs::identity_for_item(tcx, def_id); let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); - opaque_type_bounds(tcx, def_id, bounds, item_ty, *span) + opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter) } - // Since RPITITs are astconv'd as projections in `ast_ty_to_ty`, when we're asking - // for the item bounds of the *opaques* in a trait's default method signature, we + // Since RPITITs are lowered as projections in `<dyn HirTyLowerer>::lower_ty`, when we're + // asking for the item bounds of the *opaques* in a trait's default method signature, we // need to map these projections back to opaques. hir::Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: true, origin, .. }), @@ -135,7 +154,7 @@ pub(super) fn explicit_item_bounds( let args = GenericArgs::identity_for_item(tcx, def_id); let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); tcx.arena.alloc_slice( - &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span) + &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span, filter) .to_vec() .fold_with(&mut AssocTyToOpaque { tcx, fn_def_id: fn_def_id.to_def_id() }), ) @@ -155,6 +174,31 @@ pub(super) fn item_bounds( }) } +pub(super) fn item_super_predicates( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> ty::EarlyBinder<&'_ ty::List<ty::Clause<'_>>> { + tcx.explicit_item_super_predicates(def_id).map_bound(|bounds| { + tcx.mk_clauses_from_iter( + util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound)).filter_only_self(), + ) + }) +} + +pub(super) fn item_non_self_assumptions( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> ty::EarlyBinder<&'_ ty::List<ty::Clause<'_>>> { + let all_bounds: FxIndexSet<_> = tcx.item_bounds(def_id).skip_binder().iter().collect(); + let own_bounds: FxIndexSet<_> = + tcx.item_super_predicates(def_id).skip_binder().iter().collect(); + if all_bounds.len() == own_bounds.len() { + ty::EarlyBinder::bind(ty::List::empty()) + } else { + ty::EarlyBinder::bind(tcx.mk_clauses_from_iter(all_bounds.difference(&own_bounds).copied())) + } +} + struct AssocTyToOpaque<'tcx> { tcx: TyCtxt<'tcx>, fn_def_id: DefId, diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 66c9fb93500..efd3ceebe6c 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -1,7 +1,7 @@ -use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter}; use crate::bounds::Bounds; use crate::collect::ItemCtxt; use crate::constrained_generic_params as cgp; +use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; use hir::{HirId, Node}; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; @@ -123,7 +123,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // Preserving the order of insertion is important here so as not to break UI tests. let mut predicates: FxIndexSet<(ty::Clause<'_>, Span)> = FxIndexSet::default(); - let ast_generics = node.generics().unwrap_or(NO_GENERICS); + let hir_generics = node.generics().unwrap_or(NO_GENERICS); if let Node::Item(item) = node { match item.kind { ItemKind::Impl(impl_) => { @@ -149,8 +149,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // like `trait Foo: A + B + C`. if let Some(self_bounds) = is_trait { predicates.extend( - icx.astconv() - .compute_bounds(tcx.types.self_param, self_bounds, PredicateFilter::All) + icx.lowerer() + .lower_mono_bounds(tcx.types.self_param, self_bounds, PredicateFilter::All) .clauses(), ); } @@ -170,19 +170,19 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen // Collect the predicates that were written inline by the user on each // type parameter (e.g., `<T: Foo>`). Also add `ConstArgHasType` predicates // for each const parameter. - for param in ast_generics.params { + for param in hir_generics.params { match param.kind { // We already dealt with early bound lifetimes above. GenericParamKind::Lifetime { .. } => (), GenericParamKind::Type { .. } => { - let param_ty = icx.astconv().hir_id_to_bound_ty(param.hir_id); + let param_ty = icx.lowerer().lower_ty_param(param.hir_id); let mut bounds = Bounds::default(); // Params are implicitly sized unless a `?Sized` bound is found - icx.astconv().add_implicitly_sized( + icx.lowerer().add_sized_bound( &mut bounds, param_ty, &[], - Some((param.def_id, ast_generics.predicates)), + Some((param.def_id, hir_generics.predicates)), param.span, ); trace!(?bounds); @@ -194,7 +194,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen .type_of(param.def_id.to_def_id()) .no_bound_vars() .expect("const parameters cannot be generic"); - let ct = icx.astconv().hir_id_to_bound_const(param.hir_id, ct_ty); + let ct = icx.lowerer().lower_const_param(param.hir_id, ct_ty); predicates.insert(( ty::ClauseKind::ConstArgHasType(ct, ct_ty).to_predicate(tcx), param.span, @@ -205,10 +205,10 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen trace!(?predicates); // Add in the bounds that appear in the where-clause. - for predicate in ast_generics.predicates { + for predicate in hir_generics.predicates { match predicate { hir::WherePredicate::BoundPredicate(bound_pred) => { - let ty = icx.to_ty(bound_pred.bounded_ty); + let ty = icx.lower_ty(bound_pred.bounded_ty); let bound_vars = tcx.late_bound_vars(bound_pred.hir_id); // Keep the type around in a dummy predicate, in case of no bounds. // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` @@ -232,7 +232,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } let mut bounds = Bounds::default(); - icx.astconv().add_bounds( + icx.lowerer().lower_poly_bounds( ty, bound_pred.bounds.iter(), &mut bounds, @@ -243,11 +243,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } hir::WherePredicate::RegionPredicate(region_pred) => { - let r1 = icx.astconv().ast_region_to_region(region_pred.lifetime, None); + let r1 = icx.lowerer().lower_lifetime(region_pred.lifetime, None); predicates.extend(region_pred.bounds.iter().map(|bound| { let (r2, span) = match bound { hir::GenericBound::Outlives(lt) => { - (icx.astconv().ast_region_to_region(lt, None), lt.ident.span) + (icx.lowerer().lower_lifetime(lt, None), lt.ident.span) } bound => { span_bug!( @@ -542,8 +542,8 @@ pub(super) fn explicit_predicates_of<'tcx>( } /// Ensures that the super-predicates of the trait with a `DefId` -/// of `trait_def_id` are converted and stored. This also ensures that -/// the transitive super-predicates are converted. +/// of `trait_def_id` are lowered and stored. This also ensures that +/// the transitive super-predicates are lowered. pub(super) fn super_predicates_of( tcx: TyCtxt<'_>, trait_def_id: LocalDefId, @@ -574,8 +574,8 @@ pub(super) fn implied_predicates_of( } /// Ensures that the super-predicates of the trait with a `DefId` -/// of `trait_def_id` are converted and stored. This also ensures that -/// the transitive super-predicates are converted. +/// of `trait_def_id` are lowered and stored. This also ensures that +/// the transitive super-predicates are lowered. pub(super) fn implied_predicates_with_filter( tcx: TyCtxt<'_>, trait_def_id: DefId, @@ -601,9 +601,9 @@ pub(super) fn implied_predicates_with_filter( let icx = ItemCtxt::new(tcx, trait_def_id); let self_param_ty = tcx.types.self_param; - let superbounds = icx.astconv().compute_bounds(self_param_ty, bounds, filter); + let superbounds = icx.lowerer().lower_mono_bounds(self_param_ty, bounds, filter); - let where_bounds_that_match = icx.type_parameter_bounds_in_generics( + let where_bounds_that_match = icx.probe_ty_param_bounds_in_generics( generics, item.owner_id.def_id, self_param_ty, @@ -615,7 +615,7 @@ pub(super) fn implied_predicates_with_filter( &*tcx.arena.alloc_from_iter(superbounds.clauses().chain(where_bounds_that_match)); debug!(?implied_bounds); - // Now require that immediate supertraits are converted, which will, in + // Now require that immediate supertraits are lowered, which will, in // turn, reach indirect supertraits, so we detect cycles now instead of // overflowing during elaboration. Same for implied predicates, which // make sure we walk into associated type bounds. @@ -624,7 +624,7 @@ pub(super) fn implied_predicates_with_filter( for &(pred, span) in implied_bounds { debug!("superbound: {:?}", pred); if let ty::ClauseKind::Trait(bound) = pred.kind().skip_binder() - && bound.polarity == ty::ImplPolarity::Positive + && bound.polarity == ty::PredicatePolarity::Positive { tcx.at(span).super_predicates_of(bound.def_id()); } @@ -634,7 +634,7 @@ pub(super) fn implied_predicates_with_filter( for &(pred, span) in implied_bounds { debug!("superbound: {:?}", pred); if let ty::ClauseKind::Trait(bound) = pred.kind().skip_binder() - && bound.polarity == ty::ImplPolarity::Positive + && bound.polarity == ty::PredicatePolarity::Positive { tcx.at(span).implied_predicates_of(bound.def_id()); } @@ -656,7 +656,7 @@ pub(super) fn type_param_predicates( use rustc_hir::*; use rustc_middle::ty::Ty; - // In the AST, bounds can derive from two places. Either + // In the HIR, bounds can derive from two places. Either // written inline like `<T: Foo>` or in a where-clause like // `where T: Foo`. @@ -676,7 +676,7 @@ pub(super) fn type_param_predicates( let mut result = parent .map(|parent| { let icx = ItemCtxt::new(tcx, parent); - icx.get_type_parameter_bounds(DUMMY_SP, def_id, assoc_name) + icx.probe_ty_param_bounds(DUMMY_SP, def_id, assoc_name) }) .unwrap_or_default(); let mut extend = None; @@ -684,7 +684,7 @@ pub(super) fn type_param_predicates( let item_hir_id = tcx.local_def_id_to_hir_id(item_def_id); let hir_node = tcx.hir_node(item_hir_id); - let Some(ast_generics) = hir_node.generics() else { return result }; + let Some(hir_generics) = hir_node.generics() else { return result }; if let Node::Item(item) = hir_node && let ItemKind::Trait(..) = item.kind // Implied `Self: Trait` and supertrait bounds. @@ -696,8 +696,8 @@ pub(super) fn type_param_predicates( let icx = ItemCtxt::new(tcx, item_def_id); let extra_predicates = extend.into_iter().chain( - icx.type_parameter_bounds_in_generics( - ast_generics, + icx.probe_ty_param_bounds_in_generics( + hir_generics, def_id, ty, PredicateFilter::SelfThatDefines(assoc_name), @@ -714,21 +714,22 @@ pub(super) fn type_param_predicates( } impl<'tcx> ItemCtxt<'tcx> { - /// Finds bounds from `hir::Generics`. This requires scanning through the - /// AST. We do this to avoid having to convert *all* the bounds, which - /// would create artificial cycles. Instead, we can only convert the - /// bounds for a type parameter `X` if `X::Foo` is used. - #[instrument(level = "trace", skip(self, ast_generics))] - fn type_parameter_bounds_in_generics( + /// Finds bounds from `hir::Generics`. + /// + /// This requires scanning through the HIR. + /// We do this to avoid having to lower *all* the bounds, which would create artificial cycles. + /// Instead, we can only lower the bounds for a type parameter `X` if `X::Foo` is used. + #[instrument(level = "trace", skip(self, hir_generics))] + fn probe_ty_param_bounds_in_generics( &self, - ast_generics: &'tcx hir::Generics<'tcx>, + hir_generics: &'tcx hir::Generics<'tcx>, param_def_id: LocalDefId, ty: Ty<'tcx>, filter: PredicateFilter, ) -> Vec<(ty::Clause<'tcx>, Span)> { let mut bounds = Bounds::default(); - for predicate in ast_generics.predicates { + for predicate in hir_generics.predicates { let hir::WherePredicate::BoundPredicate(predicate) = predicate else { continue; }; @@ -750,13 +751,13 @@ impl<'tcx> ItemCtxt<'tcx> { let bound_ty = if predicate.is_param_bound(param_def_id.to_def_id()) { ty } else if matches!(filter, PredicateFilter::All) { - self.to_ty(predicate.bounded_ty) + self.lower_ty(predicate.bounded_ty) } else { continue; }; let bound_vars = self.tcx.late_bound_vars(predicate.hir_id); - self.astconv().add_bounds( + self.lowerer().lower_poly_bounds( bound_ty, predicate.bounds.iter().filter(|bound| { assoc_name diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 86b075a84a7..27a26cfe474 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1917,18 +1917,18 @@ fn is_late_bound_map( /// /// If we conservatively considered `'a` unconstrained then we could break users who had written code before /// we started correctly handling aliases. If we considered `'a` constrained then it would become late bound - /// causing an error during astconv as the `'a` is not constrained by the input type `<() as Trait<'a>>::Assoc` + /// causing an error during HIR ty lowering as the `'a` is not constrained by the input type `<() as Trait<'a>>::Assoc` /// but appears in the output type `<() as Trait<'a>>::Assoc`. /// /// We must therefore "look into" the `Alias` to see whether we should consider `'a` constrained or not. /// /// See #100508 #85533 #47511 for additional context - struct ConstrainedCollectorPostAstConv { + struct ConstrainedCollectorPostHirTyLowering { arg_is_constrained: Box<[bool]>, } use ty::Ty; - impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ConstrainedCollectorPostAstConv { + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ConstrainedCollectorPostHirTyLowering { fn visit_ty(&mut self, t: Ty<'tcx>) { match t.kind() { ty::Param(param_ty) => { @@ -1970,10 +1970,10 @@ fn is_late_bound_map( None, hir::Path { res: Res::Def(DefKind::TyAlias, alias_def), segments, span }, )) => { - // See comments on `ConstrainedCollectorPostAstConv` for why this arm does not just consider - // args to be unconstrained. + // See comments on `ConstrainedCollectorPostHirTyLowering` for why this arm does not + // just consider args to be unconstrained. let generics = self.tcx.generics_of(alias_def); - let mut walker = ConstrainedCollectorPostAstConv { + let mut walker = ConstrainedCollectorPostHirTyLowering { arg_is_constrained: vec![false; generics.params.len()].into_boxed_slice(), }; walker.visit_ty(self.tcx.type_of(alias_def).instantiate_identity()); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index fd86f2dd1b1..722def2563c 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -47,7 +47,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { let ty = tcx.fold_regions(ty, |r, _| { if r.is_erased() { ty::Region::new_error_misc(tcx) } else { r } }); - let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false) { + let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false, None) { (ty, Some((span, Applicability::MachineApplicable))) } else { (ty, None) @@ -97,10 +97,10 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU Node::Ty(hir_ty @ hir::Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { // Find the Item containing the associated type so we can create an ItemCtxt. - // Using the ItemCtxt convert the HIR for the unresolved assoc type into a + // Using the ItemCtxt lower the HIR for the unresolved assoc type into a // ty which is a fully resolved projection. - // For the code example above, this would mean converting Self::Assoc<3> - // into a ty::Alias(ty::Projection, <Self as Foo>::Assoc<3>) + // For the code example above, this would mean lowering `Self::Assoc<3>` + // to a ty::Alias(ty::Projection, `<Self as Foo>::Assoc<3>`). let item_def_id = tcx .hir() .parent_owner_iter(hir_id) @@ -108,7 +108,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { .unwrap() .0 .def_id; - let ty = ItemCtxt::new(tcx, item_def_id).to_ty(hir_ty); + let ty = ItemCtxt::new(tcx, item_def_id).lower_ty(hir_ty); // Iterate through the generics of the projection to find the one that corresponds to // the def_id that this query was called with. We filter to only type and const args here @@ -369,8 +369,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty ) }) }) - .unwrap_or_else(|| icx.to_ty(ty)), - TraitItemKind::Type(_, Some(ty)) => icx.to_ty(ty), + .unwrap_or_else(|| icx.lower_ty(ty)), + TraitItemKind::Type(_, Some(ty)) => icx.lower_ty(ty), TraitItemKind::Type(_, None) => { span_bug!(item.span, "associated type missing default"); } @@ -392,7 +392,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty "associated constant", ) } else { - icx.to_ty(ty) + icx.lower_ty(ty) } } ImplItemKind::Type(ty) => { @@ -400,7 +400,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty check_feature_inherent_assoc_ty(tcx, item.span); } - icx.to_ty(ty) + icx.lower_ty(ty) } }, @@ -416,17 +416,17 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty "static variable", ) } else { - icx.to_ty(ty) + icx.lower_ty(ty) } } ItemKind::Const(ty, _, body_id) => { if ty.is_suggestable_infer_ty() { infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant") } else { - icx.to_ty(ty) + icx.lower_ty(ty) } } - ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty), + ItemKind::TyAlias(self_ty, _) => icx.lower_ty(self_ty), ItemKind::Impl(hir::Impl { self_ty, .. }) => match self_ty.find_self_aliases() { spans if spans.len() > 0 => { let guar = tcx @@ -434,7 +434,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty .emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: () }); Ty::new_error(tcx, guar) } - _ => icx.to_ty(*self_ty), + _ => icx.lower_ty(*self_ty), }, ItemKind::Fn(..) => { let args = ty::GenericArgs::identity_for_item(tcx, def_id); @@ -466,7 +466,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty let args = ty::GenericArgs::identity_for_item(tcx, def_id); Ty::new_fn_def(tcx, def_id.to_def_id(), args) } - ForeignItemKind::Static(t, _) => icx.to_ty(t), + ForeignItemKind::Static(t, _) => icx.lower_ty(t), ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()), }, @@ -480,7 +480,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty } }, - Node::Field(field) => icx.to_ty(field.ty), + Node::Field(field) => icx.lower_ty(field.ty), Node::Expr(&Expr { kind: ExprKind::Closure { .. }, .. }) => { tcx.typeck(def_id).node_type(hir_id) @@ -495,7 +495,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty Node::GenericParam(param) => match ¶m.kind { GenericParamKind::Type { default: Some(ty), .. } - | GenericParamKind::Const { ty, .. } => icx.to_ty(ty), + | GenericParamKind::Const { ty, .. } => icx.lower_ty(ty), x => bug!("unexpected non-type Node::GenericParam: {:?}", x), }, @@ -587,7 +587,7 @@ fn infer_placeholder_type<'a>( suggestions.clear(); } - if let Some(ty) = ty.make_suggestable(tcx, false) { + if let Some(ty) = ty.make_suggestable(tcx, false, None) { err.span_suggestion( span, format!("provide a type for the {kind}"), @@ -606,7 +606,7 @@ fn infer_placeholder_type<'a>( let mut diag = bad_placeholder(tcx, vec![span], kind); if !ty.references_error() { - if let Some(ty) = ty.make_suggestable(tcx, false) { + if let Some(ty) = ty.make_suggestable(tcx, false, None) { diag.span_suggestion( span, "replace with the correct type", diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 3067e2d0b71..11bd3e5282d 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -12,17 +12,19 @@ use rustc_trait_selection::traits; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use smallvec::SmallVec; -use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter}; use crate::bounds::Bounds; use crate::errors; +use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; -impl<'tcx> dyn AstConv<'tcx> + '_ { - /// Sets `implicitly_sized` to true on `Bounds` if necessary - pub(crate) fn add_implicitly_sized( +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Add a `Sized` bound to the `bounds` if appropriate. + /// + /// Doesn't add the bound if the HIR bounds contain any of `Sized`, `?Sized` or `!Sized`. + pub(crate) fn add_sized_bound( &self, bounds: &mut Bounds<'tcx>, self_ty: Ty<'tcx>, - ast_bounds: &'tcx [hir::GenericBound<'tcx>], + hir_bounds: &'tcx [hir::GenericBound<'tcx>], self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, span: Span, ) { @@ -33,9 +35,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // Try to find an unbound in bounds. let mut unbounds: SmallVec<[_; 1]> = SmallVec::new(); - let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| { - for ab in ast_bounds { - let hir::GenericBound::Trait(ptr, modifier) = ab else { + let mut search_bounds = |hir_bounds: &'tcx [hir::GenericBound<'tcx>]| { + for hir_bound in hir_bounds { + let hir::GenericBound::Trait(ptr, modifier) = hir_bound else { continue; }; match modifier { @@ -58,7 +60,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } }; - search_bounds(ast_bounds); + search_bounds(hir_bounds); if let Some((self_ty, where_clause)) = self_ty_where_predicates { for clause in where_clause { if let hir::WherePredicate::BoundPredicate(pred) = clause @@ -101,51 +103,57 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - /// This helper takes a *converted* parameter type (`param_ty`) - /// and an *unconverted* list of bounds: + /// Lower HIR bounds into `bounds` given the self type `param_ty` and the overarching late-bound vars if any. + /// + /// ### Examples /// - /// ```text - /// fn foo<T: Debug> - /// ^ ^^^^^ `ast_bounds` parameter, in HIR form - /// | - /// `param_ty`, in ty form + /// ```ignore (illustrative) + /// fn foo<T>() where for<'a> T: Trait<'a> + Copy {} + /// // ^^^^^^^ ^ ^^^^^^^^^^^^^^^^ `hir_bounds`, in HIR form + /// // | | + /// // | `param_ty`, in ty form + /// // `bound_vars`, in ty form + /// + /// fn bar<T>() where T: for<'a> Trait<'a> + Copy {} // no overarching `bound_vars` here! + /// // ^ ^^^^^^^^^^^^^^^^^^^^^^^^ `hir_bounds`, in HIR form + /// // | + /// // `param_ty`, in ty form /// ``` /// - /// It adds these `ast_bounds` into the `bounds` structure. + /// ### A Note on Binders /// - /// **A note on binders:** there is an implied binder around - /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref` - /// for more details. - #[instrument(level = "debug", skip(self, ast_bounds, bounds))] - pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'tcx>>>( + /// There is an implied binder around `param_ty` and `hir_bounds`. + /// See `lower_poly_trait_ref` for more details. + #[instrument(level = "debug", skip(self, hir_bounds, bounds))] + pub(crate) fn lower_poly_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'tcx>>>( &self, param_ty: Ty<'tcx>, - ast_bounds: I, + hir_bounds: I, bounds: &mut Bounds<'tcx>, bound_vars: &'tcx ty::List<ty::BoundVariableKind>, only_self_bounds: OnlySelfBounds, ) where 'tcx: 'hir, { - for ast_bound in ast_bounds { - match ast_bound { + for hir_bound in hir_bounds { + match hir_bound { hir::GenericBound::Trait(poly_trait_ref, modifier) => { let (constness, polarity) = match modifier { hir::TraitBoundModifier::Const => { - (ty::BoundConstness::Const, ty::ImplPolarity::Positive) + (ty::BoundConstness::Const, ty::PredicatePolarity::Positive) } hir::TraitBoundModifier::MaybeConst => { - (ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive) + (ty::BoundConstness::ConstIfConst, ty::PredicatePolarity::Positive) } hir::TraitBoundModifier::None => { - (ty::BoundConstness::NotConst, ty::ImplPolarity::Positive) + (ty::BoundConstness::NotConst, ty::PredicatePolarity::Positive) } hir::TraitBoundModifier::Negative => { - (ty::BoundConstness::NotConst, ty::ImplPolarity::Negative) + (ty::BoundConstness::NotConst, ty::PredicatePolarity::Negative) } hir::TraitBoundModifier::Maybe => continue, }; - let _ = self.instantiate_poly_trait_ref( + let _ = self.lower_poly_trait_ref( &poly_trait_ref.trait_ref, poly_trait_ref.span, constness, @@ -156,7 +164,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { ); } hir::GenericBound::Outlives(lifetime) => { - let region = self.ast_region_to_region(lifetime, None); + let region = self.lower_lifetime(lifetime, None); bounds.push_region_bound( self.tcx(), ty::Binder::bind_with_vars( @@ -170,26 +178,19 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - /// Translates a list of bounds from the HIR into the `Bounds` data structure. - /// The self-type for the bounds is given by `param_ty`. + /// Lower HIR bounds into `bounds` given the self type `param_ty` and *no* overarching late-bound vars. /// - /// Example: + /// ### Example /// /// ```ignore (illustrative) /// fn foo<T: Bar + Baz>() { } - /// // ^ ^^^^^^^^^ ast_bounds + /// // ^ ^^^^^^^^^ hir_bounds /// // param_ty /// ``` - /// - /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be - /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the - /// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`. - /// - /// `span` should be the declaration size of the parameter. - pub(crate) fn compute_bounds( + pub(crate) fn lower_mono_bounds( &self, param_ty: Ty<'tcx>, - ast_bounds: &[hir::GenericBound<'tcx>], + hir_bounds: &[hir::GenericBound<'tcx>], filter: PredicateFilter, ) -> Bounds<'tcx> { let mut bounds = Bounds::default(); @@ -201,9 +202,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => OnlySelfBounds(true), }; - self.add_bounds( + self.lower_poly_bounds( param_ty, - ast_bounds.iter().filter(|bound| match filter { + hir_bounds.iter().filter(|bound| match filter { PredicateFilter::All | PredicateFilter::SelfOnly | PredicateFilter::SelfAndAssociatedTypeBounds => true, @@ -227,14 +228,16 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { bounds } - /// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates - /// onto `bounds`. + /// Lower an associated item binding from HIR into `bounds`. + /// + /// ### A Note on Binders /// - /// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the - /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside* - /// the binder (e.g., `&'a u32`) and hence may reference bound regions. + /// Given something like `T: for<'a> Iterator<Item = &'a u32>`, + /// the `trait_ref` here will be `for<'a> T: Iterator`. + /// The `binding` data however is from *inside* the binder + /// (e.g., `&'a u32`) and hence may reference bound regions. #[instrument(level = "debug", skip(self, bounds, dup_bindings, path_span))] - pub(super) fn add_predicates_for_ast_type_binding( + pub(super) fn lower_assoc_item_binding( &self, hir_ref_id: hir::HirId, trait_ref: ty::PolyTraitRef<'tcx>, @@ -244,22 +247,6 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { path_span: Span, only_self_bounds: OnlySelfBounds, ) -> Result<(), ErrorGuaranteed> { - // Given something like `U: SomeTrait<T = X>`, we want to produce a - // predicate like `<U as SomeTrait>::T = X`. This is somewhat - // subtle in the event that `T` is defined in a supertrait of - // `SomeTrait`, because in that case we need to upcast. - // - // That is, consider this case: - // - // ``` - // trait SubTrait: SuperTrait<i32> { } - // trait SuperTrait<A> { type T; } - // - // ... B: SubTrait<T = foo> ... - // ``` - // - // We want to produce `<B as SuperTrait<i32>>::T == foo`. - let tcx = self.tcx(); let assoc_kind = if binding.gen_args.parenthesized @@ -272,7 +259,15 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { ty::AssocKind::Type }; - let candidate = if self.trait_defines_associated_item_named( + // Given something like `U: Trait<T = X>`, we want to produce a predicate like + // `<U as Trait>::T = X`. + // This is somewhat subtle in the event that `T` is defined in a supertrait of `Trait`, + // because in that case we need to upcast. I.e., we want to produce + // `<B as SuperTrait<i32>>::T == X` for `B: SubTrait<T = X>` where + // + // trait SubTrait: SuperTrait<i32> {} + // trait SuperTrait<A> { type T; } + let candidate = if self.probe_trait_that_defines_assoc_item( trait_ref.def_id(), assoc_kind, binding.ident, @@ -282,7 +277,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } else { // Otherwise, we have to walk through the supertraits to find // one that does define it. - self.one_bound_for_assoc_item( + self.probe_single_bound_for_assoc_item( || traits::supertraits(tcx, trait_ref), trait_ref.skip_binder().print_only_trait_name(), None, @@ -417,7 +412,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { infer_args: false, }; - let alias_args = self.create_args_for_associated_item( + let alias_args = self.lower_generic_args_of_assoc_item( path_span, assoc_item.def_id, &item_segment, @@ -449,9 +444,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { span: binding.span, })); } + // Lower an equality constraint like `Item = u32` as found in HIR bound `T: Iterator<Item = u32>` + // to a projection predicate: `<T as Iterator>::Item = u32`. hir::TypeBindingKind::Equality { term } => { let term = match term { - hir::Term::Ty(ty) => self.ast_ty_to_ty(ty).into(), + hir::Term::Ty(ty) => self.lower_ty(ty).into(), hir::Term::Const(ct) => ty::Const::from_anon_const(tcx, ct.def_id).into(), }; @@ -490,10 +487,6 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { }, ); - // "Desugar" a constraint like `T: Iterator<Item = u32>` this to - // the "projection predicate" for: - // - // `<T as Iterator>::Item = u32` bounds.push_projection_bound( tcx, projection_ty @@ -501,22 +494,18 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { binding.span, ); } - hir::TypeBindingKind::Constraint { bounds: ast_bounds } => { - // "Desugar" a constraint like `T: Iterator<Item: Debug>` to - // - // `<T as Iterator>::Item: Debug` - // - // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty` - // parameter to have a skipped binder. - // - // NOTE: If `only_self_bounds` is true, do NOT expand this associated - // type bound into a trait predicate, since we only want to add predicates - // for the `Self` type. + // Lower a constraint like `Item: Debug` as found in HIR bound `T: Iterator<Item: Debug>` + // to a bound involving a projection: `<T as Iterator>::Item: Debug`. + hir::TypeBindingKind::Constraint { bounds: hir_bounds } => { + // NOTE: If `only_self_bounds` is true, do NOT expand this associated type bound into + // a trait predicate, since we only want to add predicates for the `Self` type. if !only_self_bounds.0 { + // Calling `skip_binder` is okay, because `lower_bounds` expects the `param_ty` + // parameter to have a skipped binder. let param_ty = Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder()); - self.add_bounds( + self.lower_poly_bounds( param_ty, - ast_bounds.iter(), + hir_bounds.iter(), bounds, projection_ty.bound_vars(), only_self_bounds, @@ -565,7 +554,7 @@ fn check_assoc_const_binding_type<'tcx>( let mut guar = ty.visit_with(&mut collector).break_value(); let ty_note = ty - .make_suggestable(tcx, false) + .make_suggestable(tcx, false, None) .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty }); let enclosing_item_owner_id = tcx diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 68896768e8d..ca2e14ee359 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1,9 +1,9 @@ -use crate::astconv::AstConv; use crate::errors::{ self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams, ParenthesizedFnTraitExpansion, }; use crate::fluent_generated as fluent; +use crate::hir_ty_lowering::HirTyLowerer; use crate::traits::error_reporting::report_object_safety_error; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; @@ -22,7 +22,7 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, Symbol, DUMMY_SP}; use rustc_trait_selection::traits::object_safety_violations_for_assoc_item; -impl<'tcx> dyn AstConv<'tcx> + '_ { +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// On missing type parameters, emit an E0393 error and provide a structured suggestion using /// the type parameter's name as a placeholder. pub(crate) fn complain_about_missing_type_params( @@ -311,7 +311,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // FIXME(associated_const_equality): This has quite a few false positives and negatives. let wrap_in_braces_sugg = if let Some(binding) = binding && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(hir_ty) } = binding.kind - && let ty = self.ast_ty_to_ty(hir_ty) + && let ty = self.lower_ty(hir_ty) && (ty.is_enum() || ty.references_error()) && tcx.features().associated_const_equality { @@ -349,7 +349,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { }) } - pub(super) fn report_ambiguous_associated_type( + pub(super) fn report_ambiguous_assoc_ty( &self, span: Span, types: &[String], @@ -458,7 +458,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { reported } - pub(crate) fn complain_about_ambiguous_inherent_assoc_type( + pub(crate) fn complain_about_ambiguous_inherent_assoc_ty( &self, name: Ident, candidates: Vec<DefId>, @@ -471,14 +471,14 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { "multiple applicable items in scope" ); err.span_label(name.span, format!("multiple `{name}` found")); - self.note_ambiguous_inherent_assoc_type(&mut err, candidates, span); + self.note_ambiguous_inherent_assoc_ty(&mut err, candidates, span); let reported = err.emit(); self.set_tainted_by_errors(reported); reported } // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate. - fn note_ambiguous_inherent_assoc_type( + fn note_ambiguous_inherent_assoc_ty( &self, err: &mut Diag<'_>, candidates: Vec<DefId>, @@ -521,7 +521,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } // FIXME(inherent_associated_types): Find similarly named associated types and suggest them. - pub(crate) fn complain_about_inherent_assoc_type_not_found( + pub(crate) fn complain_about_inherent_assoc_ty_not_found( &self, name: Ident, self_ty: Ty<'tcx>, @@ -697,7 +697,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { /// reasonable suggestion on how to write it. For the case of multiple associated types in the /// same trait bound have the same name (as they come from different supertraits), we instead /// emit a generic note suggesting using a `where` clause to constraint instead. - pub(crate) fn complain_about_missing_associated_types( + pub(crate) fn complain_about_missing_assoc_tys( &self, associated_types: FxIndexMap<Span, FxIndexSet<DefId>>, potential_assoc_types: Vec<Span>, @@ -1027,7 +1027,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } /// Emits an error regarding forbidden type binding associations -pub fn prohibit_assoc_ty_binding( +pub fn prohibit_assoc_item_binding( tcx: TyCtxt<'_>, span: Span, segment: Option<(&hir::PathSegment<'_>, Span)>, diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index 42e303c10ea..d340a08ee79 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -1,7 +1,7 @@ use super::IsMethodCall; -use crate::astconv::{ - errors::prohibit_assoc_ty_binding, CreateInstantiationsForGenericArgsCtxt, ExplicitLateBound, - GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, +use crate::hir_ty_lowering::{ + errors::prohibit_assoc_item_binding, ExplicitLateBound, GenericArgCountMismatch, + GenericArgCountResult, GenericArgPosition, GenericArgsLowerer, }; use crate::structured_errors::{GenericArgsInfo, StructuredDiag, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; @@ -143,24 +143,22 @@ fn generic_arg_mismatch_err( err.emit() } -/// Creates the relevant generic arguments -/// corresponding to a set of generic parameters. This is a -/// rather complex function. Let us try to explain the role +/// Lower generic arguments from the HIR to the [`rustc_middle::ty`] representation. +/// +/// This is a rather complex function. Let us try to explain the role /// of each of its parameters: /// -/// To start, we are given the `def_id` of the thing whose generic -/// parameters we are instantiating, and a partial set of -/// arguments `parent_args`. In general, the generic arguments -/// for an item begin with arguments for all the "parents" of -/// that item -- e.g., for a method it might include the -/// parameters from the impl. +/// To start, we are given the `def_id` of the thing whose generic parameters we +/// are creating, and a partial set of arguments `parent_args`. In general, +/// the generic arguments for an item begin with arguments for all the "parents" +/// of that item -- e.g., for a method it might include the parameters from the impl. /// /// Therefore, the method begins by walking down these parents, /// starting with the outermost parent and proceed inwards until /// it reaches `def_id`. For each parent `P`, it will check `parent_args` /// first to see if the parent's arguments are listed in there. If so, -/// we can append those and move on. Otherwise, it invokes the -/// three callback functions: +/// we can append those and move on. Otherwise, it uses the provided +/// [`GenericArgsLowerer`] `ctx` which has the following methods: /// /// - `args_for_def_id`: given the `DefId` `P`, supplies back the /// generic arguments that were given to that parent from within @@ -168,18 +166,18 @@ fn generic_arg_mismatch_err( /// might refer to the trait `Foo`, and the arguments might be /// `[T]`. The boolean value indicates whether to infer values /// for arguments whose values were not explicitly provided. -/// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`, -/// instantiate a `GenericArg`. -/// - `inferred_kind`: if no parameter was provided, and inference is enabled, then -/// creates a suitable inference variable. -pub fn create_args_for_parent_generic_args<'tcx: 'a, 'a>( +/// - `provided_kind`: given the generic parameter and the value +/// from `args_for_def_id`, creating a `GenericArg`. +/// - `inferred_kind`: if no parameter was provided, and inference +/// is enabled, then creates a suitable inference variable. +pub fn lower_generic_args<'tcx: 'a, 'a>( tcx: TyCtxt<'tcx>, def_id: DefId, parent_args: &[ty::GenericArg<'tcx>], has_self: bool, self_ty: Option<Ty<'tcx>>, arg_count: &GenericArgCountResult, - ctx: &mut impl CreateInstantiationsForGenericArgsCtxt<'a, 'tcx>, + ctx: &mut impl GenericArgsLowerer<'a, 'tcx>, ) -> GenericArgsRef<'tcx> { // Collect the segments of the path; we need to instantiate arguments // for parameters throughout the entire path (wherever there are @@ -456,7 +454,7 @@ pub(crate) fn check_generic_arg_count( if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() { - prohibit_assoc_ty_binding(tcx, b.span, None); + prohibit_assoc_item_binding(tcx, b.span, None); } let explicit_late_bound = diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index b421a33ba29..80be563686a 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -6,9 +6,9 @@ use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; -use super::AstConv; +use super::HirTyLowerer; -impl<'tcx> dyn AstConv<'tcx> + '_ { +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Make sure that we are in the condition to suggest the blanket implementation. pub(super) fn maybe_lint_blanket_trait_impl<G: EmissionGuarantee>( &self, diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index a912d7f578d..b865bf976b5 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1,6 +1,17 @@ -//! Conversion from AST representation of types to the `ty.rs` representation. -//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an -//! instance of `AstConv`. +//! HIR ty lowering: Lowers type-system entities[^1] from the [HIR][hir] to +//! the [`rustc_middle::ty`] representation. +//! +//! Not to be confused with *AST lowering* which lowers AST constructs to HIR ones +//! or with *THIR* / *MIR* *lowering* / *building* which lowers HIR *bodies* +//! (i.e., “executable code”) to THIR / MIR. +//! +//! Most lowering routines are defined on [`dyn HirTyLowerer`](HirTyLowerer) directly, +//! like the main routine of this module, `lower_ty`. +//! +//! This module used to be called `astconv`. +//! +//! [^1]: This includes types, lifetimes / regions, constants in type positions, +//! trait references and bounds. mod bounds; mod errors; @@ -8,11 +19,11 @@ pub mod generics; mod lint; mod object_safety; -use crate::astconv::errors::prohibit_assoc_ty_binding; -use crate::astconv::generics::{check_generic_arg_count, create_args_for_parent_generic_args}; use crate::bounds::Bounds; use crate::collect::HirPlaceholderCollector; use crate::errors::AmbiguousLifetimeBound; +use crate::hir_ty_lowering::errors::prohibit_assoc_item_binding; +use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; use crate::require_c_abi_if_c_variadic; use rustc_ast::TraitObjectSyntax; @@ -43,8 +54,9 @@ use rustc_trait_selection::traits::{self, ObligationCtxt}; use std::fmt::Display; use std::slice; +/// A path segment that is semantically allowed to have generic arguments. #[derive(Debug)] -pub struct PathSeg(pub DefId, pub usize); +pub struct GenericPathSegment(pub DefId, pub usize); #[derive(Copy, Clone, Debug)] pub struct OnlySelfBounds(pub bool); @@ -67,40 +79,26 @@ pub enum PredicateFilter { SelfAndAssociatedTypeBounds, } -pub trait AstConv<'tcx> { +/// A context which can lower type-system entities from the [HIR][hir] to +/// the [`rustc_middle::ty`] representation. +/// +/// This trait used to be called `AstConv`. +pub trait HirTyLowerer<'tcx> { fn tcx(&self) -> TyCtxt<'tcx>; + /// Returns the [`DefId`] of the overarching item whose constituents get lowered. fn item_def_id(&self) -> DefId; - /// Returns predicates in scope of the form `X: Foo<T>`, where `X` - /// is a type parameter `X` with the given id `def_id` and T - /// matches `assoc_name`. This is a subset of the full set of - /// predicates. - /// - /// This is used for one specific purpose: resolving "short-hand" - /// associated type references like `T::Item`. In principle, we - /// would do that by first getting the full set of predicates in - /// scope and then filtering down to find those that apply to `T`, - /// but this can lead to cycle errors. The problem is that we have - /// to do this resolution *in order to create the predicates in - /// the first place*. Hence, we have this "special pass". - fn get_type_parameter_bounds( - &self, - span: Span, - def_id: LocalDefId, - assoc_name: Ident, - ) -> ty::GenericPredicates<'tcx>; + /// Returns `true` if the current context allows the use of inference variables. + fn allow_infer(&self) -> bool; - /// Returns the lifetime to use when a lifetime is omitted (and not elided). + /// Returns the region to use when a lifetime is omitted (and not elided). fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>>; /// Returns the type to use when a type is omitted. fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>; - /// Returns `true` if `_` is allowed in type signatures in the current context. - fn allow_ty_infer(&self) -> bool; - /// Returns the const to use when a const is omitted. fn ct_infer( &self, @@ -109,14 +107,40 @@ pub trait AstConv<'tcx> { span: Span, ) -> Const<'tcx>; - /// Projecting an associated type from a (potentially) - /// higher-ranked trait reference is more complicated, because of - /// the possibility of late-bound regions appearing in the - /// associated type binding. This is not legal in function - /// signatures for that reason. In a function body, we can always - /// handle it because we can use inference variables to remove the - /// late-bound regions. - fn projected_ty_from_poly_trait_ref( + /// Probe bounds in scope where the bounded type coincides with the given type parameter. + /// + /// Rephrased, this returns bounds of the form `T: Trait`, where `T` is a type parameter + /// with the given `def_id`. This is a subset of the full set of bounds. + /// + /// This method may use the given `assoc_name` to disregard bounds whose trait reference + /// doesn't define an associated item with the provided name. + /// + /// This is used for one specific purpose: Resolving “short-hand” associated type references + /// like `T::Item` where `T` is a type parameter. In principle, we would do that by first + /// getting the full set of predicates in scope and then filtering down to find those that + /// apply to `T`, but this can lead to cycle errors. The problem is that we have to do this + /// resolution *in order to create the predicates in the first place*. + /// Hence, we have this “special pass”. + fn probe_ty_param_bounds( + &self, + span: Span, + def_id: LocalDefId, + assoc_name: Ident, + ) -> ty::GenericPredicates<'tcx>; + + /// Lower an associated type to a projection. + /// + /// This method has to be defined by the concrete lowering context because + /// dealing with higher-ranked trait references depends on its capabilities: + /// + /// If the context can make use of type inference, it can simply instantiate + /// any late-bound vars bound by the trait reference with inference variables. + /// If it doesn't support type inference, there is nothing reasonable it can + /// do except reject the associated type. + /// + /// The canonical example of this is associated type `T::P` where `T` is a type + /// param constrained by `T: for<'a> Trait<'a>` and where `Trait` defines `P`. + fn lower_assoc_ty( &self, span: Span, item_def_id: DefId, @@ -125,28 +149,35 @@ pub trait AstConv<'tcx> { ) -> Ty<'tcx>; /// Returns `AdtDef` if `ty` is an ADT. - /// Note that `ty` might be a projection type that needs normalization. + /// + /// Note that `ty` might be a alias type that needs normalization. /// This used to get the enum variants in scope of the type. /// For example, `Self::A` could refer to an associated type /// or to an enum variant depending on the result of this function. fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>>; - /// Invoked when we encounter an error from some prior pass - /// (e.g., resolve) that is translated into a ty-error. This is - /// used to help suppress derived errors typeck might otherwise - /// report. - fn set_tainted_by_errors(&self, e: ErrorGuaranteed); - + /// Record the lowered type of a HIR node in this context. fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span); - fn astconv(&self) -> &dyn AstConv<'tcx> + /// The inference context of the lowering context if applicable. + fn infcx(&self) -> Option<&InferCtxt<'tcx>>; + + /// Taint the context with errors. + /// + /// Invoke this when you encounter an error from some prior pass like name resolution. + /// This is used to help suppress derived errors typeck might otherwise report. + fn set_tainted_by_errors(&self, e: ErrorGuaranteed); + + /// Convenience method for coercing the lowering context into a trait object type. + /// + /// Most lowering routines are defined on the trait object type directly + /// necessitating a coercion step from the concrete lowering context. + fn lowerer(&self) -> &dyn HirTyLowerer<'tcx> where Self: Sized, { self } - - fn infcx(&self) -> Option<&InferCtxt<'tcx>>; } /// New-typed boolean indicating whether explicit late-bound lifetimes @@ -197,7 +228,11 @@ pub struct GenericArgCountResult { pub correct: Result<(), GenericArgCountMismatch>, } -pub trait CreateInstantiationsForGenericArgsCtxt<'a, 'tcx> { +/// A context which can lower HIR's [`GenericArg`] to `rustc_middle`'s [`ty::GenericArg`]. +/// +/// Its only consumer is [`generics::lower_generic_args`]. +/// Read its documentation to learn more. +pub trait GenericArgsLowerer<'a, 'tcx> { fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'tcx>>, bool); fn provided_kind( @@ -214,9 +249,10 @@ pub trait CreateInstantiationsForGenericArgsCtxt<'a, 'tcx> { ) -> ty::GenericArg<'tcx>; } -impl<'tcx> dyn AstConv<'tcx> + '_ { +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Lower a lifetime from the HIR to our internal notion of a lifetime called a *region*. #[instrument(level = "debug", skip(self), ret)] - pub fn ast_region_to_region( + pub fn lower_lifetime( &self, lifetime: &hir::Lifetime, def: Option<&ty::GenericParamDef>, @@ -271,15 +307,13 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, - /// returns an appropriate set of generic arguments for this particular reference to `I`. - pub fn ast_path_args_for_ty( + pub fn lower_generic_args_of_path_segment( &self, span: Span, def_id: DefId, item_segment: &hir::PathSegment<'tcx>, ) -> GenericArgsRef<'tcx> { - let (args, _) = self.create_args_for_ast_path( + let (args, _) = self.lower_generic_args_of_path( span, def_id, &[], @@ -288,20 +322,19 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { ty::BoundConstness::NotConst, ); if let Some(b) = item_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span))); + prohibit_assoc_item_binding(self.tcx(), b.span, Some((item_segment, span))); } - args } - /// Given the type/lifetime/const arguments provided to some path (along with - /// an implicit `Self`, if this is a trait reference), returns the complete - /// set of generic arguments. This may involve applying defaulted type parameters. + /// Lower the generic arguments provided to some path. /// - /// Constraints on associated types are not converted here but - /// separately in `add_predicates_for_ast_type_binding`. + /// If this is a trait reference, you also need to pass the self type `self_ty`. + /// The lowering process may involve applying defaulted type parameters. /// - /// Example: + /// Associated item bindings are not handled here! + /// + /// ### Example /// /// ```ignore (illustrative) /// T: std::ops::Index<usize, Output = u32> @@ -328,7 +361,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { /// type itself: `['a]`. The returned `GenericArgsRef` concatenates these two /// lists: `[Vec<u8>, u8, 'a]`. #[instrument(level = "debug", skip(self, span), ret)] - fn create_args_for_ast_path( + fn lower_generic_args_of_path( &self, span: Span, def_id: DefId, @@ -343,7 +376,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let tcx = self.tcx(); let generics = tcx.generics_of(def_id); - debug!("generics: {:?}", generics); + debug!(?generics); if generics.has_self { if generics.parent.is_some() { @@ -381,8 +414,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { return (tcx.mk_args(parent_args), arg_count); } - struct InstantiationsForAstPathCtxt<'a, 'tcx> { - astconv: &'a dyn AstConv<'tcx>, + struct GenericArgsCtxt<'a, 'tcx> { + lowerer: &'a dyn HirTyLowerer<'tcx>, def_id: DefId, generic_args: &'a GenericArgs<'tcx>, span: Span, @@ -390,9 +423,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { infer_args: bool, } - impl<'a, 'tcx> CreateInstantiationsForGenericArgsCtxt<'a, 'tcx> - for InstantiationsForAstPathCtxt<'a, 'tcx> - { + impl<'a, 'tcx> GenericArgsLowerer<'a, 'tcx> for GenericArgsCtxt<'a, 'tcx> { fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'tcx>>, bool) { if did == self.def_id { (Some(self.generic_args), self.infer_args) @@ -407,7 +438,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { param: &ty::GenericParamDef, arg: &GenericArg<'tcx>, ) -> ty::GenericArg<'tcx> { - let tcx = self.astconv.tcx(); + let tcx = self.lowerer.tcx(); let mut handle_ty_args = |has_default, ty: &hir::Ty<'tcx>| { if has_default { @@ -426,17 +457,17 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { }, ); } - if let (hir::TyKind::Infer, false) = (&ty.kind, self.astconv.allow_ty_infer()) { + if let (hir::TyKind::Infer, false) = (&ty.kind, self.lowerer.allow_infer()) { self.inferred_params.push(ty.span); Ty::new_misc_error(tcx).into() } else { - self.astconv.ast_ty_to_ty(ty).into() + self.lowerer.lower_ty(ty).into() } }; match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.astconv.ast_region_to_region(lt, Some(param)).into() + self.lowerer.lower_lifetime(lt, Some(param)).into() } (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => { handle_ty_args(has_default, ty) @@ -455,8 +486,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { .type_of(param.def_id) .no_bound_vars() .expect("const parameter types cannot be generic"); - if self.astconv.allow_ty_infer() { - self.astconv.ct_infer(ty, Some(param), inf.span).into() + if self.lowerer.allow_infer() { + self.lowerer.ct_infer(ty, Some(param), inf.span).into() } else { self.inferred_params.push(inf.span); ty::Const::new_misc_error(tcx, ty).into() @@ -475,10 +506,10 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { param: &ty::GenericParamDef, infer_args: bool, ) -> ty::GenericArg<'tcx> { - let tcx = self.astconv.tcx(); + let tcx = self.lowerer.tcx(); match param.kind { GenericParamDefKind::Lifetime => self - .astconv + .lowerer .re_infer(Some(param), self.span) .unwrap_or_else(|| { debug!(?param, "unelided lifetime in signature"); @@ -504,7 +535,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } tcx.at(self.span).type_of(param.def_id).instantiate(tcx, args).into() } else if infer_args { - self.astconv.ty_infer(Some(param), self.span).into() + self.lowerer.ty_infer(Some(param), self.span).into() } else { // We've already errored above about the mismatch. Ty::new_misc_error(tcx).into() @@ -526,7 +557,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { .into() } else { if infer_args { - self.astconv.ct_infer(ty, Some(param), self.span).into() + self.lowerer.ct_infer(ty, Some(param), self.span).into() } else { // We've already errored above about the mismatch. ty::Const::new_misc_error(tcx, ty).into() @@ -537,8 +568,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - let mut args_ctx = InstantiationsForAstPathCtxt { - astconv: self, + let mut args_ctx = GenericArgsCtxt { + lowerer: self, def_id, span, generic_args: segment.args(), @@ -557,7 +588,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { arg_count.correct = Err(GenericArgCountMismatch { reported: Some(e), invalid_args: vec![] }); } - let args = create_args_for_parent_generic_args( + let args = lower_generic_args( tcx, def_id, parent_args, @@ -570,18 +601,16 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { (args, arg_count) } - pub fn create_args_for_associated_item( + #[instrument(level = "debug", skip_all)] + pub fn lower_generic_args_of_assoc_item( &self, span: Span, item_def_id: DefId, item_segment: &hir::PathSegment<'tcx>, parent_args: GenericArgsRef<'tcx>, ) -> GenericArgsRef<'tcx> { - debug!( - "create_args_for_associated_item(span: {:?}, item_def_id: {:?}, item_segment: {:?}", - span, item_def_id, item_segment - ); - let (args, _) = self.create_args_for_ast_path( + debug!(?span, ?item_def_id, ?item_segment); + let (args, _) = self.lower_generic_args_of_path( span, item_def_id, parent_args, @@ -589,28 +618,23 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { None, ty::BoundConstness::NotConst, ); - if let Some(b) = item_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span))); + prohibit_assoc_item_binding(self.tcx(), b.span, Some((item_segment, span))); } - args } - /// Instantiates the path for the given trait reference, assuming that it's - /// bound to a valid trait type. Returns the `DefId` of the defining trait. - /// The type _cannot_ be a type other than a trait type. + /// Lower a trait reference as found in an impl header as the implementee. /// - /// If the `projections` argument is `None`, then assoc type bindings like `Foo<T = X>` - /// are disallowed. Otherwise, they are pushed onto the vector given. - pub fn instantiate_mono_trait_ref( + /// The self type `self_ty` is the implementer of the trait. + pub fn lower_impl_trait_ref( &self, trait_ref: &hir::TraitRef<'tcx>, self_ty: Ty<'tcx>, ) -> ty::TraitRef<'tcx> { - self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); + self.prohibit_generic_args(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); - self.ast_path_to_mono_trait_ref( + self.lower_mono_trait_ref( trait_ref.path.span, trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), self_ty, @@ -620,32 +644,36 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { ) } - /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct - /// a full trait reference. The resulting trait reference is returned. This may also generate - /// auxiliary bounds, which are added to `bounds`. + /// Lower a polymorphic trait reference given a self type into `bounds`. /// - /// Example: + /// *Polymorphic* in the sense that it may bind late-bound vars. /// - /// ```ignore (illustrative) - /// poly_trait_ref = Iterator<Item = u32> - /// self_ty = Foo - /// ``` + /// This may generate auxiliary bounds if the trait reference contains associated item bindings. + /// + /// ### Example + /// + /// Given the trait ref `Iterator<Item = u32>` and the self type `Ty`, this will add the /// - /// this would return `Foo: Iterator` and add `<Foo as Iterator>::Item = u32` into `bounds`. + /// 1. *trait predicate* `<Ty as Iterator>` (known as `Foo: Iterator` in surface syntax) and the + /// 2. *projection predicate* `<Ty as Iterator>::Item = u32` + /// + /// to `bounds`. + /// + /// ### A Note on Binders + /// + /// Against our usual convention, there is an implied binder around the `self_ty` and the + /// `trait_ref` here. So they may reference late-bound vars. /// - /// **A note on binders:** against our usual convention, there is an implied binder around - /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions. /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>` - /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be - /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly, - /// however. + /// where `'a` is a bound region at depth 0. Similarly, the `trait_ref` would be `Bar<'a>`. + /// The lowered poly-trait-ref will track this binder explicitly, however. #[instrument(level = "debug", skip(self, span, constness, bounds))] - pub(crate) fn instantiate_poly_trait_ref( + pub(crate) fn lower_poly_trait_ref( &self, trait_ref: &hir::TraitRef<'tcx>, span: Span, constness: ty::BoundConstness, - polarity: ty::ImplPolarity, + polarity: ty::PredicatePolarity, self_ty: Ty<'tcx>, bounds: &mut Bounds<'tcx>, only_self_bounds: OnlySelfBounds, @@ -653,10 +681,10 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()); let trait_segment = trait_ref.path.segments.last().unwrap(); - self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); + self.prohibit_generic_args(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false); - let (generic_args, arg_count) = self.create_args_for_ast_path( + let (generic_args, arg_count) = self.lower_generic_args_of_path( trait_ref.path.span, trait_def_id, &[], @@ -682,7 +710,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // Don't register additional associated type bounds for negative bounds, // since we should have emitten an error for them earlier, and they will // not be well-formed! - if polarity == ty::ImplPolarity::Negative { + if polarity != ty::PredicatePolarity::Positive { assert!( self.tcx().dcx().has_errors().is_some(), "negative trait bounds should not have bindings", @@ -691,7 +719,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } // Specify type to assert that error was already reported in `Err` case. - let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding( + let _: Result<_, ErrorGuaranteed> = self.lower_assoc_item_binding( trait_ref.hir_ref_id, poly_trait_ref, binding, @@ -706,19 +734,22 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { arg_count } - fn ast_path_to_mono_trait_ref( + /// Lower a monomorphic trait reference given a self type while prohibiting associated item bindings. + /// + /// *Monomorphic* in the sense that it doesn't bind any late-bound vars. + fn lower_mono_trait_ref( &self, span: Span, trait_def_id: DefId, self_ty: Ty<'tcx>, trait_segment: &hir::PathSegment<'tcx>, is_impl: bool, - // FIXME(effects) move all host param things in astconv to hir lowering + // FIXME(effects): Move all host param things in HIR ty lowering to AST lowering. constness: ty::BoundConstness, ) -> ty::TraitRef<'tcx> { self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); - let (generic_args, _) = self.create_args_for_ast_path( + let (generic_args, _) = self.lower_generic_args_of_path( span, trait_def_id, &[], @@ -727,12 +758,12 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { constness, ); if let Some(b) = trait_segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span, Some((trait_segment, span))); + prohibit_assoc_item_binding(self.tcx(), b.span, Some((trait_segment, span))); } ty::TraitRef::new(self.tcx(), trait_def_id, generic_args) } - fn trait_defines_associated_item_named( + fn probe_trait_that_defines_assoc_item( &self, trait_def_id: DefId, assoc_kind: ty::AssocKind, @@ -744,14 +775,14 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { .is_some() } - fn ast_path_to_ty( + fn lower_path_segment( &self, span: Span, did: DefId, item_segment: &hir::PathSegment<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx(); - let args = self.ast_path_args_for_ty(span, did, item_segment); + let args = self.lower_generic_args_of_path_segment(span, did, item_segment); if let DefKind::TyAlias = tcx.def_kind(did) && tcx.type_alias_is_lazy(did) @@ -766,30 +797,27 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - // Search for a bound on a type parameter which includes the associated item - // given by `assoc_name`. `ty_param_def_id` is the `DefId` of the type parameter - // This function will fail if there are no suitable bounds or there is - // any ambiguity. - fn find_bound_for_assoc_item( + /// Search for a trait bound on a type parameter whose trait defines the associated type given by `assoc_name`. + /// + /// This fails if there is no such bound in the list of candidates or if there are multiple + /// candidates in which case it reports ambiguity. + /// + /// `ty_param_def_id` is the `LocalDefId` of the type parameter. + #[instrument(level = "debug", skip_all, ret)] + fn probe_single_ty_param_bound_for_assoc_ty( &self, ty_param_def_id: LocalDefId, assoc_name: Ident, span: Span, ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> { + debug!(?ty_param_def_id, ?assoc_name, ?span); let tcx = self.tcx(); - debug!( - "find_bound_for_assoc_item(ty_param_def_id={:?}, assoc_name={:?}, span={:?})", - ty_param_def_id, assoc_name, span, - ); - - let predicates = - &self.get_type_parameter_bounds(span, ty_param_def_id, assoc_name).predicates; - - debug!("find_bound_for_assoc_item: predicates={:#?}", predicates); + let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates; + debug!("predicates={:#?}", predicates); let param_name = tcx.hir().ty_param_name(ty_param_def_id); - self.one_bound_for_assoc_item( + self.probe_single_bound_for_assoc_item( || { traits::transitive_bounds_that_define_assoc_item( tcx, @@ -808,10 +836,12 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { ) } - // Checks that `bounds` contains exactly one element and reports appropriate - // errors otherwise. + /// Search for a single trait bound whose trait defines the associated item given by `assoc_name`. + /// + /// This fails if there is no such bound in the list of candidates or if there are multiple + /// candidates in which case it reports ambiguity. #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, binding), ret)] - fn one_bound_for_assoc_item<I>( + fn probe_single_bound_for_assoc_item<I>( &self, all_candidates: impl Fn() -> I, ty_param_name: impl Display, @@ -827,7 +857,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let tcx = self.tcx(); let mut matching_candidates = all_candidates().filter(|r| { - self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name) + self.probe_trait_that_defines_assoc_item(r.def_id(), assoc_kind, assoc_name) }); let Some(bound) = matching_candidates.next() else { @@ -886,7 +916,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { match binding.kind { hir::TypeBindingKind::Equality { term } => { let term: ty::Term<'_> = match term { - hir::Term::Ty(ty) => self.ast_ty_to_ty(ty).into(), + hir::Term::Ty(ty) => self.lower_ty(ty).into(), hir::Term::Const(ct) => { ty::Const::from_anon_const(tcx, ct.def_id).into() } @@ -932,16 +962,35 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { Ok(bound) } - // Create a type from a path to an associated type or to an enum variant. - // For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C` - // and item_segment is the path segment for `D`. We return a type and a def for - // the whole path. - // Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type - // parameter or `Self`. + /// Lower a [type-relative] path referring to an associated type or to an enum variant. + /// + /// If the path refers to an enum variant and `permit_variants` holds, + /// the returned type is simply the provided self type `qself_ty`. + /// + /// A path like `A::B::C::D` is understood as `<A::B::C>::D`. I.e., + /// `qself_ty` / `qself` is `A::B::C` and `assoc_segment` is `D`. + /// We return the lowered type and the `DefId` for the whole path. + /// + /// We only support associated type paths whose self type is a type parameter or a `Self` + /// type alias (in a trait impl) like `T::Ty` (where `T` is a ty param) or `Self::Ty`. + /// We **don't** support paths whose self type is an arbitrary type like `Struct::Ty` where + /// struct `Struct` impls an in-scope trait that defines an associated type called `Ty`. + /// For the latter case, we report ambiguity. + /// While desirable to support, the implemention would be non-trivial. Tracked in [#22519]. + /// + /// At the time of writing, *inherent associated types* are also resolved here. This however + /// is [problematic][iat]. A proper implementation would be as non-trivial as the one + /// described in the previous paragraph and their modeling of projections would likely be + /// very similar in nature. + /// + /// [type-relative]: hir::QPath::TypeRelative + /// [#22519]: https://github.com/rust-lang/rust/issues/22519 + /// [iat]: https://github.com/rust-lang/rust/issues/8995#issuecomment-1569208403 + // // NOTE: When this function starts resolving `Trait::AssocTy` successfully // it should also start reporting the `BARE_TRAIT_OBJECTS` lint. - #[instrument(level = "debug", skip(self, hir_ref_id, span, qself, assoc_segment), fields(assoc_ident=?assoc_segment.ident), ret)] - pub fn associated_path_to_ty( + #[instrument(level = "debug", skip_all, ret)] + pub fn lower_assoc_path( &self, hir_ref_id: hir::HirId, span: Span, @@ -950,7 +999,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { assoc_segment: &hir::PathSegment<'tcx>, permit_variants: bool, ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> { + debug!(%qself_ty, ?assoc_segment.ident); let tcx = self.tcx(); + let assoc_ident = assoc_segment.ident; let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { path.res @@ -969,7 +1020,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { if let Some(variant_def) = variant_def { if permit_variants { tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None); - self.prohibit_generics(slice::from_ref(assoc_segment).iter(), |err| { + self.prohibit_generic_args(slice::from_ref(assoc_segment).iter(), |err| { err.note("enum variants can't have type parameters"); let type_name = tcx.item_name(adt_def.did()); let msg = format!( @@ -1069,7 +1120,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - if let Some((ty, did)) = self.lookup_inherent_assoc_ty( + // FIXME(inherent_associated_types, #106719): Support self types other than ADTs. + if let Some((ty, did)) = self.probe_inherent_assoc_ty( assoc_ident, assoc_segment, adt_def.did(), @@ -1092,7 +1144,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { tcx.dcx().span_bug(span, "expected cycle error"); }; - self.one_bound_for_assoc_item( + self.probe_single_bound_for_assoc_item( || { traits::supertraits( tcx, @@ -1110,7 +1162,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { ( &ty::Param(_), Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), - ) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?, + ) => self.probe_single_ty_param_bound_for_assoc_ty( + param_did.expect_local(), + assoc_ident, + span, + )?, _ => { let reported = if variant_resolution.is_some() { // Variant in type position @@ -1172,7 +1228,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident); // Don't print `ty::Error` to the user. - self.report_ambiguous_associated_type( + self.report_ambiguous_assoc_ty( span, &[qself_ty.to_string()], &traits, @@ -1185,8 +1241,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { }; let trait_did = bound.def_id(); - let assoc_ty_did = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, trait_did).unwrap(); - let ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound); + let assoc_ty_did = self.probe_assoc_ty(assoc_ident, hir_ref_id, span, trait_did).unwrap(); + let ty = self.lower_assoc_ty(span, assoc_ty_did, assoc_segment, bound); if let Some(variant_def_id) = variant_resolution { tcx.node_span_lint( @@ -1220,7 +1276,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { Ok((ty, DefKind::AssocTy, assoc_ty_did)) } - fn lookup_inherent_assoc_ty( + fn probe_inherent_assoc_ty( &self, name: Ident, segment: &hir::PathSegment<'tcx>, @@ -1234,8 +1290,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // Don't attempt to look up inherent associated types when the feature is not enabled. // Theoretically it'd be fine to do so since we feature-gate their definition site. // However, due to current limitations of the implementation (caused by us performing - // selection in AstConv), IATs can lead to cycle errors (#108491, #110106) which mask the - // feature-gate error, needlessly confusing users that use IATs by accident (#113265). + // selection during HIR ty lowering instead of in the trait solver), IATs can lead to cycle + // errors (#108491) which mask the feature-gate error, needlessly confusing users + // who use IATs by accident (#113265). if !tcx.features().inherent_associated_types { return Ok(None); } @@ -1243,7 +1300,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let candidates: Vec<_> = tcx .inherent_impls(adt_did)? .iter() - .filter_map(|&impl_| Some((impl_, self.lookup_assoc_ty_unchecked(name, block, impl_)?))) + .filter_map(|&impl_| Some((impl_, self.probe_assoc_ty_unchecked(name, block, impl_)?))) .collect(); if candidates.is_empty() { @@ -1290,10 +1347,10 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { self.check_assoc_ty(assoc_item, name, def_scope, block, span); // FIXME(fmease): Currently creating throwaway `parent_args` to please - // `create_args_for_associated_item`. Modify the latter instead (or sth. similar) to + // `lower_generic_args_of_assoc_item`. Modify the latter instead (or sth. similar) to // not require the parent args logic. let parent_args = ty::GenericArgs::identity_for_item(tcx, impl_); - let args = self.create_args_for_associated_item(span, assoc_item, segment, parent_args); + let args = self.lower_generic_args_of_assoc_item(span, assoc_item, segment, parent_args); let args = tcx.mk_args_from_iter( std::iter::once(ty::GenericArg::from(self_ty)) .chain(args.into_iter().skip(parent_args.len())), @@ -1356,7 +1413,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { .collect(); match &applicable_candidates[..] { - &[] => Err(self.complain_about_inherent_assoc_type_not_found( + &[] => Err(self.complain_about_inherent_assoc_ty_not_found( name, self_ty, candidates, @@ -1366,7 +1423,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { &[applicable_candidate] => Ok(applicable_candidate), - &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc_type( + &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc_ty( name, applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(), span, @@ -1374,19 +1431,19 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - fn lookup_assoc_ty( + fn probe_assoc_ty( &self, name: Ident, block: hir::HirId, span: Span, scope: DefId, ) -> Option<DefId> { - let (item, def_scope) = self.lookup_assoc_ty_unchecked(name, block, scope)?; + let (item, def_scope) = self.probe_assoc_ty_unchecked(name, block, scope)?; self.check_assoc_ty(item, name, def_scope, block, span); Some(item) } - fn lookup_assoc_ty_unchecked( + fn probe_assoc_ty_unchecked( &self, name: Ident, block: hir::HirId, @@ -1491,7 +1548,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { .collect() } - fn qpath_to_ty( + /// Lower a qualified path to a type. + #[instrument(level = "debug", skip_all)] + fn lower_qpath( &self, span: Span, opt_self_ty: Option<Ty<'tcx>>, @@ -1503,22 +1562,19 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let tcx = self.tcx(); let trait_def_id = tcx.parent(item_def_id); - - debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id); + debug!(?trait_def_id); let Some(self_ty) = opt_self_ty else { let path_str = tcx.def_path_str(trait_def_id); let def_id = self.item_def_id(); - - debug!("qpath_to_ty: self.item_def_id()={:?}", def_id); + debug!(item_def_id = ?def_id); let parent_def_id = def_id .as_local() .map(|def_id| tcx.local_def_id_to_hir_id(def_id)) .map(|hir_id| tcx.hir().get_parent_item(hir_id).to_def_id()); - - debug!("qpath_to_ty: parent_def_id={:?}", parent_def_id); + debug!(?parent_def_id); // If the trait in segment is the same as the trait defining the item, // use the `<Self as ..>` syntax in the error. @@ -1545,7 +1601,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that // references the trait. Relevant for the first case in // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` - let reported = self.report_ambiguous_associated_type( + let reported = self.report_ambiguous_assoc_ty( span, &type_names, &[path_str], @@ -1553,27 +1609,19 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { ); return Ty::new_error(tcx, reported); }; + debug!(?self_ty); - debug!("qpath_to_ty: self_type={:?}", self_ty); - - let trait_ref = self.ast_path_to_mono_trait_ref( - span, - trait_def_id, - self_ty, - trait_segment, - false, - constness, - ); + let trait_ref = + self.lower_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false, constness); + debug!(?trait_ref); let item_args = - self.create_args_for_associated_item(span, item_def_id, item_segment, trait_ref.args); - - debug!("qpath_to_ty: trait_ref={:?}", trait_ref); + self.lower_generic_args_of_assoc_item(span, item_def_id, item_segment, trait_ref.args); Ty::new_projection(tcx, item_def_id, item_args) } - pub fn prohibit_generics<'a>( + pub fn prohibit_generic_args<'a>( &self, segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone, extend: impl Fn(&mut Diag<'_>), @@ -1676,25 +1724,41 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { for segment in segments { // Only emit the first error to avoid overloading the user with error messages. if let Some(b) = segment.args().bindings.first() { - prohibit_assoc_ty_binding(self.tcx(), b.span, None); + prohibit_assoc_item_binding(self.tcx(), b.span, None); return true; } } emitted } + /// Probe path segments that are semantically allowed to have generic arguments. + /// + /// ### Example + /// + /// ```ignore (illustrative) + /// Option::None::<()> + /// // ^^^^ permitted to have generic args + /// + /// // ==> [GenericPathSegment(Option_def_id, 1)] + /// + /// Option::<()>::None + /// // ^^^^^^ ^^^^ *not* permitted to have generic args + /// // permitted to have generic args + /// + /// // ==> [GenericPathSegment(Option_def_id, 0)] + /// ``` // FIXME(eddyb, varkor) handle type paths here too, not just value ones. - pub fn def_ids_for_value_path_segments( + pub fn probe_generic_path_segments( &self, segments: &[hir::PathSegment<'_>], self_ty: Option<Ty<'tcx>>, kind: DefKind, def_id: DefId, span: Span, - ) -> Vec<PathSeg> { - // We need to extract the type parameters supplied by the user in + ) -> Vec<GenericPathSegment> { + // We need to extract the generic arguments supplied by the user in // the path `path`. Due to the current setup, this is a bit of a - // tricky-process; the problem is that resolve only tells us the + // tricky process; the problem is that resolve only tells us the // end-point of the path resolution, and not the intermediate steps. // Luckily, we can (at least for now) deduce the intermediate steps // just from the end-point. @@ -1705,35 +1769,35 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // // struct Foo<T>(...) // - // In this case, the parameters are declared in the type space. + // In this case, the generic arguments are declared in the type space. // // 2. Reference to a constructor of an enum variant: // // enum E<T> { Foo(...) } // - // In this case, the parameters are defined in the type space, + // In this case, the generic arguments are defined in the type space, // but may be specified either on the type or the variant. // - // 3. Reference to a fn item or a free constant: + // 3. Reference to a free function or constant: // - // fn foo<T>() { } + // fn foo<T>() {} // // In this case, the path will again always have the form - // `a::b::foo::<T>` where only the final segment should have - // type parameters. However, in this case, those parameters are - // declared on a value, and hence are in the `FnSpace`. + // `a::b::foo::<T>` where only the final segment should have generic + // arguments. However, in this case, those arguments are declared on + // a value, and hence are in the value space. // - // 4. Reference to a method or an associated constant: + // 4. Reference to an associated function or constant: // // impl<A> SomeStruct<A> { - // fn foo<B>(...) + // fn foo<B>(...) {} // } // - // Here we can have a path like - // `a::b::SomeStruct::<A>::foo::<B>`, in which case parameters - // may appear in two places. The penultimate segment, - // `SomeStruct::<A>`, contains parameters in TypeSpace, and the - // final segment, `foo::<B>` contains parameters in fn space. + // Here we can have a path like `a::b::SomeStruct::<A>::foo::<B>`, + // in which case generic arguments may appear in two places. The + // penultimate segment, `SomeStruct::<A>`, contains generic arguments + // in the type space, and the final segment, `foo::<B>` contains + // generic arguments in value space. // // The first step then is to categorize the segments appropriately. @@ -1742,7 +1806,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { assert!(!segments.is_empty()); let last = segments.len() - 1; - let mut path_segs = vec![]; + let mut generic_segments = vec![]; match kind { // Case 1. Reference to a struct constructor. @@ -1753,7 +1817,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // Variant and struct constructors use the // generics of their parent type definition. let generics_def_id = generics.parent.unwrap_or(def_id); - path_segs.push(PathSeg(generics_def_id, last)); + generic_segments.push(GenericPathSegment(generics_def_id, last)); } // Case 2. Reference to a variant constructor. @@ -1786,56 +1850,53 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // generics of their parent type definition. (generics.parent.unwrap_or(def_id), last) }; - path_segs.push(PathSeg(generics_def_id, index)); + generic_segments.push(GenericPathSegment(generics_def_id, index)); } // Case 3. Reference to a top-level value. DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static { .. } => { - path_segs.push(PathSeg(def_id, last)); + generic_segments.push(GenericPathSegment(def_id, last)); } // Case 4. Reference to a method or associated const. DefKind::AssocFn | DefKind::AssocConst => { if segments.len() >= 2 { let generics = tcx.generics_of(def_id); - path_segs.push(PathSeg(generics.parent.unwrap(), last - 1)); + generic_segments.push(GenericPathSegment(generics.parent.unwrap(), last - 1)); } - path_segs.push(PathSeg(def_id, last)); + generic_segments.push(GenericPathSegment(def_id, last)); } kind => bug!("unexpected definition kind {:?} for {:?}", kind, def_id), } - debug!("path_segs = {:?}", path_segs); + debug!(?generic_segments); - path_segs + generic_segments } - /// Check a type `Path` and convert it to a `Ty`. - pub fn res_to_ty( + /// Lower a type `Path` to a type. + #[instrument(level = "debug", skip_all)] + pub fn lower_path( &self, opt_self_ty: Option<Ty<'tcx>>, path: &hir::Path<'tcx>, hir_id: hir::HirId, permit_variants: bool, ) -> Ty<'tcx> { + debug!(?path.res, ?opt_self_ty, ?path.segments); let tcx = self.tcx(); - debug!( - "res_to_ty(res={:?}, opt_self_ty={:?}, path_segments={:?})", - path.res, opt_self_ty, path.segments - ); - let span = path.span; match path.res { Res::Def(DefKind::OpaqueTy, did) => { // Check for desugared `impl Trait`. assert!(tcx.is_type_alias_impl_trait(did)); let item_segment = path.segments.split_last().unwrap(); - self.prohibit_generics(item_segment.1.iter(), |err| { + self.prohibit_generic_args(item_segment.1.iter(), |err| { err.note("`impl Trait` types can't have type parameters"); }); - let args = self.ast_path_args_for_ty(span, did, item_segment.0); + let args = self.lower_generic_args_of_path_segment(span, did, item_segment.0); Ty::new_opaque(tcx, did, args) } Res::Def( @@ -1847,44 +1908,44 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { did, ) => { assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments.split_last().unwrap().1.iter(), |_| {}); - self.ast_path_to_ty(span, did, path.segments.last().unwrap()) + self.prohibit_generic_args(path.segments.split_last().unwrap().1.iter(), |_| {}); + self.lower_path_segment(span, did, path.segments.last().unwrap()) } Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => { - // Convert "variant type" as if it were a real type. + // Lower "variant type" as if it were a real type. // The resulting `Ty` is type of the variant's enum for now. assert_eq!(opt_self_ty, None); - let path_segs = - self.def_ids_for_value_path_segments(path.segments, None, kind, def_id, span); - let generic_segs: FxHashSet<_> = - path_segs.iter().map(|PathSeg(_, index)| index).collect(); - self.prohibit_generics( + let generic_segments = + self.probe_generic_path_segments(path.segments, None, kind, def_id, span); + let indices: FxHashSet<_> = + generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); + self.prohibit_generic_args( path.segments.iter().enumerate().filter_map(|(index, seg)| { - if !generic_segs.contains(&index) { Some(seg) } else { None } + if !indices.contains(&index) { Some(seg) } else { None } }), |err| { err.note("enum variants can't have type parameters"); }, ); - let PathSeg(def_id, index) = path_segs.last().unwrap(); - self.ast_path_to_ty(span, *def_id, &path.segments[*index]) + let GenericPathSegment(def_id, index) = generic_segments.last().unwrap(); + self.lower_path_segment(span, *def_id, &path.segments[*index]) } Res::Def(DefKind::TyParam, def_id) => { assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments.iter(), |err| { + self.prohibit_generic_args(path.segments.iter(), |err| { if let Some(span) = tcx.def_ident_span(def_id) { let name = tcx.item_name(def_id); err.span_note(span, format!("type parameter `{name}` defined here")); } }); - self.hir_id_to_bound_ty(hir_id) + self.lower_ty_param(hir_id) } Res::SelfTyParam { .. } => { // `Self` in trait or type alias. assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments.iter(), |err| { + self.prohibit_generic_args(path.segments.iter(), |err| { if let [hir::PathSegment { args: Some(args), ident, .. }] = &path.segments { err.span_suggestion_verbose( ident.span.shrink_to_hi().to(args.span_ext), @@ -1902,7 +1963,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // Try to evaluate any array length constants. let ty = tcx.at(span).type_of(def_id).instantiate_identity(); let span_of_impl = tcx.span_of_impl(def_id); - self.prohibit_generics(path.segments.iter(), |err| { + self.prohibit_generic_args(path.segments.iter(), |err| { let def_id = match *ty.kind() { ty::Adt(self_def, _) => self_def.did(), _ => return, @@ -2000,14 +2061,14 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } Res::Def(DefKind::AssocTy, def_id) => { debug_assert!(path.segments.len() >= 2); - self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {}); + self.prohibit_generic_args(path.segments[..path.segments.len() - 2].iter(), |_| {}); // HACK: until we support `<Type as ~const Trait>`, assume all of them are. let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) { ty::BoundConstness::ConstIfConst } else { ty::BoundConstness::NotConst }; - self.qpath_to_ty( + self.lower_qpath( span, opt_self_ty, def_id, @@ -2018,7 +2079,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } Res::PrimTy(prim_ty) => { assert_eq!(opt_self_ty, None); - self.prohibit_generics(path.segments.iter(), |err| { + self.prohibit_generic_args(path.segments.iter(), |err| { let name = prim_ty.name_str(); for segment in path.segments { if let Some(args) = segment.args { @@ -2052,9 +2113,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - // Converts a hir id corresponding to a type parameter to - // a early-bound `ty::Param` or late-bound `ty::Bound`. - pub(crate) fn hir_id_to_bound_ty(&self, hir_id: hir::HirId) -> Ty<'tcx> { + /// Lower a type parameter from the HIR to our internal notion of a type. + /// + /// Early-bound type parameters get lowered to [`ty::Param`] + /// and late-bound ones to [`ty::Bound`]. + pub(crate) fn lower_ty_param(&self, hir_id: hir::HirId) -> Ty<'tcx> { let tcx = self.tcx(); match tcx.named_bound_var(hir_id) { Some(rbv::ResolvedArg::LateBound(debruijn, index, def_id)) => { @@ -2077,13 +2140,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - // Converts a hir id corresponding to a const parameter to - // a early-bound `ConstKind::Param` or late-bound `ConstKind::Bound`. - pub(crate) fn hir_id_to_bound_const( - &self, - hir_id: hir::HirId, - param_ty: Ty<'tcx>, - ) -> Const<'tcx> { + /// Lower a const parameter from the HIR to our internal notion of a constant. + /// + /// Early-bound const parameters get lowered to [`ty::ConstKind::Param`] + /// and late-bound ones to [`ty::ConstKind::Bound`]. + pub(crate) fn lower_const_param(&self, hir_id: hir::HirId, param_ty: Ty<'tcx>) -> Const<'tcx> { let tcx = self.tcx(); match tcx.named_bound_var(hir_id) { Some(rbv::ResolvedArg::EarlyBound(def_id)) => { @@ -2103,16 +2164,14 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - /// Parses the programmer's textual representation of a type into our - /// internal notion of a type. - pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { - self.ast_ty_to_ty_inner(ast_ty, false, false) + /// Lower a type from the HIR to our internal notion of a type. + pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + self.lower_ty_common(hir_ty, false, false) } - /// Parses the programmer's textual representation of a type into our - /// internal notion of a type. This is meant to be used within a path. - pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { - self.ast_ty_to_ty_inner(ast_ty, false, true) + /// Lower a type inside of a path from the HIR to our internal notion of a type. + pub fn lower_ty_in_path(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + self.lower_ty_common(hir_ty, false, true) } fn check_delegation_constraints(&self, sig_id: DefId, span: Span, emit: bool) -> bool { @@ -2179,7 +2238,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { error_occured } - fn ty_from_delegation( + fn lower_delegation_ty( &self, sig_id: DefId, idx: hir::InferDelegationKind, @@ -2213,8 +2272,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { sig.instantiate_identity() }; - // Bound vars are also inherited from `sig_id`. They will be - // rebinded later in `ty_of_fn`. + // Bound vars are also inherited from `sig_id`. + // They will be rebound later in `lower_fn_ty`. let sig = sig.skip_binder(); match idx { @@ -2223,66 +2282,72 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait - /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors. + /// Lower a type from the HIR to our internal notion of a type given some extra data for diagnostics. + /// + /// Extra diagnostic data: + /// + /// 1. `borrowed`: Whether trait object types are borrowed like in `&dyn Trait`. + /// Used to avoid emitting redundant errors. + /// 2. `in_path`: Whether the type appears inside of a path. + /// Used to provide correct diagnostics for bare trait object types. #[instrument(level = "debug", skip(self), ret)] - fn ast_ty_to_ty_inner( - &self, - ast_ty: &hir::Ty<'tcx>, - borrowed: bool, - in_path: bool, - ) -> Ty<'tcx> { + fn lower_ty_common(&self, hir_ty: &hir::Ty<'tcx>, borrowed: bool, in_path: bool) -> Ty<'tcx> { let tcx = self.tcx(); - let result_ty = match &ast_ty.kind { + let result_ty = match &hir_ty.kind { hir::TyKind::InferDelegation(sig_id, idx) => { - self.ty_from_delegation(*sig_id, *idx, ast_ty.span) - } - hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.ast_ty_to_ty(ty)), - hir::TyKind::Ptr(mt) => { - Ty::new_ptr(tcx, ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl }) + self.lower_delegation_ty(*sig_id, *idx, hir_ty.span) } + hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.lower_ty(ty)), + hir::TyKind::Ptr(mt) => Ty::new_ptr(tcx, self.lower_ty(mt.ty), mt.mutbl), hir::TyKind::Ref(region, mt) => { - let r = self.ast_region_to_region(region, None); + let r = self.lower_lifetime(region, None); debug!(?r); - let t = self.ast_ty_to_ty_inner(mt.ty, true, false); - Ty::new_ref(tcx, r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl }) + let t = self.lower_ty_common(mt.ty, true, false); + Ty::new_ref(tcx, r, t, mt.mutbl) } hir::TyKind::Never => tcx.types.never, hir::TyKind::Tup(fields) => { - Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.ast_ty_to_ty(t))) + Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.lower_ty(t))) } hir::TyKind::AnonAdt(item_id) => { + let _guard = debug_span!("AnonAdt"); + let did = item_id.owner_id.def_id; let adt_def = tcx.adt_def(did); - let generics = tcx.generics_of(did); - debug!("ast_ty_to_ty_inner(AnonAdt): generics={:?}", generics); let args = ty::GenericArgs::for_item(tcx, did.to_def_id(), |param, _| { tcx.mk_param_from_def(param) }); - debug!("ast_ty_to_ty_inner(AnonAdt): args={:?}", args); + debug!(?args); Ty::new_adt(tcx, adt_def, tcx.mk_args(args)) } hir::TyKind::BareFn(bf) => { - require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span); + require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, hir_ty.span); Ty::new_fn_ptr( tcx, - self.ty_of_fn(ast_ty.hir_id, bf.unsafety, bf.abi, bf.decl, None, Some(ast_ty)), + self.lower_fn_ty( + hir_ty.hir_id, + bf.unsafety, + bf.abi, + bf.decl, + None, + Some(hir_ty), + ), ) } hir::TyKind::TraitObject(bounds, lifetime, repr) => { - self.maybe_lint_bare_trait(ast_ty, in_path); + self.maybe_lint_bare_trait(hir_ty, in_path); let repr = match repr { TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn, TraitObjectSyntax::DynStar => ty::DynStar, }; - self.conv_object_ty_poly_trait_ref( - ast_ty.span, - ast_ty.hir_id, + self.lower_trait_object_ty( + hir_ty.span, + hir_ty.hir_id, bounds, lifetime, borrowed, @@ -2291,8 +2356,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => { debug!(?maybe_qself, ?path); - let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself)); - self.res_to_ty(opt_self_ty, path, ast_ty.hir_id, false) + let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself)); + self.lower_path(opt_self_ty, path, hir_ty.hir_id, false) } &hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => { let opaque_ty = tcx.hir().item(item_id); @@ -2308,21 +2373,21 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } else { local_def_id.to_def_id() }; - self.impl_trait_ty_to_ty(def_id, lifetimes, in_trait) + self.lower_opaque_ty(def_id, lifetimes, in_trait) } ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i), } } hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => { debug!(?qself, ?segment); - let ty = self.ast_ty_to_ty_inner(qself, false, true); - self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, qself, segment, false) + let ty = self.lower_ty_common(qself, false, true); + self.lower_assoc_path(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false) .map(|(ty, _, _)| ty) .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) } &hir::TyKind::Path(hir::QPath::LangItem(lang_item, span)) => { let def_id = tcx.require_lang_item(lang_item, Some(span)); - let (args, _) = self.create_args_for_ast_path( + let (args, _) = self.lower_generic_args_of_path( span, def_id, &[], @@ -2340,7 +2405,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } }; - Ty::new_array_with_const_len(tcx, self.ast_ty_to_ty(ty), length) + Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length) } hir::TyKind::Typeof(e) => tcx.type_of(e.def_id).instantiate_identity(), hir::TyKind::Infer => { @@ -2348,28 +2413,29 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // values in an ExprKind::Closure, or as // the type of local variables. Both of these cases are // handled specially and will not descend into this routine. - self.ty_infer(None, ast_ty.span) + self.ty_infer(None, hir_ty.span) } hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar), }; - self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span); + self.record_ty(hir_ty.hir_id, result_ty, hir_ty.span); result_ty } - #[instrument(level = "debug", skip(self), ret)] - fn impl_trait_ty_to_ty( + /// Lower an opaque type (i.e., an existential impl-Trait type) from the HIR. + #[instrument(level = "debug", skip_all, ret)] + fn lower_opaque_ty( &self, def_id: DefId, lifetimes: &[hir::GenericArg<'_>], in_trait: bool, ) -> Ty<'tcx> { - debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes); + debug!(?def_id, ?lifetimes); let tcx = self.tcx(); let generics = tcx.generics_of(def_id); + debug!(?generics); - debug!("impl_trait_ty_to_ty: generics={:?}", generics); let args = ty::GenericArgs::for_item(tcx, def_id, |param, _| { // We use `generics.count() - lifetimes.len()` here instead of `generics.parent_count` // since return-position impl trait in trait squashes all of the generics from its source fn @@ -2390,12 +2456,12 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { &lifetimes[i] ) }; - self.ast_region_to_region(lifetime, None).into() + self.lower_lifetime(lifetime, None).into() } else { tcx.mk_param_from_def(param) } }); - debug!("impl_trait_ty_to_ty: args={:?}", args); + debug!(?args); if in_trait { Ty::new_projection(tcx, def_id, args) @@ -2404,18 +2470,19 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } } - pub fn ty_of_arg(&self, ty: &hir::Ty<'tcx>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> { + pub fn lower_arg_ty(&self, ty: &hir::Ty<'tcx>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> { match ty.kind { hir::TyKind::Infer if let Some(expected_ty) = expected_ty => { self.record_ty(ty.hir_id, expected_ty, ty.span); expected_ty } - _ => self.ast_ty_to_ty(ty), + _ => self.lower_ty(ty), } } + /// Lower a function type from the HIR to our internal notion of a function signature. #[instrument(level = "debug", skip(self, hir_id, unsafety, abi, decl, generics, hir_ty), ret)] - pub fn ty_of_fn( + pub fn lower_fn_ty( &self, hir_id: hir::HirId, unsafety: hir::Unsafety, @@ -2448,7 +2515,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { .enumerate() .map(|(i, a)| { if let hir::TyKind::Infer = a.kind - && !self.allow_ty_infer() + && !self.allow_infer() { if let Some(suggested_ty) = self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) @@ -2464,14 +2531,14 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // Only visit the type looking for `_` if we didn't fix the type above visitor.visit_ty(a); - self.ty_of_arg(a, None) + self.lower_arg_ty(a, None) }) .collect(); let output_ty = match decl.output { hir::FnRetTy::Return(output) => { if let hir::TyKind::Infer = output.kind - && !self.allow_ty_infer() + && !self.allow_infer() && let Some(suggested_ty) = self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None) { @@ -2479,7 +2546,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { Ty::new_error_with_message(self.tcx(), output.span, suggested_ty.to_string()) } else { visitor.visit_ty(output); - self.ast_ty_to_ty(output) + self.lower_ty(output) } } hir::FnRetTy::DefaultReturn(..) => Ty::new_unit(tcx), @@ -2490,10 +2557,10 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi); let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); - if !self.allow_ty_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) { + if !self.allow_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) { // We always collect the spans for placeholder types when evaluating `fn`s, but we // only want to emit an error complaining about them if infer types (`_`) are not - // allowed. `allow_ty_infer` gates this behavior. We check for the presence of + // allowed. `allow_infer` gates this behavior. We check for the presence of // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`. let mut diag = crate::collect::placeholder_type_error_diag( @@ -2562,8 +2629,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { }; let i = tcx.parent_hir_node(fn_hir_id).expect_item().expect_impl(); - let trait_ref = - self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty)); + let trait_ref = self.lower_impl_trait_ref(i.of_trait.as_ref()?, self.lower_ty(i.self_ty)); let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( tcx, @@ -2620,7 +2686,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } /// Given the bounds on an object, determines what single region bound (if any) we can - /// use to summarize this type. The basic idea is that we will use the bound the user + /// use to summarize this type. + /// + /// The basic idea is that we will use the bound the user /// provided, if they provided one, and otherwise search the supertypes of trait bounds /// for region bounds. It may be that we can derive no bound at all, in which case /// we return `None`. diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs index 70c60e39941..b5b3a9131c5 100644 --- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs @@ -1,6 +1,6 @@ -use crate::astconv::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds}; use crate::bounds::Bounds; use crate::errors::TraitObjectDeclaredWithNoTraits; +use crate::hir_ty_lowering::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::{codes::*, struct_span_code_err}; use rustc_hir as hir; @@ -11,14 +11,16 @@ use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{DynKind, ToPredicate}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::report_object_safety_error; -use rustc_trait_selection::traits::{self, astconv_object_safety_violations}; +use rustc_trait_selection::traits::{self, hir_ty_lowering_object_safety_violations}; use smallvec::{smallvec, SmallVec}; -use super::AstConv; +use super::HirTyLowerer; -impl<'tcx> dyn AstConv<'tcx> + '_ { - pub(super) fn conv_object_ty_poly_trait_ref( +impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { + /// Lower a trait object type from the HIR to our internal notion of a type. + #[instrument(level = "debug", skip_all, ret)] + pub(super) fn lower_trait_object_ty( &self, span: Span, hir_id: hir::HirId, @@ -37,11 +39,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { correct: Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), .. - } = self.instantiate_poly_trait_ref( + } = self.lower_poly_trait_ref( &trait_bound.trait_ref, trait_bound.span, ty::BoundConstness::NotConst, - ty::ImplPolarity::Positive, + ty::PredicatePolarity::Positive, dummy_self, &mut bounds, // True so we don't populate `bounds` with associated type bounds, even @@ -58,7 +60,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { let bound_pred = pred.kind(); match bound_pred.skip_binder() { ty::ClauseKind::Trait(trait_pred) => { - assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive); + assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); trait_bounds.push((bound_pred.rebind(trait_pred.trait_ref), span)); } ty::ClauseKind::Projection(proj) => { @@ -133,7 +135,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // to avoid ICEs. for item in ®ular_traits { let object_safety_violations = - astconv_object_safety_violations(tcx, item.trait_ref().def_id()); + hir_ty_lowering_object_safety_violations(tcx, item.trait_ref().def_id()); if !object_safety_violations.is_empty() { let reported = report_object_safety_error( tcx, @@ -156,7 +158,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { 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]).filter_only_self() { - debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred); + debug!("observing object predicate `{pred:?}`"); let bound_predicate = pred.kind(); match bound_predicate.skip_binder() { @@ -231,7 +233,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id)); } - self.complain_about_missing_associated_types( + self.complain_about_missing_assoc_tys( associated_types, potential_assoc_types, hir_trait_bounds, @@ -243,8 +245,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // the bounds let mut duplicates = FxHashSet::default(); auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id())); - debug!("regular_traits: {:?}", regular_traits); - debug!("auto_traits: {:?}", auto_traits); + debug!(?regular_traits); + debug!(?auto_traits); // Erase the `dummy_self` (`trait_object_dummy_self`) used above. let existential_trait_refs = regular_traits.iter().map(|i| { @@ -362,11 +364,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // Use explicitly-specified region bound. let region_bound = if !lifetime.is_elided() { - self.ast_region_to_region(lifetime, None) + self.lower_lifetime(lifetime, None) } else { self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| { if tcx.named_bound_var(lifetime.hir_id).is_some() { - self.ast_region_to_region(lifetime, None) + self.lower_lifetime(lifetime, None) } else { self.re_infer(None, span).unwrap_or_else(|| { let err = struct_span_code_err!( @@ -389,10 +391,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { } }) }; - debug!("region_bound: {:?}", region_bound); + debug!(?region_bound); - let ty = Ty::new_dynamic(tcx, existential_predicates, region_bound, representation); - debug!("trait_object_type: {:?}", ty); - ty + Ty::new_dynamic(tcx, existential_predicates, region_bound, representation) } } diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 2a9101b3808..7014b23ff07 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -68,7 +68,7 @@ fn diagnostic_hir_wf_check<'tcx>( let infcx = self.tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new(&infcx); - let tcx_ty = self.icx.to_ty(ty); + let tcx_ty = self.icx.lower_ty(ty); // This visitor can walk into binders, resulting in the `tcx_ty` to // potentially reference escaping bound variables. We simply erase // those here. @@ -163,6 +163,16 @@ fn diagnostic_hir_wf_check<'tcx>( kind: hir::GenericParamKind::Type { default: Some(ty), .. }, .. }) => vec![*ty], + hir::Node::AnonConst(_) + if let Some(const_param_id) = + tcx.hir().opt_const_param_default_param_def_id(hir_id) + && let hir::Node::GenericParam(hir::GenericParam { + kind: hir::GenericParamKind::Const { ty, .. }, + .. + }) = tcx.hir_node_by_def_id(const_param_id) => + { + vec![*ty] + } ref node => bug!("Unexpected node {:?}", node), }, WellFormedLoc::Param { function: _, param_idx } => { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 5fe8d1fe586..3ec5894700f 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -30,8 +30,8 @@ several major phases: The type checker is defined into various submodules which are documented independently: -- astconv: converts the AST representation of types - into the `ty` representation. +- hir_ty_lowering: lowers type-system entities from the [HIR][hir] to the + [`rustc_middle::ty`] representation. - collect: computes the types of each top-level item and enters them into the `tcx.types` table for later use. @@ -82,11 +82,11 @@ extern crate rustc_middle; // These are used by Clippy. pub mod check; -pub mod astconv; pub mod autoderef; mod bounds; mod check_unused; mod coherence; +pub mod hir_ty_lowering; // FIXME: This module shouldn't be public. pub mod collect; mod constrained_generic_params; @@ -211,12 +211,20 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { Ok(()) } -/// A quasi-deprecated helper used in rustdoc and clippy to get -/// the type from a HIR node. -pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { +/// Lower a [`hir::Ty`] to a [`Ty`]. +/// +/// <div class="warning"> +/// +/// This function is **quasi-deprecated**. It can cause ICEs if called inside of a body +/// (of a function or constant) and especially if it contains inferred types (`_`). +/// +/// It's used in rustdoc and Clippy. +/// +/// </div> +pub fn lower_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { // In case there are any projections, etc., find the "environment" // def-ID that will be used to determine the traits/predicates in // scope. This is derived from the enclosing item-like thing. let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id); - collect::ItemCtxt::new(tcx, env_def_id.def_id).to_ty(hir_ty) + collect::ItemCtxt::new(tcx, env_def_id.def_id).lower_ty(hir_ty) } diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 0f5ba26337a..dcab571eedf 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -435,6 +435,22 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { &self, num_params_to_take: usize, ) -> String { + let is_in_a_method_call = self + .tcx + .hir() + .parent_iter(self.path_segment.hir_id) + .skip(1) + .find_map(|(_, node)| match node { + hir::Node::Expr(expr) => Some(expr), + _ => None, + }) + .is_some_and(|expr| { + matches!( + expr.kind, + hir::ExprKind::MethodCall(hir::PathSegment { args: Some(_), .. }, ..) + ) + }); + let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig); let is_used_in_input = |def_id| { fn_sig.is_some_and(|fn_sig| { @@ -453,14 +469,17 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { .skip(self.params_offset + self.num_provided_type_or_const_args()) .take(num_params_to_take) .map(|param| match param.kind { - // This is being inferred from the item's inputs, no need to set it. - ty::GenericParamDefKind::Type { .. } if is_used_in_input(param.def_id) => { - "_".to_string() + // If it's in method call (turbofish), it might be inferred from the expression (e.g. `.collect::<Vec<_>>()`) + // If it is being inferred from the item's inputs, no need to set it. + ty::GenericParamDefKind::Type { .. } + if is_in_a_method_call || is_used_in_input(param.def_id) => + { + "_" } - _ => param.name.to_string(), + _ => param.name.as_str(), }) - .collect::<Vec<_>>() - .join(", ") + .intersperse(", ") + .collect() } fn get_unbound_associated_types(&self) -> Vec<String> { diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 580cdb4a3a2..28c86d8019e 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -1,6 +1,6 @@ //! Constraint construction and representation //! -//! The second pass over the AST determines the set of constraints. +//! The second pass over the HIR determines the set of constraints. //! We walk the set of items and, for each member, generate new constraints. use hir::def_id::{DefId, LocalDefId}; @@ -253,8 +253,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_ty(current, typ, variance); } - ty::RawPtr(ref mt) => { - self.add_constraints_from_mt(current, mt, variance); + ty::RawPtr(ty, mutbl) => { + self.add_constraints_from_mt(current, &ty::TypeAndMut { ty, mutbl }, variance); } ty::Tuple(subtys) => { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 34c24583947..36f59b4ac2e 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -113,7 +113,7 @@ impl<'a> State<'a> { // `hir_map` to reconstruct their full structure for pretty // printing. Node::Ctor(..) => panic!("cannot print isolated Ctor"), - Node::Local(a) => self.print_local_decl(a), + Node::LetStmt(a) => self.print_local_decl(a), Node::Crate(..) => panic!("cannot print Crate"), Node::WhereBoundPredicate(pred) => { self.print_formal_generic_params(pred.bound_generic_params); @@ -1387,7 +1387,7 @@ impl<'a> State<'a> { // Print `}`: self.bclose_maybe_open(expr.span, true); } - hir::ExprKind::Let(&hir::Let { pat, ty, init, .. }) => { + hir::ExprKind::Let(&hir::LetExpr { pat, ty, init, .. }) => { self.print_let(pat, ty, init); } hir::ExprKind::If(test, blk, elseopt) => { @@ -1544,7 +1544,7 @@ impl<'a> State<'a> { self.end() } - fn print_local_decl(&mut self, loc: &hir::Local<'_>) { + fn print_local_decl(&mut self, loc: &hir::LetStmt<'_>) { self.print_pat(loc.pat); if let Some(ty) = loc.ty { self.word_space(":"); @@ -1808,6 +1808,12 @@ impl<'a> State<'a> { self.pclose(); } } + PatKind::Deref(inner) => { + self.word("deref!"); + self.popen(); + self.print_pat(inner); + self.pclose(); + } PatKind::Ref(inner, mutbl) => { let is_range_inner = matches!(inner.kind, PatKind::Range(..)); self.word("&"); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 220da19a29d..0ca958302f7 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -103,6 +103,8 @@ hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for { *[other] {" "}in the current scope } +hir_typeck_note_caller_chooses_ty_for_ty_param = the caller chooses a type for `{$ty_param_name}` which can be different from `{$found_ty}` + hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expected_ty}` to `{$expr_ty}` diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 4b3359858f1..9b7db9e4c8e 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -317,7 +317,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.note("`if` expressions without `else` evaluate to `()`"); err.help("consider adding an `else` block that evaluates to the expected type"); *error = true; - if let ExprKind::Let(hir::Let { span, pat, init, .. }) = cond_expr.kind + if let ExprKind::Let(hir::LetExpr { span, pat, init, .. }) = cond_expr.kind && let ExprKind::Block(block, _) = then_expr.kind // Refutability checks occur on the MIR, so we approximate it here by checking // if we have an enum with a single variant or a struct in the pattern. @@ -408,7 +408,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if let hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) = node { + if let hir::Node::LetStmt(hir::LetStmt { ty: Some(_), pat, .. }) = node { return Some((pat.span, "expected because of this assignment".to_string())); } None @@ -645,7 +645,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for ty in [first_ty, second_ty] { for (clause, _) in self .tcx - .explicit_item_bounds(rpit_def_id) + .explicit_item_super_predicates(rpit_def_id) .iter_instantiated_copied(self.tcx, args) { let pred = clause.kind().rebind(match clause.kind().skip_binder() { diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index b8d1eaee812..c948b6343b7 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -128,7 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Float(_) | ty::Array(..) | ty::CoroutineWitness(..) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(..) @@ -332,13 +332,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { let mut sugg = None; let mut sugg_mutref = false; if let ty::Ref(reg, cast_ty, mutbl) = *self.cast_ty.kind() { - if let ty::RawPtr(TypeAndMut { ty: expr_ty, .. }) = *self.expr_ty.kind() + if let ty::RawPtr(expr_ty, _) = *self.expr_ty.kind() && fcx.can_coerce( - Ty::new_ref( - fcx.tcx, - fcx.tcx.lifetimes.re_erased, - TypeAndMut { ty: expr_ty, mutbl }, - ), + Ty::new_ref(fcx.tcx, fcx.tcx.lifetimes.re_erased, expr_ty, mutbl), self.cast_ty, ) { @@ -346,14 +342,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else if let ty::Ref(expr_reg, expr_ty, expr_mutbl) = *self.expr_ty.kind() && expr_mutbl == Mutability::Not && mutbl == Mutability::Mut - && fcx.can_coerce( - Ty::new_ref( - fcx.tcx, - expr_reg, - TypeAndMut { ty: expr_ty, mutbl: Mutability::Mut }, - ), - self.cast_ty, - ) + && fcx.can_coerce(Ty::new_mut_ref(fcx.tcx, expr_reg, expr_ty), self.cast_ty) { sugg_mutref = true; } @@ -361,19 +350,15 @@ impl<'a, 'tcx> CastCheck<'tcx> { if !sugg_mutref && sugg == None && fcx.can_coerce( - Ty::new_ref(fcx.tcx, reg, TypeAndMut { ty: self.expr_ty, mutbl }), + Ty::new_ref(fcx.tcx, reg, self.expr_ty, mutbl), self.cast_ty, ) { sugg = Some((format!("&{}", mutbl.prefix_str()), false)); } - } else if let ty::RawPtr(TypeAndMut { mutbl, .. }) = *self.cast_ty.kind() + } else if let ty::RawPtr(_, mutbl) = *self.cast_ty.kind() && fcx.can_coerce( - Ty::new_ref( - fcx.tcx, - fcx.tcx.lifetimes.re_erased, - TypeAndMut { ty: self.expr_ty, mutbl }, - ), + Ty::new_ref(fcx.tcx, fcx.tcx.lifetimes.re_erased, self.expr_ty, mutbl), self.cast_ty, ) { @@ -868,7 +853,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // from a region pointer to a vector. // Coerce to a raw pointer so that we generate AddressOf in MIR. - let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr); + let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr.ty, m_expr.mutbl); fcx.coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None) .unwrap_or_else(|_| { bug!( diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 6e30cb02245..40555bf14eb 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -5,7 +5,7 @@ use super::{check_fn, CoroutineTypes, Expectation, FnCtxt}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; -use rustc_hir_analysis::astconv::AstConv; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes}; use rustc_infer::infer::{InferOk, InferResult}; @@ -329,7 +329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty, closure_kind, self.tcx - .explicit_item_bounds(def_id) + .explicit_item_super_predicates(def_id) .iter_instantiated_copied(self.tcx, args) .map(|(c, s)| (c.as_predicate(), s)), ), @@ -784,7 +784,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl: &hir::FnDecl<'tcx>, closure_kind: hir::ClosureKind, ) -> ty::PolyFnSig<'tcx> { - let astconv = self.astconv(); + let lowerer = self.lowerer(); trace!("decl = {:#?}", decl); debug!(?closure_kind); @@ -793,9 +793,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let bound_vars = self.tcx.late_bound_vars(hir_id); // First, convert the types that the user supplied (if any). - let supplied_arguments = decl.inputs.iter().map(|a| astconv.ast_ty_to_ty(a)); + let supplied_arguments = decl.inputs.iter().map(|a| lowerer.lower_ty(a)); let supplied_return = match decl.output { - hir::FnRetTy::Return(ref output) => astconv.ast_ty_to_ty(output), + hir::FnRetTy::Return(ref output) => lowerer.lower_ty(output), hir::FnRetTy::DefaultReturn(_) => match closure_kind { // In the case of the async block that we create for a function body, // we expect the return type of the block to match that of the enclosing @@ -813,7 +813,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // easily (and locally) prove that we // *have* reported an // error. --nikomatsakis - astconv.ty_infer(None, decl.output.span()) + lowerer.ty_infer(None, decl.output.span()) }) } // All `gen {}` and `async gen {}` must return unit. @@ -832,7 +832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_)) | hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { - astconv.ty_infer(None, decl.output.span()) + lowerer.ty_infer(None, decl.output.span()) } }, }; @@ -906,7 +906,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self .tcx - .explicit_item_bounds(def_id) + .explicit_item_super_predicates(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, ty::Error(_) => return Some(ret_ty), @@ -989,17 +989,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { decl: &hir::FnDecl<'tcx>, guar: ErrorGuaranteed, ) -> ty::PolyFnSig<'tcx> { - let astconv = self.astconv(); + let lowerer = self.lowerer(); let err_ty = Ty::new_error(self.tcx, guar); let supplied_arguments = decl.inputs.iter().map(|a| { // Convert the types that the user supplied (if any), but ignore them. - astconv.ast_ty_to_ty(a); + lowerer.lower_ty(a); err_ty }); if let hir::FnRetTy::Return(ref output) = decl.output { - astconv.ast_ty_to_ty(output); + lowerer.lower_ty(output); } let result = ty::Binder::dummy(self.tcx.mk_fn_sig( diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 792359c9dda..3328177634b 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -41,7 +41,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::Expr; -use rustc_hir_analysis::astconv::AstConv; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; use rustc_infer::traits::TraitEngine; @@ -55,7 +55,7 @@ use rustc_middle::ty::adjustment::{ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeAndMut}; +use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::DesugaringKind; @@ -222,8 +222,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Examine the supertype and consider auto-borrowing. match *b.kind() { - ty::RawPtr(mt_b) => { - return self.coerce_unsafe_ptr(a, b, mt_b.mutbl); + ty::RawPtr(_, b_mutbl) => { + return self.coerce_unsafe_ptr(a, b, b_mutbl); } ty::Ref(r_b, _, mutbl_b) => { return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); @@ -440,10 +440,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let derefd_ty_a = Ty::new_ref( self.tcx, r, - TypeAndMut { - ty: referent_ty, - mutbl: mutbl_b, // [1] above - }, + referent_ty, + mutbl_b, // [1] above ); match self.unify(derefd_ty_a, b) { Ok(ok) => { @@ -558,22 +556,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)), - target: Ty::new_ref( - self.tcx, - r_borrow, - ty::TypeAndMut { mutbl: mutbl_b, ty: ty_a }, - ), + target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), }, )) } - (&ty::Ref(_, ty_a, mt_a), &ty::RawPtr(ty::TypeAndMut { mutbl: mt_b, .. })) => { + (&ty::Ref(_, ty_a, mt_a), &ty::RawPtr(_, mt_b)) => { coerce_mutbls(mt_a, mt_b)?; Some(( Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), - target: Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: mt_b, ty: ty_a }), + target: Ty::new_ptr(self.tcx, ty_a, mt_b), }, )) } @@ -984,13 +978,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let (is_ref, mt_a) = match *a.kind() { ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }), - ty::RawPtr(mt) => (false, mt), + ty::RawPtr(ty, mutbl) => (false, ty::TypeAndMut { ty, mutbl }), _ => return self.unify_and(a, b, identity), }; coerce_mutbls(mt_a.mutbl, mutbl_b)?; // Check that the types which they point at are compatible. - let a_unsafe = Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty }); + let a_unsafe = Ty::new_ptr(self.tcx, mt_a.ty, mutbl_b); // Although references and unsafe ptrs have the same // representation, we still register an Adjust::DerefRef so that // regionck knows that the region for `a` must be valid here. diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 67ff412651c..564de4ab9e7 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -299,8 +299,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; }; let (init_ty_hir_id, init) = match self.tcx.parent_hir_node(pat.hir_id) { - hir::Node::Local(hir::Local { ty: Some(ty), init, .. }) => (ty.hir_id, *init), - hir::Node::Local(hir::Local { init: Some(init), .. }) => (init.hir_id, Some(*init)), + hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), init, .. }) => (ty.hir_id, *init), + hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => (init.hir_id, Some(*init)), _ => return false, }; let Some(init_ty) = self.node_ty_opt(init_ty_hir_id) else { @@ -678,7 +678,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: Option<TypeError<'tcx>>, ) { match (self.tcx.parent_hir_node(expr.hir_id), error) { - (hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. }), _) + (hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), init: Some(init), .. }), _) if init.hir_id == expr.hir_id => { // Point at `let` assignment type. @@ -724,11 +724,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { primary_span = pat.span; secondary_span = pat.span; match self.tcx.parent_hir_node(pat.hir_id) { - hir::Node::Local(hir::Local { ty: Some(ty), .. }) => { + hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) => { primary_span = ty.span; post_message = " type"; } - hir::Node::Local(hir::Local { init: Some(init), .. }) => { + hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => { primary_span = init.span; post_message = " value"; } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index df21b84f92e..eee4ac5ad23 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::Ty; use rustc_span::{ edition::{Edition, LATEST_STABLE_EDITION}, symbol::Ident, - Span, + Span, Symbol, }; #[derive(Diagnostic)] @@ -614,3 +614,10 @@ pub struct SuggestConvertViaMethod<'tcx> { pub expected: Ty<'tcx>, pub found: Ty<'tcx>, } + +#[derive(Subdiagnostic)] +#[note(hir_typeck_note_caller_chooses_ty_for_ty_param)] +pub struct NoteCallerChoosesTyForTyParam<'tcx> { + pub ty_param_name: Symbol, + pub found_ty: Ty<'tcx>, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 1a142f27809..f38f04fce43 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -35,8 +35,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath}; -use rustc_hir_analysis::astconv::AstConv as _; use rustc_hir_analysis::check::ty_kind_suggestion; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::DefineOpaqueTypes; @@ -333,7 +333,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr), ExprKind::Type(e, t) => { - let ascribed_ty = self.to_ty_saving_user_provided_ty(t); + let ascribed_ty = self.lower_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 @@ -426,7 +426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { match ty.kind() { - ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { + ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => { if oprnd.is_syntactic_place_expr() { // Places may legitimately have unsized types. // For example, dereferences of a fat pointer and @@ -442,12 +442,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.check_expr_with_expectation_and_needs(oprnd, hint, Needs::maybe_mut_place(mutbl)); - let tm = ty::TypeAndMut { ty, mutbl }; match kind { - _ if tm.ty.references_error() => Ty::new_misc_error(self.tcx), + _ if ty.references_error() => Ty::new_misc_error(self.tcx), hir::BorrowKind::Raw => { self.check_named_place_expr(oprnd); - Ty::new_ptr(self.tcx, tm) + Ty::new_ptr(self.tcx, ty, mutbl) } hir::BorrowKind::Ref => { // Note: at this point, we cannot say what the best lifetime @@ -465,7 +464,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // whose address was taken can actually be made to live as long // as it needs to live. let region = self.next_region_var(infer::AddrOfRegion(expr.span)); - Ty::new_ref(self.tcx, region, tm) + Ty::new_ref(self.tcx, region, ty, mutbl) } } } @@ -1261,7 +1260,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(super) fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>, hir_id: HirId) -> Ty<'tcx> { + pub(super) fn check_expr_let( + &self, + let_expr: &'tcx hir::LetExpr<'tcx>, + hir_id: HirId, + ) -> Ty<'tcx> { // for let statements, this is done in check_stmt let init = let_expr.init; self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression"); @@ -1371,7 +1374,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { // Find the type of `e`. Supply hints based on the type we are casting to, // if appropriate. - let t_cast = self.to_ty_saving_user_provided_ty(t); + let t_cast = self.lower_ty_saving_user_provided_ty(t); let t_cast = self.resolve_vars_if_possible(t_cast); let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast)); let t_expr = self.resolve_vars_if_possible(t_expr); @@ -1448,7 +1451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let Some(( _, - hir::Node::Local(hir::Local { ty: Some(ty), .. }) + hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }), )) = parent_node else { @@ -1499,7 +1502,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; - let count = self.array_length_to_const(count); + let count = self.lower_array_length(count); if let Some(count) = count.try_eval_target_usize(tcx, self.param_env) { self.suggest_array_len(expr, count); } @@ -1675,7 +1678,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, span: Span, variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::ExprField<'tcx>], + hir_fields: &'tcx [hir::ExprField<'tcx>], base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, ) { let tcx = self.tcx; @@ -1706,7 +1709,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut error_happened = false; // Type-check each field. - for (idx, field) in ast_fields.iter().enumerate() { + for (idx, field) in hir_fields.iter().enumerate() { let ident = tcx.adjust_ident(field.ident, variant.def_id); let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { seen_fields.insert(ident, field.span); @@ -1735,7 +1738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { variant, expr, field, - ast_fields, + hir_fields, adt.variant_descr(), ) }; @@ -1750,7 +1753,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No); if let Some(diag) = diag { - if idx == ast_fields.len() - 1 { + if idx == hir_fields.len() - 1 { if remaining_fields.is_empty() { self.suggest_fru_from_range_and_emit(field, variant, args, diag); } else { @@ -1764,7 +1767,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Make sure the programmer specified correct number of fields. if adt_kind == AdtKind::Union { - if ast_fields.len() != 1 { + if hir_fields.len() != 1 { struct_span_code_err!( tcx.dcx(), span, @@ -1901,14 +1904,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(); if !private_fields.is_empty() { - self.report_private_fields(adt_ty, span, expr.span, private_fields, ast_fields); + self.report_private_fields(adt_ty, span, expr.span, private_fields, hir_fields); } else { self.report_missing_fields( adt_ty, span, remaining_fields, variant, - ast_fields, + hir_fields, args, ); } @@ -1945,7 +1948,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, remaining_fields: UnordMap<Ident, (FieldIdx, &ty::FieldDef)>, variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::ExprField<'tcx>], + hir_fields: &'tcx [hir::ExprField<'tcx>], args: GenericArgsRef<'tcx>, ) { let len = remaining_fields.len(); @@ -1982,8 +1985,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); - if let Some(last) = ast_fields.last() { - self.suggest_fru_from_range_and_emit(last, variant, args, err); + if let Some(hir_field) = hir_fields.last() { + self.suggest_fru_from_range_and_emit(hir_field, variant, args, err); } else { err.emit(); } @@ -2682,8 +2685,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr, Some(span), ); - } else if let ty::RawPtr(ty_and_mut) = expr_t.kind() - && let ty::Adt(adt_def, _) = ty_and_mut.ty.kind() + } else if let ty::RawPtr(ptr_ty, _) = expr_t.kind() + && let ty::Adt(adt_def, _) = ptr_ty.kind() && let ExprKind::Field(base_expr, _) = expr.kind && adt_def.variants().len() == 1 && adt_def @@ -3096,7 +3099,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { cause.clone().derived_cause( ty::Binder::dummy(ty::TraitPredicate { trait_ref: impl_trait_ref, - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }), |derived| { traits::ImplDerivedObligation(Box::new( @@ -3221,7 +3224,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No); } ty::Ref(_, base_ty, mutbl) => { - let ptr_ty = Ty::new_ptr(self.tcx, ty::TypeAndMut { ty: base_ty, mutbl }); + let ptr_ty = Ty::new_ptr(self.tcx, base_ty, mutbl); self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No); } _ => {} @@ -3279,7 +3282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fields: &[Ident], expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let container = self.to_ty(container).normalized; + let container = self.lower_ty(container).normalized; if let Some(ident_2) = fields.get(1) && !self.tcx.features().offset_of_nested diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 43e95544594..3b6accb92ae 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -245,7 +245,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } } - hir::ExprKind::Let(hir::Let { pat, init, .. }) => { + hir::ExprKind::Let(hir::LetExpr { pat, init, .. }) => { self.walk_local(init, pat, None, |t| t.borrow_expr(init, ty::ImmBorrow)) } @@ -371,7 +371,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) { match stmt.kind { - hir::StmtKind::Let(hir::Local { pat, init: Some(expr), els, .. }) => { + hir::StmtKind::Let(hir::LetStmt { pat, init: Some(expr), els, .. }) => { self.walk_local(expr, pat, *els, |_| {}) } @@ -463,6 +463,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { } PatKind::Or(_) | PatKind::Box(_) + | PatKind::Deref(_) | PatKind::Ref(..) | PatKind::Wild | PatKind::Err(_) => { diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index c16e941d4c5..140618e97cc 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -4,12 +4,11 @@ use rustc_data_structures::{ graph::{iterate::DepthFirstSearch, vec_graph::VecGraph}, unord::{UnordBag, UnordMap, UnordSet}, }; -use rustc_hir::def_id::CRATE_DEF_ID; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; -enum DivergingFallbackBehavior { +#[derive(Copy, Clone)] +pub enum DivergingFallbackBehavior { /// Always fallback to `()` (aka "always spontaneous decay") FallbackToUnit, /// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken. @@ -78,9 +77,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { return false; } - let diverging_behavior = self.diverging_fallback_behavior(); - let diverging_fallback = - self.calculate_diverging_fallback(&unresolved_variables, diverging_behavior); + let diverging_fallback = self + .calculate_diverging_fallback(&unresolved_variables, self.diverging_fallback_behavior); // We do fallback in two passes, to try to generate // better error messages. @@ -94,32 +92,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { fallback_occurred } - fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior { - let Some((mode, span)) = self - .tcx - .get_attr(CRATE_DEF_ID, sym::rustc_never_type_mode) - .map(|attr| (attr.value_str().unwrap(), attr.span)) - else { - if self.tcx.features().never_type_fallback { - return DivergingFallbackBehavior::FallbackToNiko; - } - - return DivergingFallbackBehavior::FallbackToUnit; - }; - - match mode { - sym::fallback_to_unit => DivergingFallbackBehavior::FallbackToUnit, - sym::fallback_to_niko => DivergingFallbackBehavior::FallbackToNiko, - sym::fallback_to_never => DivergingFallbackBehavior::FallbackToNever, - sym::no_fallback => DivergingFallbackBehavior::NoFallback, - _ => { - self.tcx.dcx().span_err(span, format!("unknown never type mode: `{mode}` (supported: `fallback_to_unit`, `fallback_to_niko`, `fallback_to_never` and `no_fallback`)")); - - DivergingFallbackBehavior::FallbackToUnit - } - } - } - fn fallback_effects(&self) -> bool { let unsolved_effects = self.unsolved_effects(); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 1d885b801d9..8e0be7c7163 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -11,12 +11,12 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, GenericArg, Node, QPath}; -use rustc_hir_analysis::astconv::generics::{ - check_generic_arg_count_for_call, create_args_for_parent_generic_args, +use rustc_hir_analysis::hir_ty_lowering::generics::{ + check_generic_arg_count_for_call, lower_generic_args, }; -use rustc_hir_analysis::astconv::{ - AstConv, CreateInstantiationsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, - GenericArgCountResult, IsMethodCall, PathSeg, +use rustc_hir_analysis::hir_ty_lowering::{ + ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgsLowerer, + GenericPathSegment, HirTyLowerer, IsMethodCall, }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; @@ -394,20 +394,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn to_ty(&self, ast_t: &hir::Ty<'tcx>) -> LoweredTy<'tcx> { - let t = self.astconv().ast_ty_to_ty(ast_t); - self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None)); - LoweredTy::from_raw(self, ast_t.span, t) + pub fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> LoweredTy<'tcx> { + let ty = self.lowerer().lower_ty(hir_ty); + self.register_wf_obligation(ty.into(), hir_ty.span, traits::WellFormed(None)); + LoweredTy::from_raw(self, hir_ty.span, ty) } - pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { - let ty = self.to_ty(ast_ty); - debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); + #[instrument(level = "debug", skip_all)] + pub fn lower_ty_saving_user_provided_ty(&self, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { + let ty = self.lower_ty(hir_ty); + debug!(?ty); if Self::can_contain_user_lifetime_bounds(ty.raw) { let c_ty = self.canonicalize_response(UserType::Ty(ty.raw)); - debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); - self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); + debug!(?c_ty); + self.typeck_results.borrow_mut().user_provided_types_mut().insert(hir_ty.hir_id, c_ty); } ty.normalized @@ -424,7 +425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> { + pub fn lower_array_length(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> { match length { hir::ArrayLen::Infer(inf) => self.ct_infer(self.tcx.types.usize, None, inf.span), hir::ArrayLen::Body(anon_const) => { @@ -436,20 +437,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn const_arg_to_const( - &self, - ast_c: &hir::AnonConst, - param_def_id: DefId, - ) -> ty::Const<'tcx> { - let did = ast_c.def_id; + pub fn lower_const_arg(&self, hir_ct: &hir::AnonConst, param_def_id: DefId) -> ty::Const<'tcx> { + let did = hir_ct.def_id; self.tcx.feed_anon_const_type(did, self.tcx.type_of(param_def_id)); - let c = ty::Const::from_anon_const(self.tcx, did); + let ct = ty::Const::from_anon_const(self.tcx, did); self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), + ct.into(), + self.tcx.hir().span(hir_ct.hir_id), ObligationCauseCode::WellFormed(None), ); - c + ct } // If the type given by the user has free regions, save it for later, since @@ -827,12 +824,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { QPath::Resolved(ref opt_qself, path) => { return ( path.res, - opt_qself.as_ref().map(|qself| self.to_ty(qself)), + opt_qself.as_ref().map(|qself| self.lower_ty(qself)), path.segments, ); } QPath::TypeRelative(ref qself, ref segment) => { - // Don't use `self.to_ty`, since this will register a WF obligation. + // Don't use `self.lower_ty`, since this will register a WF obligation. // If we're trying to call a nonexistent method on a trait // (e.g. `MyTrait::missing_method`), then resolution will // give us a `QPath::TypeRelative` with a trait object as @@ -841,7 +838,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // to be object-safe. // We manually call `register_wf_obligation` in the success path // below. - let ty = self.astconv().ast_ty_to_ty_in_path(qself); + let ty = self.lowerer().lower_ty_in_path(qself); (LoweredTy::from_raw(self, span, ty), qself, segment) } QPath::LangItem(..) => { @@ -1119,9 +1116,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> (Ty<'tcx>, Res) { let tcx = self.tcx; - let path_segs = match res { + let generic_segments = match res { Res::Local(_) | Res::SelfCtor(_) => vec![], - Res::Def(kind, def_id) => self.astconv().def_ids_for_value_path_segments( + Res::Def(kind, def_id) => self.lowerer().probe_generic_path_segments( segments, self_ty.map(|ty| ty.raw), kind, @@ -1178,14 +1175,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // provided (if any) into their appropriate spaces. We'll also report // errors if type parameters are provided in an inappropriate place. - let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); - let generics_has_err = self.astconv().prohibit_generics( + let indices: FxHashSet<_> = + generic_segments.iter().map(|GenericPathSegment(_, index)| index).collect(); + let generics_has_err = self.lowerer().prohibit_generic_args( segments.iter().enumerate().filter_map(|(index, seg)| { - if !generic_segs.contains(&index) || is_alias_variant_ctor { - Some(seg) - } else { - None - } + if !indices.contains(&index) || is_alias_variant_ctor { Some(seg) } else { None } }), |_| {}, ); @@ -1212,7 +1206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut infer_args_for_err = FxHashSet::default(); let mut explicit_late_bound = ExplicitLateBound::No; - for &PathSeg(def_id, index) in &path_segs { + for &GenericPathSegment(def_id, index) in &generic_segments { let seg = &segments[index]; let generics = tcx.generics_of(def_id); @@ -1233,8 +1227,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let has_self = - path_segs.last().is_some_and(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self); + let has_self = generic_segments + .last() + .is_some_and(|GenericPathSegment(def_id, _)| tcx.generics_of(*def_id).has_self); let (res, self_ctor_args) = if let Res::SelfCtor(impl_def_id) = res { let ty = LoweredTy::from_raw( @@ -1294,22 +1289,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, }; - struct CreateCtorInstantiationsContext<'a, 'tcx> { + struct CtorGenericArgsCtxt<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, - path_segs: &'a [PathSeg], + generic_segments: &'a [GenericPathSegment], infer_args_for_err: &'a FxHashSet<usize>, segments: &'tcx [hir::PathSegment<'tcx>], } - impl<'tcx, 'a> CreateInstantiationsForGenericArgsCtxt<'a, 'tcx> - for CreateCtorInstantiationsContext<'a, 'tcx> - { + impl<'tcx, 'a> GenericArgsLowerer<'a, 'tcx> for CtorGenericArgsCtxt<'a, 'tcx> { fn args_for_def_id( &mut self, def_id: DefId, ) -> (Option<&'a hir::GenericArgs<'tcx>>, bool) { - if let Some(&PathSeg(_, index)) = - self.path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) + if let Some(&GenericPathSegment(_, index)) = + self.generic_segments.iter().find(|&GenericPathSegment(did, _)| *did == def_id) { // If we've encountered an `impl Trait`-related error, we're just // going to infer the arguments for better error messages. @@ -1332,13 +1325,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.fcx.astconv().ast_region_to_region(lt, Some(param)).into() + self.fcx.lowerer().lower_lifetime(lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.fcx.to_ty(ty).raw.into() + self.fcx.lower_ty(ty).raw.into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.fcx.const_arg_to_const(&ct.value, param.def_id).into() + self.fcx.lower_const_arg(&ct.value, param.def_id).into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { self.fcx.ty_infer(Some(param), inf.span).into() @@ -1420,17 +1413,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let args_raw = self_ctor_args.unwrap_or_else(|| { - create_args_for_parent_generic_args( + lower_generic_args( tcx, def_id, &[], has_self, self_ty.map(|s| s.raw), &arg_count, - &mut CreateCtorInstantiationsContext { + &mut CtorGenericArgsCtxt { fcx: self, span, - path_segs: &path_segs, + generic_segments: &generic_segments, infer_args_for_err: &infer_args_for_err, segments, }, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 5c56c6acd27..ebc5e11a561 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -24,9 +24,9 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, Node, QPath}; -use rustc_hir_analysis::astconv::AstConv; use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_hir_analysis::structured_errors::StructuredDiag; use rustc_index::IndexVec; use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; @@ -45,6 +45,29 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext} use std::iter; use std::mem; +#[derive(Clone, Copy, Default)] +pub enum DivergingBlockBehavior { + /// This is the current stable behavior: + /// + /// ```rust + /// { + /// return; + /// } // block has type = !, even though we are supposedly dropping it with `;` + /// ``` + #[default] + Never, + + /// Alternative behavior: + /// + /// ```ignore (very-unstable-new-attribute) + /// #![rustc_never_type_options(diverging_block_default = "unit")] + /// { + /// return; + /// } // block has type = (), since we are dropping `!` from `return` with `;` + /// ``` + Unit, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { // don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors @@ -1371,9 +1394,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg: &hir::Expr<'tcx>, err: &mut Diag<'tcx>, ) { - if let ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Mut, .. }) = expected_ty.kind() - && let ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Not, .. }) = - provided_ty.kind() + if let ty::RawPtr(_, hir::Mutability::Mut) = expected_ty.kind() + && let ty::RawPtr(_, hir::Mutability::Not) = provided_ty.kind() && let hir::ExprKind::Call(callee, _) = arg.kind && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = callee.kind && let Res::Def(_, def_id) = path.res @@ -1579,7 +1601,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Type check a `let` statement. - pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + pub fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) { self.check_decl(local.into()); if local.pat.is_never_pattern() { self.diverges.set(Diverges::Always { @@ -1710,7 +1732,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // // #41425 -- label the implicit `()` as being the // "found type" here, rather than the "expected type". - if !self.diverges.get().is_always() { + if !self.diverges.get().is_always() + || matches!(self.diverging_block_behavior, DivergingBlockBehavior::Unit) + { // #50009 -- Do not point at the entire fn block span, point at the return type // span, as it is the cause of the requirement, and // `consider_hint_about_removing_semicolon` will point at the last expression @@ -1765,7 +1789,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [ hir::Stmt { kind: - hir::StmtKind::Let(hir::Local { + hir::StmtKind::Let(hir::LetStmt { source: hir::LocalSource::AssignDesugar(_), .. @@ -1936,16 +1960,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> (Res, LoweredTy<'tcx>) { match *qpath { QPath::Resolved(ref maybe_qself, path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw); - let ty = self.astconv().res_to_ty(self_ty, path, hir_id, true); + let self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself).raw); + let ty = self.lowerer().lower_path(self_ty, path, hir_id, true); (path.res, LoweredTy::from_raw(self, path_span, ty)) } QPath::TypeRelative(qself, segment) => { - let ty = self.to_ty(qself); + let ty = self.lower_ty(qself); let result = self - .astconv() - .associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true); + .lowerer() + .lower_assoc_path(hir_id, path_span, ty.raw, qself, segment, true); let ty = result .map(|(ty, _, _)| ty) .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar)); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 685b1af931e..efa2862177e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -5,11 +5,14 @@ mod checks; mod suggestions; use crate::coercion::DynamicCoerceMany; +use crate::fallback::DivergingFallbackBehavior; +use crate::fn_ctxt::checks::DivergingBlockBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited}; +use hir::def_id::CRATE_DEF_ID; use rustc_errors::{DiagCtxt, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir_analysis::astconv::AstConv; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer; use rustc_infer::infer::error_reporting::sub_relations::SubRelations; use rustc_infer::infer::error_reporting::TypeErrCtxt; @@ -18,7 +21,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::symbol::Ident; -use rustc_span::{self, Span, DUMMY_SP}; +use rustc_span::{self, sym, Span, DUMMY_SP}; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::cell::{Cell, RefCell}; @@ -108,6 +111,9 @@ pub struct FnCtxt<'a, 'tcx> { pub(super) inh: &'a Inherited<'tcx>, pub(super) fallback_has_occurred: Cell<bool>, + + pub(super) diverging_fallback_behavior: DivergingFallbackBehavior, + pub(super) diverging_block_behavior: DivergingBlockBehavior, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -116,6 +122,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, ) -> FnCtxt<'a, 'tcx> { + let (diverging_fallback_behavior, diverging_block_behavior) = + parse_never_type_options_attr(inh.tcx); FnCtxt { body_id, param_env, @@ -131,6 +139,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }), inh, fallback_has_occurred: Cell::new(false), + diverging_fallback_behavior, + diverging_block_behavior, } } @@ -202,7 +212,7 @@ impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { } } -impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { +impl<'a, 'tcx> HirTyLowerer<'tcx> for FnCtxt<'a, 'tcx> { fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { self.tcx } @@ -211,31 +221,8 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { self.body_id.to_def_id() } - fn get_type_parameter_bounds( - &self, - _: Span, - def_id: LocalDefId, - _: Ident, - ) -> ty::GenericPredicates<'tcx> { - let tcx = self.tcx; - let item_def_id = tcx.hir().ty_param_owner(def_id); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id.to_def_id()]; - // HACK(eddyb) should get the original `Span`. - let span = tcx.def_span(def_id); - ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter( - self.param_env.caller_bounds().iter().filter_map(|predicate| { - match predicate.kind().skip_binder() { - ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => { - Some((predicate, span)) - } - _ => None, - } - }), - ), - } + fn allow_infer(&self) -> bool { + true } fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> { @@ -246,10 +233,6 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { Some(self.next_region_var(v)) } - fn allow_ty_infer(&self) -> bool { - true - } - fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { match param { Some(param) => self.var_for_def(span, param).as_type().unwrap(), @@ -282,7 +265,34 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { } } - fn projected_ty_from_poly_trait_ref( + fn probe_ty_param_bounds( + &self, + _: Span, + def_id: LocalDefId, + _: Ident, + ) -> ty::GenericPredicates<'tcx> { + let tcx = self.tcx; + let item_def_id = tcx.hir().ty_param_owner(def_id); + let generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id.to_def_id()]; + // HACK(eddyb) should get the original `Span`. + let span = tcx.def_span(def_id); + ty::GenericPredicates { + parent: None, + predicates: tcx.arena.alloc_from_iter( + self.param_env.caller_bounds().iter().filter_map(|predicate| { + match predicate.kind().skip_binder() { + ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => { + Some((predicate, span)) + } + _ => None, + } + }), + ), + } + } + + fn lower_assoc_ty( &self, span: Span, item_def_id: DefId, @@ -295,7 +305,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { poly_trait_ref, ); - let item_args = self.astconv().create_args_for_associated_item( + let item_args = self.lowerer().lower_generic_args_of_assoc_item( span, item_def_id, item_segment, @@ -318,10 +328,6 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { } } - fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { - self.infcx.set_tainted_by_errors(e) - } - fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) { // FIXME: normalization and escaping regions let ty = if !ty.has_escaping_bound_vars() { @@ -345,13 +351,17 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> { Some(&self.infcx) } + + fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + self.infcx.set_tainted_by_errors(e) + } } /// The `ty` representation of a user-provided type. Depending on the use-site /// we want to either use the unnormalized or the normalized form of this type. /// -/// This is a bridge between the interface of `AstConv`, which outputs a raw `Ty`, -/// and the API in this module, which expect `Ty` to be fully normalized. +/// This is a bridge between the interface of HIR ty lowering, which outputs a raw +/// `Ty`, and the API in this module, which expect `Ty` to be fully normalized. #[derive(Clone, Copy, Debug)] pub struct LoweredTy<'tcx> { /// The unnormalized type provided by the user. @@ -374,3 +384,64 @@ impl<'tcx> LoweredTy<'tcx> { LoweredTy { raw, normalized } } } + +fn parse_never_type_options_attr( + tcx: TyCtxt<'_>, +) -> (DivergingFallbackBehavior, DivergingBlockBehavior) { + use DivergingFallbackBehavior::*; + + // Error handling is dubious here (unwraps), but that's probably fine for an internal attribute. + // Just don't write incorrect attributes <3 + + let mut fallback = None; + let mut block = None; + + let items = tcx + .get_attr(CRATE_DEF_ID, sym::rustc_never_type_options) + .map(|attr| attr.meta_item_list().unwrap()) + .unwrap_or_default(); + + for item in items { + if item.has_name(sym::fallback) && fallback.is_none() { + let mode = item.value_str().unwrap(); + match mode { + sym::unit => fallback = Some(FallbackToUnit), + sym::niko => fallback = Some(FallbackToNiko), + sym::never => fallback = Some(FallbackToNever), + sym::no => fallback = Some(NoFallback), + _ => { + tcx.dcx().span_err(item.span(), format!("unknown never type fallback mode: `{mode}` (supported: `unit`, `niko`, `never` and `no`)")); + } + }; + continue; + } + + if item.has_name(sym::diverging_block_default) && fallback.is_none() { + let mode = item.value_str().unwrap(); + match mode { + sym::unit => block = Some(DivergingBlockBehavior::Unit), + sym::never => block = Some(DivergingBlockBehavior::Never), + _ => { + tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{mode}` (supported: `unit` and `never`)")); + } + }; + continue; + } + + tcx.dcx().span_err( + item.span(), + format!( + "unknown never type option: `{}` (supported: `fallback`)", + item.name_or_empty() + ), + ); + } + + let fallback = fallback.unwrap_or_else(|| { + if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit } + }); + + let block = block.unwrap_or_default(); + + (fallback, block) +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 3f6f4cccba7..442bfd75746 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -7,7 +7,6 @@ use crate::hir::is_range_literal; use crate::method::probe; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use crate::rustc_middle::ty::Article; -use crate::ty::TypeAndMut; use core::cmp::min; use core::iter; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; @@ -21,7 +20,7 @@ use rustc_hir::{ CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; -use rustc_hir_analysis::astconv::AstConv; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{self}; use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; @@ -318,7 +317,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); expr_id = parent_id; } - Node::Local(local) => { + Node::LetStmt(local) => { if let Some(mut ty) = local.ty { while let Some(index) = tuple_indexes.pop() { match ty.kind { @@ -809,7 +808,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { - if let Some(found) = found.make_suggestable(self.tcx, false) { + if let Some(found) = found.make_suggestable(self.tcx, false, None) { err.subdiagnostic( self.dcx(), errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() }, @@ -877,7 +876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Only point to return type if the expected type is the return type, as if they // are not, the expectation must have been caused by something else. debug!("return type {:?}", hir_ty); - let ty = self.astconv().ast_ty_to_ty(hir_ty); + let ty = self.lowerer().lower_ty(hir_ty); debug!("return type {:?}", ty); debug!("expected type {:?}", expected); let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into()); @@ -889,7 +888,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.dcx(), errors::ExpectedReturnTypeLabel::Other { span: hir_ty.span, expected }, ); - self.try_suggest_return_impl_trait(err, expected, ty, fn_id); + self.try_suggest_return_impl_trait(err, expected, found, fn_id); + self.note_caller_chooses_ty_for_ty_param(err, expected, found); return true; } } @@ -899,6 +899,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + fn note_caller_chooses_ty_for_ty_param( + &self, + diag: &mut Diag<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if let ty::Param(expected_ty_as_param) = expected.kind() { + diag.subdiagnostic( + self.dcx(), + errors::NoteCallerChoosesTyForTyParam { + ty_param_name: expected_ty_as_param.name, + found_ty: found, + }, + ); + } + } + /// check whether the return type is a generic type with a trait bound /// only suggest this if the generic param is not present in the arguments /// if this is true, hint them towards changing the return type to `impl Trait` @@ -957,8 +974,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bounded_ty, .. }) => { - // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below) - let ty = self.astconv().ast_ty_to_ty(bounded_ty); + // FIXME: Maybe these calls to `lower_ty` can be removed (and the ones below) + let ty = self.lowerer().lower_ty(bounded_ty); Some((ty, bounds)) } _ => None, @@ -996,7 +1013,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let all_bounds_str = all_matching_bounds_strs.join(" + "); let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| { - let ty = self.astconv().ast_ty_to_ty( param); + let ty = self.lowerer().lower_ty( param); matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param) }); @@ -1071,7 +1088,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let can_return = match fn_decl.output { hir::FnRetTy::Return(ty) => { - let ty = self.astconv().ast_ty_to_ty(ty); + let ty = self.lowerer().lower_ty(ty); let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self .tcx @@ -1331,7 +1348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ++++++++++ // since the user probably just misunderstood how `let else` // and `&&` work together. - if let Some((_, hir::Node::Local(local))) = cond_parent + if let Some((_, hir::Node::LetStmt(local))) = cond_parent && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind && let hir::QPath::Resolved(None, path) = qpath @@ -1479,7 +1496,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty: Ty<'tcx>, ) -> bool { // Expected type needs to be a raw pointer. - let ty::RawPtr(ty::TypeAndMut { mutbl, .. }) = expected_ty.kind() else { + let ty::RawPtr(_, mutbl) = expected_ty.kind() else { return false; }; @@ -1731,7 +1748,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match self.tcx.parent_hir_node(*hir_id) { // foo.clone() - hir::Node::Local(hir::Local { init: Some(init), .. }) => { + hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => { self.note_type_is_not_clone_inner_expr(init) } // When `expr` is more complex like a tuple @@ -1740,7 +1757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: hir::PatKind::Tuple(pats, ..), .. }) => { - let hir::Node::Local(hir::Local { init: Some(init), .. }) = + let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) = self.tcx.parent_hir_node(*pat_hir_id) else { return expr; @@ -1774,7 +1791,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } = call_expr_path && let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding) - && let hir::Node::Local(hir::Local { init: Some(init), .. }) = + && let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) = self.tcx.parent_hir_node(*hir_id) && let Expr { kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }), @@ -2509,11 +2526,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return make_sugg(sp, expr.span.lo()); } } - ( - _, - &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }), - &ty::Ref(_, ty_a, mutbl_a), - ) => { + (_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => { if let Some(steps) = self.deref_steps(ty_a, ty_b) // Only suggest valid if dereferencing needed. && steps > 0 @@ -3134,7 +3147,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else { return; }; - let hir::Node::Local(hir::Local { ty: None, init: Some(init), .. }) = + let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) = self.tcx.parent_hir_node(pat.hir_id) else { return; diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 4d37f725c94..1fa799dcec7 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -29,7 +29,7 @@ impl<'a> DeclOrigin<'a> { } } -/// A declaration is an abstraction of [hir::Local] and [hir::Let]. +/// A declaration is an abstraction of [hir::LetStmt] and [hir::LetExpr]. /// /// It must have a hir_id, as this is how we connect gather_locals to the check functions. pub(super) struct Declaration<'a> { @@ -41,16 +41,16 @@ pub(super) struct Declaration<'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; +impl<'a> From<&'a hir::LetStmt<'a>> for Declaration<'a> { + fn from(local: &'a hir::LetStmt<'a>) -> Self { + let hir::LetStmt { hir_id, pat, ty, span, init, els, source: _ } = *local; Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } } } } -impl<'a> From<(&'a hir::Let<'a>, hir::HirId)> for Declaration<'a> { - fn from((let_expr, hir_id): (&'a hir::Let<'a>, hir::HirId)) -> Self { - let hir::Let { pat, ty, span, init, is_recovered: _ } = *let_expr; +impl<'a> From<(&'a hir::LetExpr<'a>, hir::HirId)> for Declaration<'a> { + fn from((let_expr, hir_id): (&'a hir::LetExpr<'a>, hir::HirId)) -> Self { + let hir::LetExpr { pat, ty, span, init, is_recovered: _ } = *let_expr; Declaration { hir_id, pat, ty, span, init: Some(init), origin: DeclOrigin::LetExpr } } } @@ -93,7 +93,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { fn declare(&mut self, decl: Declaration<'tcx>) { let local_ty = match decl.ty { Some(ref ty) => { - let o_ty = self.fcx.to_ty(ty); + let o_ty = self.fcx.lower_ty(ty); let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty.raw)); @@ -120,7 +120,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { // Add explicitly-declared locals. - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { self.declare(local.into()); intravisit::walk_local(self, local) } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index d259e71f957..0b67b37df29 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -57,8 +57,8 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; use rustc_hir::{HirIdMap, Node}; -use rustc_hir_analysis::astconv::AstConv; use rustc_hir_analysis::check::check_abi; +use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::query::Providers; @@ -178,7 +178,7 @@ fn typeck_with_fallback<'tcx>( if let Some(hir::FnSig { header, decl, .. }) = fn_sig { let fn_sig = if decl.output.get_infer_ret_ty().is_some() { - fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None) + fcx.lowerer().lower_fn_ty(id, header.unsafety, header.abi, decl, None, None) } else { tcx.fn_sig(def_id).instantiate_identity() }; diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 9307cccf092..f5b6dd162b3 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -268,11 +268,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { adjustment::Adjust::Deref(overloaded) => { // Equivalent to *expr or something similar. let base = if let Some(deref) = overloaded { - let ref_ty = Ty::new_ref( - self.tcx(), - deref.region, - ty::TypeAndMut { ty: target, mutbl: deref.mutbl }, - ); + let ref_ty = Ty::new_ref(self.tcx(), deref.region, target, deref.mutbl); self.cat_rvalue(expr.hir_id, ref_ty) } else { previous()? @@ -479,7 +475,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { let ty::Ref(region, _, mutbl) = *base_ty.kind() else { span_bug!(expr.span, "cat_overloaded_place: base is not a reference"); }; - let ref_ty = Ty::new_ref(self.tcx(), region, ty::TypeAndMut { ty: place_ty, mutbl }); + let ref_ty = Ty::new_ref(self.tcx(), region, place_ty, mutbl); let base = self.cat_rvalue(expr.hir_id, ref_ty); self.cat_deref(expr, base) @@ -719,7 +715,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_pattern_(place_with_id, subpat, op)?; } - PatKind::Box(subpat) | PatKind::Ref(subpat, _) => { + PatKind::Box(subpat) | PatKind::Ref(subpat, _) | PatKind::Deref(subpat) => { // box p1, &p1, &mut p1. we can ignore the mutability of // PatKind::Ref since that information is already contained // in the type. diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index a580c114f26..36860e446fc 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -4,10 +4,10 @@ use crate::{callee, FnCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; -use rustc_hir_analysis::astconv::generics::{ - check_generic_arg_count_for_call, create_args_for_parent_generic_args, +use rustc_hir_analysis::hir_ty_lowering::generics::{ + check_generic_arg_count_for_call, lower_generic_args, }; -use rustc_hir_analysis::astconv::{AstConv, CreateInstantiationsForGenericArgsCtxt, IsMethodCall}; +use rustc_hir_analysis::hir_ty_lowering::{GenericArgsLowerer, HirTyLowerer, IsMethodCall}; use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -188,7 +188,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // Type we're wrapping in a reference, used later for unsizing let base_ty = target; - target = Ty::new_ref(self.tcx, region, ty::TypeAndMut { mutbl, ty: target }); + target = Ty::new_ref(self.tcx, region, target, mutbl); // Method call receivers are the primary use case // for two-phase borrows. @@ -208,11 +208,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { base_ty ) }; - target = Ty::new_ref( - self.tcx, - region, - ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty }, - ); + target = Ty::new_ref(self.tcx, region, unsized_ty, mutbl.into()); adjustments.push(Adjustment { kind: Adjust::Pointer(PointerCoercion::Unsize), target, @@ -221,9 +217,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => { target = match target.kind() { - &ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { + &ty::RawPtr(ty, mutbl) => { assert!(mutbl.is_mut()); - Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty }) + Ty::new_imm_ptr(self.tcx, ty) } other => panic!("Cannot adjust receiver type {other:?} to const ptr"), }; @@ -366,14 +362,12 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // combining parameters from the type and those from the method. assert_eq!(generics.parent_count, parent_args.len()); - struct MethodInstantiationsCtxt<'a, 'tcx> { + struct GenericArgsCtxt<'a, 'tcx> { cfcx: &'a ConfirmContext<'a, 'tcx>, pick: &'a probe::Pick<'tcx>, seg: &'a hir::PathSegment<'tcx>, } - impl<'a, 'tcx> CreateInstantiationsForGenericArgsCtxt<'a, 'tcx> - for MethodInstantiationsCtxt<'a, 'tcx> - { + impl<'a, 'tcx> GenericArgsLowerer<'a, 'tcx> for GenericArgsCtxt<'a, 'tcx> { fn args_for_def_id( &mut self, def_id: DefId, @@ -393,13 +387,13 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - self.cfcx.fcx.astconv().ast_region_to_region(lt, Some(param)).into() + self.cfcx.fcx.lowerer().lower_lifetime(lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.cfcx.to_ty(ty).raw.into() + self.cfcx.lower_ty(ty).raw.into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.cfcx.const_arg_to_const(&ct.value, param.def_id).into() + self.cfcx.lower_const_arg(&ct.value, param.def_id).into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { self.cfcx.ty_infer(Some(param), inf.span).into() @@ -432,14 +426,14 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } } - let args = create_args_for_parent_generic_args( + let args = lower_generic_args( self.tcx, pick.item.def_id, parent_args, false, None, &arg_count_correct, - &mut MethodInstantiationsCtxt { cfcx: self, pick, seg }, + &mut GenericArgsCtxt { cfcx: self, pick, seg }, ); // When the method is confirmed, the `args` includes diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 3b26a791f65..e9752d7a4a8 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -200,11 +200,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(span) = result.illegal_sized_bound { let mut needs_mut = false; if let ty::Ref(region, t_type, mutability) = self_ty.kind() { - let trait_type = Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, - ); + let trait_type = Ty::new_ref(self.tcx, *region, *t_type, mutability.invert()); // We probe again to see if there might be a borrow mutability discrepancy. match self.lookup_probe( segment.ident, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index eada5a0bc30..4e63600dbdf 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -528,7 +528,7 @@ fn method_autoderef_steps<'tcx>( from_unsafe_deref: reached_raw_pointer, unsize: false, }; - if let ty::RawPtr(_) = ty.kind() { + if let ty::RawPtr(_, _) = ty.kind() { // all the subsequent steps will be from_unsafe_deref reached_raw_pointer = true; } @@ -696,7 +696,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::Str | ty::Array(..) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::Never | ty::Tuple(..) => self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty), @@ -1213,7 +1213,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // In general, during probing we erase regions. let region = tcx.lifetimes.re_erased; - let autoref_ty = Ty::new_ref(tcx, region, ty::TypeAndMut { ty: self_ty, mutbl }); + let autoref_ty = Ty::new_ref(tcx, region, self_ty, mutbl); self.pick_method(autoref_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; @@ -1238,12 +1238,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { return None; } - let &ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) = self_ty.kind() else { + let &ty::RawPtr(ty, hir::Mutability::Mut) = self_ty.kind() else { return None; }; - let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }; - let const_ptr_ty = Ty::new_ptr(self.tcx, const_self_ty); + let const_ptr_ty = Ty::new_imm_ptr(self.tcx, ty); self.pick_method(const_ptr_ty, unstable_candidates).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c5bbcc56f86..0dcde0cdecb 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -304,11 +304,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { if needs_mut { - let trait_type = Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, - ); + let trait_type = + Ty::new_ref(self.tcx, *region, *t_type, mutability.invert()); let msg = format!("you need `{trait_type}` instead of `{rcvr_ty}`"); let mut kind = &self_expr.kind; while let hir::ExprKind::AddrOf(_, _, expr) @@ -533,7 +530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - if let ty::RawPtr(_) = &rcvr_ty.kind() { + if let ty::RawPtr(_, _) = &rcvr_ty.kind() { err.note( "try using `<*const T>::as_ref()` to get a reference to the \ type behind the pointer: https://doc.rust-lang.org/std/\ @@ -875,7 +872,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match pred.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { Some(pred.def_id()) == self.tcx.lang_items().sized_trait() - && pred.polarity == ty::ImplPolarity::Positive + && pred.polarity == ty::PredicatePolarity::Positive } _ => false, } @@ -2166,7 +2163,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (filename, parent_node) { ( FileName::Real(_), - Node::Local(hir::Local { + Node::LetStmt(hir::LetStmt { source: hir::LocalSource::Normal, ty, .. @@ -2221,7 +2218,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { impl<'v> Visitor<'v> for LetVisitor { type Result = ControlFlow<Option<&'v hir::Expr<'v>>>; fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { - if let hir::StmtKind::Let(&hir::Local { pat, init, .. }) = ex.kind + if let hir::StmtKind::Let(&hir::LetStmt { pat, init, .. }) = ex.kind && let Binding(_, _, ident, ..) = pat.kind && ident.name == self.ident_name { @@ -3264,8 +3261,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Colon, Nothing, } - let ast_generics = hir.get_generics(id.owner.def_id).unwrap(); - let trait_def_ids: DefIdSet = ast_generics + let hir_generics = hir.get_generics(id.owner.def_id).unwrap(); + let trait_def_ids: DefIdSet = hir_generics .bounds_for_param(def_id) .flat_map(|bp| bp.bounds.iter()) .filter_map(|bound| bound.trait_ref()?.trait_def_id()) @@ -3277,7 +3274,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "restrict type parameter `{}` with", param.name.ident(), )); - let bounds_span = ast_generics.bounds_span_for_suggestions(def_id); + let bounds_span = hir_generics.bounds_span_for_suggestions(def_id); if rcvr_ty.is_ref() && param.is_impl_trait() && bounds_span.is_some() { err.multipart_suggestions( msg, @@ -3367,7 +3364,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "inherent impls can't be candidates, only trait impls can be", ) }) - .filter(|header| header.polarity == ty::ImplPolarity::Negative) + .filter(|header| header.polarity != ty::ImplPolarity::Positive) .any(|header| { let imp = header.trait_ref.instantiate_identity(); let imp_simp = diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 79f574aa7fd..b17b312a797 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -508,11 +508,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggest_deref_binop(&mut err, *lhs_deref_ty); } else { let lhs_inv_mutbl = mutbl.invert(); - let lhs_inv_mutbl_ty = Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { ty: *lhs_deref_ty, mutbl: lhs_inv_mutbl }, - ); + let lhs_inv_mutbl_ty = + Ty::new_ref(self.tcx, *region, *lhs_deref_ty, lhs_inv_mutbl); suggest_different_borrow( &mut err, @@ -524,11 +521,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() { let rhs_inv_mutbl = mutbl.invert(); - let rhs_inv_mutbl_ty = Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { ty: *rhs_deref_ty, mutbl: rhs_inv_mutbl }, - ); + let rhs_inv_mutbl_ty = + Ty::new_ref(self.tcx, *region, *rhs_deref_ty, rhs_inv_mutbl); suggest_different_borrow( &mut err, @@ -601,8 +595,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(output_def_id) = output_def_id && let Some(trait_def_id) = trait_def_id && self.tcx.parent(output_def_id) == trait_def_id - && let Some(output_ty) = - output_ty.make_suggestable(self.tcx, false) + && let Some(output_ty) = output_ty + .make_suggestable(self.tcx, false, None) { Some(("Output", output_ty)) } else { diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 491da7eb2c2..7ecd380ebeb 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -18,8 +18,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::Span; -use rustc_span::{BytePos, DUMMY_SP}; +use rustc_span::{BytePos, Span, DUMMY_SP}; use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::{ObligationCause, Pattern}; use ty::VariantDef; @@ -212,6 +211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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::Deref(inner) => self.check_pat_deref(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, pat_info) @@ -295,6 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::TupleStruct(..) | PatKind::Tuple(..) | PatKind::Box(_) + | PatKind::Deref(_) | PatKind::Range(..) | PatKind::Slice(..) => AdjustMode::Peel, // A never pattern behaves somewhat like a literal or unit variant. @@ -743,7 +744,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ident_kind = match binding_parent { hir::Node::Param(_) => "parameter", - hir::Node::Local(_) => "variable", + hir::Node::LetStmt(_) => "variable", hir::Node::Arm(_) => "binding", // Provide diagnostics only if the parent pattern is struct-like, @@ -760,6 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::Binding(..) | PatKind::Path(..) | PatKind::Box(..) + | PatKind::Deref(_) | PatKind::Ref(..) | PatKind::Lit(..) | PatKind::Range(..) @@ -1975,6 +1977,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { box_ty } + fn check_pat_deref( + &self, + span: Span, + inner: &'tcx Pat<'tcx>, + expected: Ty<'tcx>, + pat_info: PatInfo<'tcx, '_>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + // FIXME(deref_patterns): use `DerefPure` for soundness + // FIXME(deref_patterns): use `DerefMut` when required + // <expected as Deref>::Target + let ty = Ty::new_projection( + tcx, + tcx.require_lang_item(hir::LangItem::DerefTarget, Some(span)), + [expected], + ); + let ty = self.normalize(span, ty); + let ty = self.try_structurally_resolve_type(span, ty); + self.check_pat(inner, ty, pat_info); + expected + } + // Precondition: Pat is Ref(inner) fn check_pat_ref( &self, @@ -2033,8 +2057,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Create a reference type with a fresh region variable. fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { let region = self.next_region_var(infer::PatternRegion(span)); - let mt = ty::TypeAndMut { ty, mutbl }; - Ty::new_ref(self.tcx, region, mt) + Ty::new_ref(self.tcx, region, ty, mutbl) } fn try_resolve_slice_ty_to_array_ty( diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 6c5715b323c..f29dc39b7be 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -162,11 +162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(*region, AutoBorrowMutability::Not)), - target: Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty }, - ), + target: Ty::new_imm_ref(self.tcx, *region, adjusted_ty), }); } else { span_bug!(expr.span, "input to index is not a ref?"); @@ -400,11 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { allow_two_phase_borrow: AllowTwoPhase::No, }; adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)); - adjustment.target = Ty::new_ref( - self.tcx, - *region, - ty::TypeAndMut { ty: source, mutbl: mutbl.into() }, - ); + adjustment.target = Ty::new_ref(self.tcx, *region, source, mutbl.into()); } source = adjustment.target; } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index b71e88a1579..e489b431e81 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -217,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bug!(); }; for stmt in block.stmts { - let hir::StmtKind::Let(hir::Local { + let hir::StmtKind::Let(hir::LetStmt { init: Some(init), source: hir::LocalSource::AsyncFn, pat, @@ -1719,7 +1719,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for pointer_ty in place.deref_tys() { match pointer_ty.kind() { // We don't capture derefs of raw ptrs - ty::RawPtr(_) => unreachable!(), + ty::RawPtr(_, _) => unreachable!(), // Dereferencing a mut-ref allows us to mut the Place if we don't deref // an immut-ref after on top of this. @@ -1780,11 +1780,9 @@ fn apply_capture_kind_on_capture_ty<'tcx>( ) -> Ty<'tcx> { match capture_kind { ty::UpvarCapture::ByValue => ty, - ty::UpvarCapture::ByRef(kind) => Ty::new_ref( - tcx, - region.unwrap(), - ty::TypeAndMut { ty: ty, mutbl: kind.to_mutbl_lossy() }, - ), + ty::UpvarCapture::ByRef(kind) => { + Ty::new_ref(tcx, region.unwrap(), ty, kind.to_mutbl_lossy()) + } } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 0740a3fd3e9..f4516b684c3 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -351,7 +351,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_pat(self, p); } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { intravisit::walk_local(self, l); let var_ty = self.fcx.local_ty(l.span, l.hir_id); let var_ty = self.resolve(var_ty, &l.span); diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 24512dea939..5156a8d3479 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -148,7 +148,7 @@ pub fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { + for id in crate_items.free_items() { dirty_clean_visitor.check_item(id.owner_id.def_id); } diff --git a/compiler/rustc_index_macros/src/lib.rs b/compiler/rustc_index_macros/src/lib.rs index 532cac5791e..015518ae4d6 100644 --- a/compiler/rustc_index_macros/src/lib.rs +++ b/compiler/rustc_index_macros/src/lib.rs @@ -34,13 +34,7 @@ mod newtype; #[proc_macro] #[cfg_attr( feature = "nightly", - allow_internal_unstable( - step_trait, - rustc_attrs, - trusted_step, - spec_option_partial_eq, - min_specialization - ) + allow_internal_unstable(step_trait, rustc_attrs, trusted_step, min_specialization) )] pub fn newtype_index(input: TokenStream) -> TokenStream { newtype::newtype(input) diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs index 0b25628b9e1..e5c2ba42483 100644 --- a/compiler/rustc_index_macros/src/newtype.rs +++ b/compiler/rustc_index_macros/src/newtype.rs @@ -156,32 +156,6 @@ impl Parse for Newtype { } }; - let spec_partial_eq_impl = if let Lit::Int(max) = &max { - if let Ok(max_val) = max.base10_parse::<u32>() { - quote! { - #gate_rustc_only - impl core::option::SpecOptionPartialEq for #name { - #[inline] - fn eq(l: &Option<Self>, r: &Option<Self>) -> bool { - if #max_val < u32::MAX { - l.map(|i| i.as_u32()).unwrap_or(#max_val+1) == r.map(|i| i.as_u32()).unwrap_or(#max_val+1) - } else { - match (l, r) { - (Some(l), Some(r)) => r == l, - (None, None) => true, - _ => false - } - } - } - } - } - } else { - quote! {} - } - } else { - quote! {} - }; - Ok(Self(quote! { #(#attrs)* #[derive(Clone, Copy, PartialEq, Eq, Hash, #(#derive_paths),*)] @@ -283,8 +257,6 @@ impl Parse for Newtype { #step - #spec_partial_eq_impl - impl From<#name> for u32 { #[inline] fn from(v: #name) -> u32 { diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index e44a6ae3b3f..521c65c6009 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -169,7 +169,7 @@ infer_lifetime_param_suggestion_elided = each elided lifetime in input position infer_meant_byte_literal = if you meant to write a byte literal, prefix with `b` infer_meant_char_literal = if you meant to write a `char` literal, use single quotes -infer_meant_str_literal = if you meant to write a `str` literal, use double quotes +infer_meant_str_literal = if you meant to write a string literal, use double quotes infer_mismatched_static_lifetime = incompatible lifetime on type infer_more_targeted = {$has_param_name -> [true] `{$param_name}` diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index a3cf0d8e520..d0b1f2848ff 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1339,15 +1339,12 @@ pub enum TypeErrorAdditionalDiags { span: Span, code: String, }, - #[suggestion( - infer_meant_str_literal, - code = "\"{code}\"", - applicability = "machine-applicable" - )] + #[multipart_suggestion(infer_meant_str_literal, applicability = "machine-applicable")] MeantStrLiteral { - #[primary_span] - span: Span, - code: String, + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, }, #[suggestion( infer_consider_specifying_length, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 331b3b97c34..82634f7308d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -403,8 +403,10 @@ impl<'tcx> InferCtxt<'tcx> { let future_trait = self.tcx.require_lang_item(LangItem::Future, None); let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; - self.tcx.explicit_item_bounds(def_id).iter_instantiated_copied(self.tcx, args).find_map( - |(predicate, _)| { + self.tcx + .explicit_item_super_predicates(def_id) + .iter_instantiated_copied(self.tcx, args) + .find_map(|(predicate, _)| { predicate .kind() .map_bound(|kind| match kind { @@ -417,8 +419,7 @@ impl<'tcx> InferCtxt<'tcx> { }) .no_bound_vars() .flatten() - }, - ) + }) } } @@ -2078,16 +2079,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // If a string was expected and the found expression is a character literal, // perhaps the user meant to write `"s"` to specify a string literal. (ty::Ref(_, r, _), ty::Char) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { - if let Some(code) = - code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) - { - suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { - span, - code: escape_literal(code), - }) - } - } + suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { + start: span.with_hi(span.lo() + BytePos(1)), + end: span.with_lo(span.hi() - BytePos(1)), + }) } // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`, // we try to suggest to add the missing `let` for `if let Some(..) = expr` @@ -2139,7 +2134,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // the same span as the error and the type is specified. if let hir::Stmt { kind: - hir::StmtKind::Let(hir::Local { + hir::StmtKind::Let(hir::LetStmt { init: Some(hir::Expr { span: init_span, .. }), ty: Some(array_ty), .. diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 0686994b037..f89ed256a08 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -11,7 +11,7 @@ use rustc_hir::def::Res; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; +use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource}; use rustc_middle::hir::nested_filter; use rustc_middle::infer::unify_key::{ ConstVariableOrigin, ConstVariableOriginKind, ConstVariableValue, @@ -546,40 +546,55 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => { - let mut printer = fmt_printer(self, Namespace::ValueNS); - printer.print_def_path(def_id, args).unwrap(); - let def_path = printer.into_buffer(); - - // We only care about whether we have to add `&` or `&mut ` for now. - // This is the case if the last adjustment is a borrow and the - // first adjustment was not a builtin deref. - let adjustment = match typeck_results.expr_adjustments(receiver) { - [ - Adjustment { kind: Adjust::Deref(None), target: _ }, - .., - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, - ] => "", - [ - .., - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), target: _ }, - ] => hir::Mutability::from(*mut_).ref_prefix_str(), - _ => "", - }; + let placeholder = Some(self.next_ty_var(TypeVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + })); + if let Some(args) = args.make_suggestable(self.infcx.tcx, true, placeholder) { + let mut printer = fmt_printer(self, Namespace::ValueNS); + printer.print_def_path(def_id, args).unwrap(); + let def_path = printer.into_buffer(); + + // We only care about whether we have to add `&` or `&mut ` for now. + // This is the case if the last adjustment is a borrow and the + // first adjustment was not a builtin deref. + let adjustment = match typeck_results.expr_adjustments(receiver) { + [ + Adjustment { kind: Adjust::Deref(None), target: _ }, + .., + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, + ] => "", + [ + .., + Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), + target: _, + }, + ] => hir::Mutability::from(*mut_).ref_prefix_str(), + _ => "", + }; - multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( - receiver.span, - def_path, - adjustment, - successor, - )); + multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( + receiver.span, + def_path, + adjustment, + successor, + )); + } } InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { - let ty_info = ty_to_string(self, ty, None); - multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( - ty_info, - data, - should_wrap_expr, - )); + let placeholder = Some(self.next_ty_var(TypeVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + })); + if let Some(ty) = ty.make_suggestable(self.infcx.tcx, true, placeholder) { + let ty_info = ty_to_string(self, ty, None); + multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( + ty_info, + data, + should_wrap_expr, + )); + } } } match error_code { @@ -1107,7 +1122,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { self.tecx.tcx.hir() } - fn visit_local(&mut self, local: &'tcx Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) { intravisit::walk_local(self, local); if let Some(ty) = self.opt_node_type(local.hir_id) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index 503645191aa..fe70b631cdb 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -13,8 +13,8 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnosti use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ - self as hir, GenericBound, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, Node, - TyKind, + self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, + LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind, }; use rustc_middle::ty::{ self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, @@ -355,20 +355,8 @@ pub fn suggest_new_region_bound( // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any if let Some(id) = scope_def_id && let Some(generics) = tcx.hir().get_generics(id) - && let mut spans_suggs = generics - .params - .iter() - .filter(|p| p.is_elided_lifetime()) - .map(|p| { - if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { - // Ampersand (elided without '_) - (p.span.shrink_to_hi(), format!("{name} ")) - } else { - // Underscore (elided with '_) - (p.span, name.to_string()) - } - }) - .collect::<Vec<_>>() + && let mut spans_suggs = + make_elided_region_spans_suggs(name, generics.params.iter()) && spans_suggs.len() > 1 { let use_lt = if existing_lt_name == None { @@ -430,6 +418,57 @@ pub fn suggest_new_region_bound( } } +fn make_elided_region_spans_suggs<'a>( + name: &str, + generic_params: impl Iterator<Item = &'a GenericParam<'a>>, +) -> Vec<(Span, String)> { + let mut spans_suggs = Vec::new(); + let mut bracket_span = None; + let mut consecutive_brackets = 0; + + let mut process_consecutive_brackets = + |span: Option<Span>, spans_suggs: &mut Vec<(Span, String)>| { + if span + .is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span)) + { + consecutive_brackets += 1; + } else if let Some(bracket_span) = bracket_span.take() { + let sugg = std::iter::once("<") + .chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", ")) + .chain([">"]) + .collect(); + spans_suggs.push((bracket_span.shrink_to_hi(), sugg)); + consecutive_brackets = 0; + } + bracket_span = span; + }; + + for p in generic_params { + if let GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(kind) } = p.kind { + match kind { + MissingLifetimeKind::Underscore => { + process_consecutive_brackets(None, &mut spans_suggs); + spans_suggs.push((p.span, name.to_string())) + } + MissingLifetimeKind::Ampersand => { + process_consecutive_brackets(None, &mut spans_suggs); + spans_suggs.push((p.span.shrink_to_hi(), format!("{name} "))); + } + MissingLifetimeKind::Comma => { + process_consecutive_brackets(None, &mut spans_suggs); + spans_suggs.push((p.span.shrink_to_hi(), format!("{name}, "))); + } + MissingLifetimeKind::Brackets => { + process_consecutive_brackets(Some(p.span), &mut spans_suggs); + } + } + } + } + process_consecutive_brackets(None, &mut spans_suggs); + + spans_suggs +} + impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { pub fn get_impl_ident_and_self_ty_from_trait( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 24eaff08220..008b75b4c9a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -299,17 +299,19 @@ impl<T> Trait<T> for X { } (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) if let Some(def_id) = t.principal_def_id() - && tcx.explicit_item_bounds(alias.def_id).skip_binder().iter().any( - |(pred, _span)| match pred.kind().skip_binder() { + && tcx + .explicit_item_super_predicates(alias.def_id) + .skip_binder() + .iter() + .any(|(pred, _span)| match pred.kind().skip_binder() { ty::ClauseKind::Trait(trait_predicate) if trait_predicate.polarity - == ty::ImplPolarity::Positive => + == ty::PredicatePolarity::Positive => { trait_predicate.def_id() == def_id } _ => false, - }, - ) => + }) => { diag.help(format!( "you can box the `{}` to coerce it to `Box<{}>`, but you'll have to \ @@ -412,13 +414,13 @@ impl<T> Trait<T> for X { ty::Alias(..) => values.expected, _ => values.found, }; - let preds = tcx.explicit_item_bounds(opaque_ty.def_id); + let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id); for (pred, _span) in preds.skip_binder() { let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder() else { continue; }; - if trait_predicate.polarity != ty::ImplPolarity::Positive { + if trait_predicate.polarity != ty::PredicatePolarity::Positive { continue; } let def_id = trait_predicate.def_id(); diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 9081fbaa2dc..e220c4d02a2 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -2,7 +2,7 @@ use crate::infer::error_reporting::hir::Path; use core::ops::ControlFlow; use hir::def::CtorKind; use hir::intravisit::{walk_expr, walk_stmt, Visitor}; -use hir::{Local, QPath}; +use hir::{LetStmt, QPath}; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; @@ -321,7 +321,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let Some(expr) = block.expr && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind && let Res::Local(local) = res - && let Node::Local(Local { init: Some(init), .. }) = self.tcx.parent_hir_node(*local) + && let Node::LetStmt(LetStmt { init: Some(init), .. }) = + self.tcx.parent_hir_node(*local) { fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) { match expr.kind { @@ -585,7 +586,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { - if let hir::StmtKind::Let(hir::Local { + if let hir::StmtKind::Let(LetStmt { span, pat: hir::Pat { .. }, ty: None, 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 c39d0425f7e..5ae7f8bf504 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -13,6 +13,7 @@ use rustc_data_structures::graph::implementation::{ Direction, Graph, NodeIndex, INCOMING, OUTGOING, }; use rustc_data_structures::intern::Interned; +use rustc_data_structures::unord::UnordSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -139,8 +140,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let mut var_data = self.construct_var_data(); // Deduplicating constraints is shown to have a positive perf impact. - self.data.constraints.sort_by_key(|(constraint, _)| *constraint); - self.data.constraints.dedup_by_key(|(constraint, _)| *constraint); + let mut seen = UnordSet::default(); + self.data.constraints.retain(|(constraint, _)| seen.insert(*constraint)); if cfg!(debug_assertions) { self.dump_constraints(); @@ -888,12 +889,14 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } Constraint::RegSubVar(region, _) | Constraint::VarSubReg(_, region) => { - let constraint_idx = - this.constraints.binary_search_by(|(c, _)| c.cmp(&edge.data)).unwrap(); - state.result.push(RegionAndOrigin { - region, - origin: this.constraints[constraint_idx].1.clone(), - }); + let origin = this + .constraints + .iter() + .find(|(c, _)| *c == edge.data) + .unwrap() + .1 + .clone(); + state.result.push(RegionAndOrigin { region, origin }); } Constraint::RegSubReg(..) => panic!( diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs index a6f8115c27e..02b8ded285f 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs @@ -94,9 +94,6 @@ impl<'tcx> InferCtxt<'tcx> { cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> InferResult<'tcx, ()> { - if a.references_error() || b.references_error() { - return Ok(InferOk { value: (), obligations: vec![] }); - } let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) if def_id.is_local() => { let def_id = def_id.expect_local(); diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 3ef37bf3466..bd981c20567 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -300,7 +300,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { alias_ty: ty::AliasTy<'tcx>, ) -> impl Iterator<Item = ty::Region<'tcx>> { let tcx = self.tcx; - let bounds = tcx.item_bounds(alias_ty.def_id); + let bounds = tcx.item_super_predicates(alias_ty.def_id); trace!("{:#?}", bounds.skip_binder()); bounds .iter_instantiated(tcx, alias_ty.args) diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 0f3f2bc5fa6..3d6b54721d0 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -104,7 +104,7 @@ pub struct RegionConstraintData<'tcx> { } /// Represents a constraint that influences the inference process. -#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum Constraint<'tcx> { /// A region variable is a subregion of another. VarSubVar(RegionVid, RegionVid), diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 807d5500a90..ee9ce842d00 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -24,6 +24,7 @@ #![feature(extend_one)] #![feature(let_chains)] #![feature(if_let_guard)] +#![feature(iter_intersperse)] #![feature(iterator_try_collect)] #![feature(try_blocks)] #![feature(yeet_expr)] diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 4808a1defdd..616f5cc0456 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -209,7 +209,7 @@ impl<'tcx> FulfillmentError<'tcx> { } impl<'tcx> PolyTraitObligation<'tcx> { - pub fn polarity(&self) -> ty::ImplPolarity { + pub fn polarity(&self) -> ty::PredicatePolarity { self.predicate.skip_binder().polarity } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index e9df0505cbb..6d43011d33c 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -270,7 +270,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { match bound_clause.skip_binder() { ty::ClauseKind::Trait(data) => { // Negative trait bounds do not imply any supertrait bounds - if data.polarity == ty::ImplPolarity::Negative { + if data.polarity != ty::PredicatePolarity::Positive { return; } // Get predicates implied by the trait, or only super predicates if we only care about self predicates. diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 1a82e6c6910..8ba14d37982 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -341,51 +341,22 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se let sysroot = filesearch::materialize_sysroot(config.opts.maybe_sysroot.clone()); - let (codegen_backend, target_override) = match config.make_codegen_backend { - None => { - // Build a target without override, so that it can override the backend if needed - let target = - config::build_target_config(&early_dcx, &config.opts, None, &sysroot); - - let backend = util::get_codegen_backend( - &early_dcx, - &sysroot, - config.opts.unstable_opts.codegen_backend.as_deref(), - &target, - ); - - // target_override is documented to be called before init(), so this is okay - let target_override = backend.target_override(&config.opts); - - // Assert that we don't use target's override of the backend and - // backend's override of the target at the same time - if config.opts.unstable_opts.codegen_backend.is_none() - && target.default_codegen_backend.is_some() - && target_override.is_some() - { - rustc_middle::bug!( - "Codegen backend requested target override even though the target requested the backend" - ); - } - - (backend, target_override) - } + let target = config::build_target_config(&early_dcx, &config.opts, &sysroot); + + let codegen_backend = match config.make_codegen_backend { + None => util::get_codegen_backend( + &early_dcx, + &sysroot, + config.opts.unstable_opts.codegen_backend.as_deref(), + &target, + ), Some(make_codegen_backend) => { - // N.B. `make_codegen_backend` takes precedence over `target.default_codegen_backend`, - // which is ignored in this case. - let backend = make_codegen_backend(&config.opts); - - // target_override is documented to be called before init(), so this is okay - let target_override = backend.target_override(&config.opts); - - (backend, target_override) + // N.B. `make_codegen_backend` takes precedence over + // `target.default_codegen_backend`, which is ignored in this case. + make_codegen_backend(&config.opts) } }; - // Re-build target with the (potential) override - let target_cfg = - config::build_target_config(&early_dcx, &config.opts, target_override, &sysroot); - let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from); let bundle = match rustc_errors::fluent_bundle( @@ -418,7 +389,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se locale_resources, config.lint_caps, config.file_loader, - target_cfg, + target, sysroot, util::rustc_version_str().unwrap_or("unknown"), config.ice_file, diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 4d8e749d1da..8a27e9a6453 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -41,8 +41,7 @@ fn mk_session(matches: getopts::Matches) -> (Session, Cfg) { let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone()); - let target_cfg = - rustc_session::config::build_target_config(&early_dcx, &sessopts, None, &sysroot); + let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot); let sess = build_session( early_dcx, @@ -53,7 +52,7 @@ fn mk_session(matches: getopts::Matches) -> (Session, Cfg) { vec![], Default::default(), None, - target_cfg, + target, sysroot, "", None, diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 7d48f90db36..d09f8d7d7cf 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -48,12 +48,20 @@ pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dy } } -const STACK_SIZE: usize = 8 * 1024 * 1024; - -fn get_stack_size() -> Option<usize> { - // FIXME: Hacks on hacks. If the env is trying to override the stack size - // then *don't* set it explicitly. - env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE) +pub static STACK_SIZE: OnceLock<usize> = OnceLock::new(); +pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; + +fn init_stack_size() -> usize { + // Obey the environment setting or default + *STACK_SIZE.get_or_init(|| { + env::var_os("RUST_MIN_STACK") + .map(|os_str| os_str.to_string_lossy().into_owned()) + // ignore if it is set to nothing + .filter(|s| s.trim() != "") + .map(|s| s.trim().parse::<usize>().unwrap()) + // otherwise pick a consistent default + .unwrap_or(DEFAULT_STACK_SIZE) + }) } pub(crate) fn run_in_thread_with_globals<F: FnOnce() -> R + Send, R: Send>( @@ -66,10 +74,7 @@ pub(crate) fn run_in_thread_with_globals<F: FnOnce() -> R + Send, R: Send>( // the parallel compiler, in particular to ensure there is no accidental // sharing of data between the main thread and the compilation thread // (which might cause problems for the parallel compiler). - let mut builder = thread::Builder::new().name("rustc".to_string()); - if let Some(size) = get_stack_size() { - builder = builder.stack_size(size); - } + let builder = thread::Builder::new().name("rustc".to_string()).stack_size(init_stack_size()); // We build the session globals and run `f` on the spawned thread, because // `SessionGlobals` does not impl `Send` in the non-parallel compiler. @@ -120,7 +125,7 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( }); } - let mut builder = rayon::ThreadPoolBuilder::new() + let builder = rayon::ThreadPoolBuilder::new() .thread_name(|_| "rustc".to_string()) .acquire_thread_handler(jobserver::acquire_thread) .release_thread_handler(jobserver::release_thread) @@ -144,10 +149,8 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( on_panic.disable(); }) .unwrap(); - }); - if let Some(size) = get_stack_size() { - builder = builder.stack_size(size); - } + }) + .stack_size(init_stack_size()); // We create the session globals on the main thread, then create the thread // pool. Upon creation, each worker thread created gets a copy of the diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index aba7f95487e..d173c3ac032 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -46,7 +46,7 @@ impl<'a> Cursor<'a> { /// If requested position doesn't exist, `EOF_CHAR` is returned. /// However, getting `EOF_CHAR` doesn't always mean actual end of file, /// it should be checked with `is_eof` method. - pub(crate) fn first(&self) -> char { + pub fn first(&self) -> char { // `.next()` optimizes better than `.nth(0)` self.chars.clone().next().unwrap_or(EOF_CHAR) } @@ -59,6 +59,15 @@ impl<'a> Cursor<'a> { iter.next().unwrap_or(EOF_CHAR) } + /// Peeks the third symbol from the input stream without consuming it. + pub fn third(&self) -> char { + // `.next()` optimizes better than `.nth(1)` + let mut iter = self.chars.clone(); + iter.next(); + iter.next(); + iter.next().unwrap_or(EOF_CHAR) + } + /// Checks if there is nothing more to consume. pub(crate) fn is_eof(&self) -> bool { self.chars.as_str().is_empty() diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index aff1dc40954..70c7aff3f20 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -726,7 +726,7 @@ fn type_implements_negative_copy_modulo_regions<'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 pred = ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Negative }; let obligation = traits::Obligation { cause: traits::ObligationCause::dummy(), param_env, @@ -2477,7 +2477,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { Adt(..) if ty.is_box() => Some("`Box` must be non-null".into()), FnPtr(..) => Some("function pointers must be non-null".into()), Never => Some("the `!` type has no valid value".into()), - RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) => + RawPtr(ty, _) if matches!(ty.kind(), Dynamic(..)) => // raw ptr to dyn Trait { Some("the vtable of a wide raw pointer must be non-null".into()) @@ -2493,7 +2493,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { Some("integers must be initialized".into()) } Float(_) if init == InitKind::Uninit => Some("floats must be initialized".into()), - RawPtr(_) if init == InitKind::Uninit => { + RawPtr(_, _) if init == InitKind::Uninit => { Some("raw pointers must be initialized".into()) } // Recurse and checks for some compound types. (but not unions) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 8c0e5b17fd8..64ef30039a8 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -937,7 +937,7 @@ impl<'tcx> LateContext<'tcx> { } && let Some(init) = match parent_node { hir::Node::Expr(expr) => Some(expr), - hir::Node::Local(hir::Local { init, .. }) => *init, + hir::Node::LetStmt(hir::LetStmt { init, .. }) => *init, _ => None, } { @@ -982,7 +982,7 @@ impl<'tcx> LateContext<'tcx> { } && let Some(init) = match parent_node { hir::Node::Expr(expr) => Some(expr), - hir::Node::Local(hir::Local { init, .. }) => *init, + hir::Node::LetStmt(hir::LetStmt { init, .. }) => *init, hir::Node::Item(item) => match item.kind { hir::ItemKind::Const(.., body_id) | hir::ItemKind::Static(.., body_id) => { Some(self.tcx.hir().body(body_id).value) diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index b995f38f23c..fae492f252e 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -322,10 +322,10 @@ fn structurally_same_type_impl<'tcx>( (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 + (RawPtr(a_ty, a_mutbl), RawPtr(b_ty, b_mutbl)) => { + a_mutbl == b_mutbl && structurally_same_type_impl( - seen_types, tcx, param_env, a_tymut.ty, b_tymut.ty, ckind, + seen_types, tcx, param_env, *a_ty, *b_ty, ckind, ) } (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 506716e39a1..384bd353d75 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -238,7 +238,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { self.with_lint_attrs(l.hir_id, |cx| { lint_callback!(cx, check_local, l); hir_visit::walk_local(cx, l); diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index de642f373b5..9d4d75a646a 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -105,7 +105,7 @@ const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable - fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) { if matches!(local.source, rustc_hir::LocalSource::AsyncFn) { return; } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 26fc3f20b2c..5bb2942ad4b 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -354,7 +354,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { intravisit::walk_variant(self, v); } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { self.add_id(l.hir_id); intravisit::walk_local(self, l); } @@ -428,7 +428,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<' intravisit::walk_variant(self, v); } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { self.add_id(l.hir_id); intravisit::walk_local(self, l); } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 508f3e1ec31..3e93cc0be6a 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -15,7 +15,7 @@ macro_rules! late_lint_methods { fn check_foreign_item(a: &'tcx rustc_hir::ForeignItem<'tcx>); fn check_item(a: &'tcx rustc_hir::Item<'tcx>); fn check_item_post(a: &'tcx rustc_hir::Item<'tcx>); - fn check_local(a: &'tcx rustc_hir::Local<'tcx>); + fn check_local(a: &'tcx rustc_hir::LetStmt<'tcx>); fn check_block(a: &'tcx rustc_hir::Block<'tcx>); fn check_block_post(a: &'tcx rustc_hir::Block<'tcx>); fn check_stmt(a: &'tcx rustc_hir::Stmt<'tcx>); diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs index f386db9d8db..9b938b34c00 100644 --- a/compiler/rustc_lint/src/reference_casting.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -1,7 +1,7 @@ use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_middle::ty::layout::LayoutOf as _; -use rustc_middle::ty::{self, layout::TyAndLayout, TypeAndMut}; +use rustc_middle::ty::{self, layout::TyAndLayout}; use rustc_span::sym; use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext}; @@ -153,7 +153,7 @@ fn is_cast_from_ref_to_mut_ptr<'tcx>( let end_ty = cx.typeck_results().node_type(orig_expr.hir_id); // Bail out early if the end type is **not** a mutable pointer. - if !matches!(end_ty.kind(), ty::RawPtr(TypeAndMut { ty: _, mutbl: Mutability::Mut })) { + if !matches!(end_ty.kind(), ty::RawPtr(_, Mutability::Mut)) { return None; } @@ -183,7 +183,7 @@ fn is_cast_to_bigger_memory_layout<'tcx>( ) -> Option<(TyAndLayout<'tcx>, TyAndLayout<'tcx>, Expr<'tcx>)> { let end_ty = cx.typeck_results().node_type(orig_expr.hir_id); - let ty::RawPtr(TypeAndMut { ty: inner_end_ty, mutbl: _ }) = end_ty.kind() else { + let ty::RawPtr(inner_end_ty, _) = end_ty.kind() else { return None; }; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 0ad257d02bd..5331d2fb752 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -18,10 +18,10 @@ use rustc_errors::DiagMessage; use rustc_hir as hir; use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; +use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{ self, AdtKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; -use rustc_middle::ty::{GenericArgsRef, TypeAndMut}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map; use rustc_span::symbol::sym; @@ -673,7 +673,7 @@ fn lint_wide_pointer<'tcx>( refs += 1; } match ty.kind() { - ty::RawPtr(TypeAndMut { mutbl: _, ty }) => (!ty.is_sized(cx.tcx, cx.param_env)) + ty::RawPtr(ty, _) => (!ty.is_sized(cx.tcx, cx.param_env)) .then(|| (refs, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))), _ => None, } @@ -1046,10 +1046,10 @@ fn get_nullable_type<'tcx>( } ty::Int(ty) => Ty::new_int(tcx, ty), ty::Uint(ty) => Ty::new_uint(tcx, ty), - ty::RawPtr(ty_mut) => Ty::new_ptr(tcx, ty_mut), + ty::RawPtr(ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl), // As these types are always non-null, the nullable equivalent of // `Option<T>` of these types are their raw pointer counterparts. - ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl }), + ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl), // There is no nullable equivalent for Rust's function pointers, // you must use an `Option<fn(..) -> _>` to represent it. ty::FnPtr(..) => ty, @@ -1374,7 +1374,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: Some(fluent::lint_improper_ctypes_tuple_help), }, - ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) + ty::RawPtr(ty, _) | ty::Ref(_, ty, _) if { matches!(self.mode, CItemKind::Definition) && ty.is_sized(self.cx.tcx, self.cx.param_env) @@ -1383,7 +1383,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } - ty::RawPtr(ty::TypeAndMut { ty, .. }) + ty::RawPtr(ty, _) if match ty.kind() { ty::Tuple(tuple) => tuple.is_empty(), _ => false, @@ -1392,9 +1392,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } - ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { - self.check_type_for_ffi(cache, ty) - } + ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(cache, ty), ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty), diff --git a/compiler/rustc_lint/src/unit_bindings.rs b/compiler/rustc_lint/src/unit_bindings.rs index b74430d8fa0..6222adf58ae 100644 --- a/compiler/rustc_lint/src/unit_bindings.rs +++ b/compiler/rustc_lint/src/unit_bindings.rs @@ -46,7 +46,7 @@ declare_lint! { declare_lint_pass!(UnitBindings => [UNIT_BINDINGS]); impl<'tcx> LateLintPass<'tcx> for UnitBindings { - fn check_local(&mut self, cx: &crate::LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { + fn check_local(&mut self, cx: &crate::LateContext<'tcx>, local: &'tcx hir::LetStmt<'tcx>) { // Suppress warning if user: // - explicitly ascribes a type to the pattern // - explicitly wrote `let pat = ();` diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 3e10879e241..0b757f95a99 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -295,7 +295,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { elaborate( cx.tcx, - cx.tcx.explicit_item_bounds(def).instantiate_identity_iter_copied(), + cx.tcx + .explicit_item_super_predicates(def) + .instantiate_identity_iter_copied(), ) // We only care about self bounds for the impl-trait .filter_only_self() @@ -863,7 +865,7 @@ trait UnusedDelimLint { (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true) } - Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { + Match(ref head, ..) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { let left = e.span.lo() + rustc_span::BytePos(5); (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true) } @@ -1131,7 +1133,7 @@ impl EarlyLintPass for UnusedParens { } return; } - ExprKind::Match(ref _expr, ref arm) => { + ExprKind::Match(ref _expr, ref arm, _) => { for a in arm { if let Some(body) = &a.body { self.check_unused_delims_expr( @@ -1181,7 +1183,7 @@ impl EarlyLintPass for UnusedParens { self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), + Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 03783fa9798..596da58b091 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1063,6 +1063,20 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ty::EarlyBinder::bind(&*output) } + fn get_explicit_item_super_predicates( + self, + index: DefIndex, + tcx: TyCtxt<'tcx>, + ) -> ty::EarlyBinder<&'tcx [(ty::Clause<'tcx>, Span)]> { + let lazy = self.root.tables.explicit_item_super_predicates.get(self, index); + let output = if lazy.is_default() { + &mut [] + } else { + tcx.arena.alloc_from_iter(lazy.decode((self, tcx))) + }; + ty::EarlyBinder::bind(&*output) + } + fn get_variant( self, kind: DefKind, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 1c59af51589..1aabd296641 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -206,6 +206,7 @@ impl IntoArgs for (CrateNum, SimplifiedType) { provide! { tcx, def_id, other, cdata, explicit_item_bounds => { cdata.get_explicit_item_bounds(def_id.index, tcx) } + explicit_item_super_predicates => { cdata.get_explicit_item_super_predicates(def_id.index, tcx) } explicit_predicates_of => { table } generics_of => { table } inferred_outlives_of => { table_defaulted_array } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 1bd2c88ebaa..42724f7dd2b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1491,6 +1491,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); + self.encode_explicit_item_super_predicates(def_id); self.tables .is_type_alias_impl_trait .set(def_id.index, self.tcx.is_type_alias_impl_trait(def_id)); @@ -1599,6 +1600,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds); } + fn encode_explicit_item_super_predicates(&mut self, def_id: DefId) { + debug!("EncodeContext::encode_explicit_item_super_predicates({:?})", def_id); + let bounds = self.tcx.explicit_item_super_predicates(def_id).skip_binder(); + record_defaulted_array!(self.tables.explicit_item_super_predicates[def_id] <- bounds); + } + #[instrument(level = "debug", skip(self))] fn encode_info_for_assoc_item(&mut self, def_id: DefId) { let tcx = self.tcx; @@ -1611,6 +1618,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { AssocItemContainer::TraitContainer => { if let ty::AssocKind::Type = item.kind { self.encode_explicit_item_bounds(def_id); + self.encode_explicit_item_super_predicates(def_id); } } AssocItemContainer::ImplContainer => { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 8aa31ef564f..5b0be8ac230 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -387,6 +387,7 @@ define_tables! { // corresponding DefPathHash. def_path_hashes: Table<DefIndex, u64>, explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, + explicit_item_super_predicates: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>, inherent_impls: Table<DefIndex, LazyArray<DefIndex>>, associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 96c58ef411b..9e8ee92b5d9 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" +derivative = "2.2.0" either = "1.5.0" field-offset = "0.3.5" gsgdt = "0.1.2" diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 5e74ce86007..53cb05198cd 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -166,12 +166,12 @@ impl<'hir> Map<'hir> { #[inline] pub fn items(self) -> impl Iterator<Item = ItemId> + 'hir { - self.tcx.hir_crate_items(()).items.iter().copied() + self.tcx.hir_crate_items(()).free_items.iter().copied() } #[inline] pub fn module_items(self, module: LocalModDefId) -> impl Iterator<Item = ItemId> + 'hir { - self.tcx.hir_module_items(module).items() + self.tcx.hir_module_items(module).free_items() } pub fn def_key(self, def_id: LocalDefId) -> DefKey { @@ -418,7 +418,7 @@ impl<'hir> Map<'hir> { V: Visitor<'hir>, { let krate = self.tcx.hir_crate_items(()); - walk_list!(visitor, visit_item, krate.items().map(|id| self.item(id))); + walk_list!(visitor, visit_item, krate.free_items().map(|id| self.item(id))); walk_list!(visitor, visit_trait_item, krate.trait_items().map(|id| self.trait_item(id))); walk_list!(visitor, visit_impl_item, krate.impl_items().map(|id| self.impl_item(id))); walk_list!( @@ -436,7 +436,7 @@ impl<'hir> Map<'hir> { V: Visitor<'hir>, { let module = self.tcx.hir_module_items(module); - walk_list!(visitor, visit_item, module.items().map(|id| self.item(id))); + walk_list!(visitor, visit_item, module.free_items().map(|id| self.item(id))); walk_list!(visitor, visit_trait_item, module.trait_items().map(|id| self.trait_item(id))); walk_list!(visitor, visit_impl_item, module.impl_items().map(|id| self.impl_item(id))); walk_list!( @@ -567,7 +567,7 @@ impl<'hir> Map<'hir> { } // Ignore `return`s on the first iteration Node::Expr(Expr { kind: ExprKind::Loop(..) | ExprKind::Ret(..), .. }) - | Node::Local(_) => { + | Node::LetStmt(_) => { return None; } _ => {} @@ -906,7 +906,7 @@ impl<'hir> Map<'hir> { Node::Lifetime(lifetime) => lifetime.ident.span, Node::GenericParam(param) => param.span, Node::Infer(i) => i.span, - Node::Local(local) => local.span, + Node::LetStmt(local) => local.span, Node::Crate(item) => item.spans.inner_span, Node::WhereBoundPredicate(pred) => pred.span, Node::ArrayLenInfer(inf) => inf.span, @@ -1163,7 +1163,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::Arm(_) => node_str("arm"), Node::Block(_) => node_str("block"), Node::Infer(_) => node_str("infer"), - Node::Local(_) => node_str("local"), + Node::LetStmt(_) => node_str("local"), Node::Ctor(ctor) => format!( "{id} (ctor {})", ctor.ctor_def_id().map_or("<missing path>".into(), |def_id| path_str(def_id)), @@ -1197,7 +1197,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod } = collector; return ModuleItems { submodules: submodules.into_boxed_slice(), - items: items.into_boxed_slice(), + free_items: items.into_boxed_slice(), trait_items: trait_items.into_boxed_slice(), impl_items: impl_items.into_boxed_slice(), foreign_items: foreign_items.into_boxed_slice(), @@ -1226,7 +1226,7 @@ pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { return ModuleItems { submodules: submodules.into_boxed_slice(), - items: items.into_boxed_slice(), + free_items: items.into_boxed_slice(), trait_items: trait_items.into_boxed_slice(), impl_items: impl_items.into_boxed_slice(), foreign_items: foreign_items.into_boxed_slice(), diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index f9fa8ac2f7a..28f7574f66f 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -22,7 +22,7 @@ use rustc_span::{ErrorGuaranteed, ExpnId}; #[derive(Debug, HashStable, Encodable, Decodable)] pub struct ModuleItems { submodules: Box<[OwnerId]>, - items: Box<[ItemId]>, + free_items: Box<[ItemId]>, trait_items: Box<[TraitItemId]>, impl_items: Box<[ImplItemId]>, foreign_items: Box<[ForeignItemId]>, @@ -30,14 +30,22 @@ pub struct ModuleItems { } impl ModuleItems { - pub fn items(&self) -> impl Iterator<Item = ItemId> + '_ { - self.items.iter().copied() + /// Returns all non-associated locally defined items in all modules. + /// + /// Note that this does *not* include associated items of `impl` blocks! It also does not + /// include foreign items. If you want to e.g. get all functions, use `definitions()` below. + /// + /// However, this does include the `impl` blocks themselves. + pub fn free_items(&self) -> impl Iterator<Item = ItemId> + '_ { + self.free_items.iter().copied() } pub fn trait_items(&self) -> impl Iterator<Item = TraitItemId> + '_ { self.trait_items.iter().copied() } + /// Returns all items that are associated with some `impl` block (both inherent and trait impl + /// blocks). pub fn impl_items(&self) -> impl Iterator<Item = ImplItemId> + '_ { self.impl_items.iter().copied() } @@ -47,7 +55,7 @@ impl ModuleItems { } pub fn owners(&self) -> impl Iterator<Item = OwnerId> + '_ { - self.items + self.free_items .iter() .map(|id| id.owner_id) .chain(self.trait_items.iter().map(|id| id.owner_id)) @@ -63,7 +71,7 @@ impl ModuleItems { &self, f: impl Fn(ItemId) -> Result<(), ErrorGuaranteed> + DynSend + DynSync, ) -> Result<(), ErrorGuaranteed> { - try_par_for_each_in(&self.items[..], |&id| f(id)) + try_par_for_each_in(&self.free_items[..], |&id| f(id)) } pub fn par_trait_items( diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index 0ee97a6bed0..f70ef51107f 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -11,12 +11,18 @@ /// [`span_bug`]: crate::span_bug #[macro_export] macro_rules! bug { - () => ( $crate::bug!("impossible case reached") ); - ($msg:expr) => ({ $crate::util::bug::bug_fmt(::std::format_args!($msg)) }); - ($msg:expr,) => ({ $crate::bug!($msg) }); - ($fmt:expr, $($arg:tt)+) => ({ + () => ( + $crate::bug!("impossible case reached") + ); + ($msg:expr) => ( + $crate::util::bug::bug_fmt(::std::format_args!($msg)) + ); + ($msg:expr,) => ( + $crate::bug!($msg) + ); + ($fmt:expr, $($arg:tt)+) => ( $crate::util::bug::bug_fmt(::std::format_args!($fmt, $($arg)+)) - }); + ); } /// A macro for triggering an ICE with a span. @@ -30,11 +36,15 @@ macro_rules! bug { /// [`DiagCtxt::span_delayed_bug`]: rustc_errors::DiagCtxt::span_delayed_bug #[macro_export] macro_rules! span_bug { - ($span:expr, $msg:expr) => ({ $crate::util::bug::span_bug_fmt($span, ::std::format_args!($msg)) }); - ($span:expr, $msg:expr,) => ({ $crate::span_bug!($span, $msg) }); - ($span:expr, $fmt:expr, $($arg:tt)+) => ({ + ($span:expr, $msg:expr) => ( + $crate::util::bug::span_bug_fmt($span, ::std::format_args!($msg)) + ); + ($span:expr, $msg:expr,) => ( + $crate::span_bug!($span, $msg) + ); + ($span:expr, $fmt:expr, $($arg:tt)+) => ( $crate::util::bug::span_bug_fmt($span, ::std::format_args!($fmt, $($arg)+)) - }); + ); } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 214297b9869..02185cbeacf 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -456,7 +456,7 @@ impl<'tcx> Const<'tcx> { } /// An unevaluated (potentially generic) constant used in MIR. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable)] #[derive(Hash, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct UnevaluatedConst<'tcx> { pub def: DefId, diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 645a417c322..588aa1f40d7 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -88,14 +88,13 @@ pub enum CoverageKind { /// Marks a span that might otherwise not be represented in MIR, so that /// coverage instrumentation can associate it with its enclosing block/BCB. /// - /// Only used by the `InstrumentCoverage` pass, and has no effect during - /// codegen. + /// Should be erased before codegen (at some point after `InstrumentCoverage`). SpanMarker, /// Marks its enclosing basic block with an ID that can be referred to by /// side data in [`BranchInfo`]. /// - /// Has no effect during codegen. + /// Should be erased before codegen (at some point after `InstrumentCoverage`). BlockMarker { id: BlockMarkerId }, /// Marks the point in MIR control flow represented by a coverage counter. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 4047891d769..00faa211853 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -37,9 +37,16 @@ pub trait AllocBytes: /// Create a zeroed `AllocBytes` of the specified size and alignment. /// Returns `None` if we ran out of memory on the host. fn zeroed(size: Size, _align: Align) -> Option<Self>; + + /// Gives direct access to the raw underlying storage. + /// + /// Crucially this pointer is compatible with: + /// - other pointers retunred by this method, and + /// - references returned from `deref()`, as long as there was no write. + fn as_mut_ptr(&mut self) -> *mut u8; } -// Default `bytes` for `Allocation` is a `Box<[u8]>`. +/// Default `bytes` for `Allocation` is a `Box<u8>`. impl AllocBytes for Box<[u8]> { fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, _align: Align) -> Self { Box::<[u8]>::from(slice.into()) @@ -51,6 +58,11 @@ impl AllocBytes for Box<[u8]> { let bytes = unsafe { bytes.assume_init() }; Some(bytes) } + + fn as_mut_ptr(&mut self) -> *mut u8 { + // Carefully avoiding any intermediate references. + ptr::addr_of_mut!(**self).cast() + } } /// This type represents an Allocation in the Miri/CTFE core engine. @@ -399,10 +411,6 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> /// Byte accessors. impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> { - pub fn base_addr(&self) -> *const u8 { - self.bytes.as_ptr() - } - /// This is the entirely abstraction-violating way to just grab the raw bytes without /// caring about provenance or initialization. /// @@ -452,13 +460,14 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> Ok(self.get_bytes_unchecked(range)) } - /// Just calling this already marks everything as defined and removes provenance, - /// so be sure to actually put data there! + /// This is the entirely abstraction-violating way to just get mutable access to the raw bytes. + /// Just calling this already marks everything as defined and removes provenance, so be sure to + /// actually overwrite all the data there! /// /// It is the caller's responsibility to check bounds and alignment beforehand. /// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods /// on `InterpCx` instead. - pub fn get_bytes_mut( + pub fn get_bytes_unchecked_for_overwrite( &mut self, cx: &impl HasDataLayout, range: AllocRange, @@ -469,8 +478,9 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> Ok(&mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()]) } - /// A raw pointer variant of `get_bytes_mut` that avoids invalidating existing aliases into this memory. - pub fn get_bytes_mut_ptr( + /// A raw pointer variant of `get_bytes_unchecked_for_overwrite` that avoids invalidating existing immutable aliases + /// into this memory. + pub fn get_bytes_unchecked_for_overwrite_ptr( &mut self, cx: &impl HasDataLayout, range: AllocRange, @@ -479,10 +489,19 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> self.provenance.clear(range, cx)?; assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check + // Cruciall, we go via `AllocBytes::as_mut_ptr`, not `AllocBytes::deref_mut`. let begin_ptr = self.bytes.as_mut_ptr().wrapping_add(range.start.bytes_usize()); let len = range.end().bytes_usize() - range.start.bytes_usize(); Ok(ptr::slice_from_raw_parts_mut(begin_ptr, len)) } + + /// This gives direct mutable access to the entire buffer, just exposing their internal state + /// without reseting anything. Directly exposes `AllocBytes::as_mut_ptr`. Only works if + /// `OFFSET_IS_ADDR` is true. + pub fn get_bytes_unchecked_raw_mut(&mut self) -> *mut u8 { + assert!(Prov::OFFSET_IS_ADDR); + self.bytes.as_mut_ptr() + } } /// Reading and writing. @@ -589,7 +608,8 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> }; let endian = cx.data_layout().endian; - let dst = self.get_bytes_mut(cx, range)?; + // Yes we do overwrite all the bytes in `dst`. + let dst = self.get_bytes_unchecked_for_overwrite(cx, range)?; write_target_uint(endian, dst, bytes).unwrap(); // See if we have to also store some provenance. diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 984d4687ef8..e4dce2bdc9e 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -20,6 +20,7 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::{self, CoroutineDesugaring, CoroutineKind, ImplicitSelfKind}; use rustc_hir::{self as hir, HirId}; use rustc_session::Session; +use rustc_span::source_map::Spanned; use rustc_target::abi::{FieldIdx, VariantIdx}; use polonius_engine::Atom; @@ -44,6 +45,7 @@ use std::ops::{Index, IndexMut}; use std::{iter, mem}; pub use self::query::*; +use self::visit::TyContext; pub use basic_blocks::BasicBlocks; mod basic_blocks; @@ -304,6 +306,21 @@ impl<'tcx> CoroutineInfo<'tcx> { } } +/// Some item that needs to monomorphize successfully for a MIR body to be considered well-formed. +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, HashStable, TyEncodable, TyDecodable)] +#[derive(TypeFoldable, TypeVisitable)] +pub enum MentionedItem<'tcx> { + /// A function that gets called. We don't necessarily know its precise type yet, since it can be + /// hidden behind a generic. + Fn(Ty<'tcx>), + /// A type that has its drop shim called. + Drop(Ty<'tcx>), + /// Unsizing casts might require vtables, so we have to record them. + UnsizeCast { source_ty: Ty<'tcx>, target_ty: Ty<'tcx> }, + /// A closure that is coerced to a function pointer. + Closure(Ty<'tcx>), +} + /// The lowered representation of a single function. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] pub struct Body<'tcx> { @@ -367,8 +384,24 @@ pub struct Body<'tcx> { /// Constants that are required to evaluate successfully for this MIR to be well-formed. /// We hold in this field all the constants we are not able to evaluate yet. + /// + /// This is soundness-critical, we make a guarantee that all consts syntactically mentioned in a + /// function have successfully evaluated if the function ever gets executed at runtime. pub required_consts: Vec<ConstOperand<'tcx>>, + /// Further items that were mentioned in this function and hence *may* become monomorphized, + /// depending on optimizations. We use this to avoid optimization-dependent compile errors: the + /// collector recursively traverses all "mentioned" items and evaluates all their + /// `required_consts`. + /// + /// This is *not* soundness-critical and the contents of this list are *not* a stable guarantee. + /// All that's relevant is that this set is optimization-level-independent, and that it includes + /// everything that the collector would consider "used". (For example, we currently compute this + /// set after drop elaboration, so some drop calls that can never be reached are not considered + /// "mentioned".) See the documentation of `CollectionMode` in + /// `compiler/rustc_monomorphize/src/collector.rs` for more context. + pub mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>, + /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check. /// /// Note that this does not actually mean that this body is not computable right now. @@ -445,6 +478,7 @@ impl<'tcx> Body<'tcx> { var_debug_info, span, required_consts: Vec::new(), + mentioned_items: Vec::new(), is_polymorphic: false, injection_phase: None, tainted_by_errors, @@ -474,6 +508,7 @@ impl<'tcx> Body<'tcx> { spread_arg: None, span: DUMMY_SP, required_consts: Vec::new(), + mentioned_items: Vec::new(), var_debug_info: Vec::new(), is_polymorphic: false, injection_phase: None, @@ -568,6 +603,17 @@ impl<'tcx> Body<'tcx> { } } + pub fn span_for_ty_context(&self, ty_context: TyContext) -> Span { + match ty_context { + TyContext::UserTy(span) => span, + TyContext::ReturnTy(source_info) + | TyContext::LocalDecl { source_info, .. } + | TyContext::YieldTy(source_info) + | TyContext::ResumeTy(source_info) => source_info.span, + TyContext::Location(loc) => self.source_info(loc).span, + } + } + /// Returns the return type; it always return first element from `local_decls` array. #[inline] pub fn return_ty(&self) -> Ty<'tcx> { @@ -750,7 +796,7 @@ impl<'tcx> Body<'tcx> { } match rvalue { - Rvalue::NullaryOp(NullOp::UbCheck(_), _) => { + Rvalue::NullaryOp(NullOp::UbChecks, _) => { Some((tcx.sess.opts.debug_assertions as u128, targets)) } Rvalue::Use(Operand::Constant(constant)) => { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 94751c44761..fbee4a9366f 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -944,7 +944,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { NullOp::SizeOf => write!(fmt, "SizeOf({t})"), NullOp::AlignOf => write!(fmt, "AlignOf({t})"), NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"), - NullOp::UbCheck(kind) => write!(fmt, "UbCheck({kind:?})"), + NullOp::UbChecks => write!(fmt, "UbChecks()"), } } ThreadLocalRef(did) => ty::tls::with(|tcx| { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 8bd872c1b19..731e050ca9b 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -284,8 +284,15 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16); /// order of the category, thereby influencing diagnostic output. /// /// See also `rustc_const_eval::borrow_check::constraints`. -#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)] +#[derive(derivative::Derivative)] +#[derivative( + PartialOrd, + Ord, + PartialOrd = "feature_allow_slow_enum", + Ord = "feature_allow_slow_enum" +)] pub enum ConstraintCategory<'tcx> { Return(ReturnConstraint), Yield, @@ -295,6 +302,7 @@ pub enum ConstraintCategory<'tcx> { Cast { /// Whether this is an unsizing cast and if yes, this contains the target type. /// Region variables are erased to ReErased. + #[derivative(PartialOrd = "ignore", Ord = "ignore")] unsize_to: Option<Ty<'tcx>>, }, @@ -304,7 +312,7 @@ pub enum ConstraintCategory<'tcx> { ClosureBounds, /// Contains the function type if available. - CallArgument(Option<Ty<'tcx>>), + CallArgument(#[derivative(PartialOrd = "ignore", Ord = "ignore")] Option<Ty<'tcx>>), CopyBound, SizedBound, Assignment, diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 0ab797134c0..36b7a48b2a2 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -124,6 +124,7 @@ pub enum AnalysisPhase { /// * [`TerminatorKind::FalseEdge`] /// * [`StatementKind::FakeRead`] /// * [`StatementKind::AscribeUserType`] + /// * [`StatementKind::Coverage`] with [`CoverageKind::BlockMarker`] or [`CoverageKind::SpanMarker`] /// * [`Rvalue::Ref`] with `BorrowKind::Fake` /// /// Furthermore, `Deref` projections must be the first projection within any place (if they @@ -1366,16 +1367,9 @@ pub enum NullOp<'tcx> { AlignOf, /// Returns the offset of a field OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>), - /// Returns whether we want to check for library UB or language UB at monomorphization time. - /// Both kinds of UB evaluate to `true` in codegen, and only library UB evalutes to `true` in - /// const-eval/Miri, because the interpreter has its own better checks for language UB. - UbCheck(UbKind), -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] -pub enum UbKind { - LanguageUb, - LibraryUb, + /// Returns whether we want to check for UB. + /// This returns the value of `cfg!(debug_assertions)` at monomorphization time. + UbChecks, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 0c29fe57d4f..56a0a623397 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -170,11 +170,11 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did), Rvalue::Ref(reg, bk, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; - Ty::new_ref(tcx, reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() }) + Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy()) } Rvalue::AddressOf(mutability, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; - Ty::new_ptr(tcx, ty::TypeAndMut { ty: place_ty, mutbl: mutability }) + Ty::new_ptr(tcx, place_ty, mutability) } Rvalue::Len(..) => tcx.types.usize, Rvalue::Cast(.., ty) => ty, @@ -194,7 +194,7 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { tcx.types.usize } - Rvalue::NullaryOp(NullOp::UbCheck(_), _) => tcx.types.bool, + Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool, Rvalue::Aggregate(ref ak, ref ops) => match **ak { AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64), AggregateKind::Tuple => { diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 33ee3371605..d3da49c26a2 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -234,6 +234,7 @@ trivial! { Option<rustc_middle::middle::stability::DeprecationEntry>, Option<rustc_middle::ty::Destructor>, Option<rustc_middle::ty::ImplTraitInTraitData>, + Option<rustc_middle::ty::ScalarInt>, Option<rustc_span::def_id::CrateNum>, Option<rustc_span::def_id::DefId>, Option<rustc_span::def_id::LocalDefId>, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 69d3974184d..3b1d1a04d6f 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -13,6 +13,7 @@ use rustc_query_system::query::DefIdCacheSelector; use rustc_query_system::query::{DefaultCacheSelector, SingleCacheSelector, VecCacheSelector}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use rustc_target::abi; /// Placeholder for `CrateNum`'s "local" counterpart #[derive(Copy, Clone, Debug)] @@ -502,6 +503,14 @@ impl<'tcx> Key for (DefId, Ty<'tcx>, GenericArgsRef<'tcx>, ty::ParamEnv<'tcx>) { } } +impl<'tcx> Key for (Ty<'tcx>, abi::VariantIdx) { + type CacheSelector = DefaultCacheSelector<Self>; + + fn default_span(&self, _tcx: TyCtxt<'_>) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for (ty::Predicate<'tcx>, traits::WellFormedLoc) { type CacheSelector = DefaultCacheSelector<Self>; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 9f0d2a89e6d..3984b3b61c2 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -343,11 +343,14 @@ rustc_queries! { } } - /// Returns the list of bounds that can be used for - /// `SelectionCandidate::ProjectionCandidate(_)` and - /// `ProjectionTyCandidate::TraitDef`. - /// Specifically this is the bounds written on the trait's type - /// definition, or those after the `impl` keyword + /// Returns the list of bounds that are required to be satsified + /// by a implementation or definition. For associated types, these + /// must be satisfied for an implementation to be well-formed, + /// and for opaque types, these are required to be satisfied by + /// the hidden-type of the opaque. + /// + /// Syntactially, these are the bounds written on the trait's type + /// definition, or those after the `impl` keyword for an opaque: /// /// ```ignore (incomplete) /// type X: Bound + 'lt @@ -363,7 +366,16 @@ rustc_queries! { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern - feedable + } + + /// The set of item bounds (see [`TyCtxt::explicit_item_bounds`]) that + /// share the `Self` type of the item. These are a subset of the bounds + /// that may explicitly be used for things like closure signature + /// deduction. + query explicit_item_super_predicates(key: DefId) -> ty::EarlyBinder<&'tcx [(ty::Clause<'tcx>, Span)]> { + desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + cache_on_disk_if { key.is_local() } + separate_provide_extern } /// Elaborated version of the predicates from `explicit_item_bounds`. @@ -390,6 +402,14 @@ rustc_queries! { desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } } + query item_super_predicates(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<ty::Clause<'tcx>>> { + desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } + } + + query item_non_self_assumptions(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<ty::Clause<'tcx>>> { + desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } + } + /// Look up all native libraries this crate depends on. /// These are assembled from the following places: /// - `extern` blocks (depending on their `link` attributes) @@ -1042,6 +1062,13 @@ rustc_queries! { } } + /// Computes the tag (if any) for a given type and variant. + query tag_for_variant( + key: (Ty<'tcx>, abi::VariantIdx) + ) -> Option<ty::ScalarInt> { + desc { "computing variant tag for enum" } + } + /// Evaluates a constant and returns the computed allocation. /// /// **Do not use this** directly, use the `eval_to_const_value` or `eval_to_valtree` instead. diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 96a61883cc1..f684f83a261 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -647,6 +647,7 @@ impl<'tcx> Pat<'tcx> { AscribeUserType { subpattern, .. } | Binding { subpattern: Some(subpattern), .. } | Deref { subpattern } + | DerefPattern { subpattern } | InlineConstant { subpattern, .. } => subpattern.walk_(it), Leaf { subpatterns } | Variant { subpatterns, .. } => { subpatterns.iter().for_each(|field| field.pattern.walk_(it)) @@ -762,6 +763,11 @@ pub enum PatKind<'tcx> { subpattern: Box<Pat<'tcx>>, }, + /// Deref pattern, written `box P` for now. + DerefPattern { + subpattern: Box<Pat<'tcx>>, + }, + /// One of the following: /// * `&str` (represented as a valtree), which will be handled as a string pattern and thus /// exhaustiveness checking will detect if you use the same string twice in different @@ -1172,6 +1178,9 @@ impl<'tcx> fmt::Display for Pat<'tcx> { } write!(f, "{subpattern}") } + PatKind::DerefPattern { ref subpattern } => { + write!(f, "deref!({subpattern})") + } PatKind::Constant { value } => write!(f, "{value}"), PatKind::InlineConstant { def: _, ref subpattern } => { write!(f, "{} (from inline const)", subpattern) diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 5952c296fb6..99ab006bcc0 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -229,6 +229,7 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( match &pat.kind { AscribeUserType { subpattern, ascription: _ } | Deref { subpattern } + | DerefPattern { subpattern } | Binding { subpattern: Some(subpattern), mutability: _, diff --git a/compiler/rustc_middle/src/ty/cast.rs b/compiler/rustc_middle/src/ty/cast.rs index e6558595519..50d629120ab 100644 --- a/compiler/rustc_middle/src/ty/cast.rs +++ b/compiler/rustc_middle/src/ty/cast.rs @@ -69,7 +69,7 @@ impl<'tcx> CastTy<'tcx> { ty::Uint(u) => Some(CastTy::Int(IntTy::U(u))), ty::Float(_) => Some(CastTy::Float), ty::Adt(d, _) if d.is_enum() && d.is_payloadfree() => Some(CastTy::Int(IntTy::CEnum)), - ty::RawPtr(mt) => Some(CastTy::Ptr(mt)), + ty::RawPtr(ty, mutbl) => Some(CastTy::Ptr(ty::TypeAndMut { ty, mutbl })), ty::FnPtr(..) => Some(CastTy::FnPtr), ty::Dynamic(_, _, ty::DynStar) => Some(CastTy::DynStar), _ => None, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5de2e2fb1e7..3393f444843 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -27,8 +27,8 @@ use crate::traits::solve::{ use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind, ImplPolarity, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate, - PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, - TypeVisitable, Visibility, + PredicateKind, PredicatePolarity, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, + TyKind, TyVid, TypeVisitable, Visibility, }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; @@ -1526,7 +1526,7 @@ macro_rules! nop_slice_lift { nop_slice_lift! {ty::ValTree<'a> => ty::ValTree<'tcx>} TrivialLiftImpls! { - ImplPolarity, Promoted + ImplPolarity, PredicatePolarity, Promoted } macro_rules! sty_debug_print { @@ -1828,12 +1828,12 @@ impl<'tcx> TyCtxt<'tcx> { let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind() else { return false }; let future_trait = self.require_lang_item(LangItem::Future, None); - self.explicit_item_bounds(def_id).skip_binder().iter().any(|&(predicate, _)| { + self.explicit_item_super_predicates(def_id).skip_binder().iter().any(|&(predicate, _)| { let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() else { return false; }; trait_predicate.trait_ref.def_id == future_trait - && trait_predicate.polarity == ImplPolarity::Positive + && trait_predicate.polarity == PredicatePolarity::Positive }) } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 05463b8554f..ee18647cdd8 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -91,7 +91,12 @@ pub trait IsSuggestable<'tcx>: Sized { /// inference variables to be suggestable. fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool; - fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<Self>; + fn make_suggestable( + self, + tcx: TyCtxt<'tcx>, + infer_suggestable: bool, + placeholder: Option<Ty<'tcx>>, + ) -> Option<Self>; } impl<'tcx, T> IsSuggestable<'tcx> for T @@ -103,8 +108,13 @@ where self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue() } - fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<T> { - self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok() + fn make_suggestable( + self, + tcx: TyCtxt<'tcx>, + infer_suggestable: bool, + placeholder: Option<Ty<'tcx>>, + ) -> Option<T> { + self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable, placeholder }).ok() } } @@ -559,6 +569,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IsSuggestableVisitor<'tcx> { pub struct MakeSuggestableFolder<'tcx> { tcx: TyCtxt<'tcx>, infer_suggestable: bool, + placeholder: Option<Ty<'tcx>>, } impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for MakeSuggestableFolder<'tcx> { @@ -572,19 +583,24 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for MakeSuggestableFolder<'tcx> { let t = match *t.kind() { Infer(InferTy::TyVar(_)) if self.infer_suggestable => t, - FnDef(def_id, args) => { + FnDef(def_id, args) if self.placeholder.is_none() => { Ty::new_fn_ptr(self.tcx, self.tcx.fn_sig(def_id).instantiate(self.tcx, args)) } - // FIXME(compiler-errors): We could replace these with infer, I guess. Closure(..) + | FnDef(..) | Infer(..) | Coroutine(..) | CoroutineWitness(..) | Bound(_, _) | Placeholder(_) | Error(_) => { - return Err(()); + if let Some(placeholder) = self.placeholder { + // We replace these with infer (which is passed in from an infcx). + placeholder + } else { + return Err(()); + } } Alias(Opaque, AliasTy { def_id, .. }) => { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index e15f0378846..09586a95f1c 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -32,7 +32,7 @@ impl<T> ExpectedFound<T> { pub enum TypeError<'tcx> { Mismatch, ConstnessMismatch(ExpectedFound<ty::BoundConstness>), - PolarityMismatch(ExpectedFound<ty::ImplPolarity>), + PolarityMismatch(ExpectedFound<ty::PredicatePolarity>), UnsafetyMismatch(ExpectedFound<hir::Unsafety>), AbiMismatch(ExpectedFound<abi::Abi>), Mutability, @@ -286,7 +286,7 @@ impl<'tcx> Ty<'tcx> { ty::Foreign(_) => "extern type".into(), ty::Array(..) => "array".into(), ty::Slice(_) => "slice".into(), - ty::RawPtr(_) => "raw pointer".into(), + ty::RawPtr(_, _) => "raw pointer".into(), ty::Ref(.., mutbl) => match mutbl { hir::Mutability::Mut => "mutable reference", _ => "reference", diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index adc153c4dfd..5b257cdfd86 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -120,7 +120,7 @@ pub fn simplify_type<'tcx>( ty::Str => Some(SimplifiedType::Str), ty::Array(..) => Some(SimplifiedType::Array), ty::Slice(..) => Some(SimplifiedType::Slice), - ty::RawPtr(ptr) => Some(SimplifiedType::Ptr(ptr.mutbl)), + ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)), ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { Some(principal_def_id) if !tcx.trait_is_auto(principal_def_id) => { Some(SimplifiedType::Trait(principal_def_id)) @@ -286,8 +286,10 @@ impl DeepRejectCtxt { } _ => false, }, - ty::RawPtr(obl) => match k { - ty::RawPtr(imp) => obl.mutbl == imp.mutbl && self.types_may_unify(obl.ty, imp.ty), + ty::RawPtr(obl_ty, obl_mutbl) => match *k { + ty::RawPtr(imp_ty, imp_mutbl) => { + obl_mutbl == imp_mutbl && self.types_may_unify(obl_ty, imp_ty) + } _ => false, }, ty::Dynamic(obl_preds, ..) => { diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 18cf5445e56..ca9c762611e 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -211,8 +211,8 @@ impl FlagComputation { &ty::Slice(tt) => self.add_ty(tt), - ty::RawPtr(m) => { - self.add_ty(m.ty); + &ty::RawPtr(ty, _) => { + self.add_ty(ty); } &ty::Ref(r, ty, _) => { diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 4748e961019..65574f5702b 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -10,6 +10,7 @@ use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::FiniteBitSet; use rustc_macros::HashStable; use rustc_middle::ty::normalize_erasing_regions::NormalizationError; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::Symbol; use std::assert_matches::assert_matches; @@ -172,6 +173,11 @@ impl<'tcx> Instance<'tcx> { // If this a non-generic instance, it cannot be a shared monomorphization. self.args.non_erasable_generics(tcx, self.def_id()).next()?; + // compiler_builtins cannot use upstream monomorphizations. + if tcx.is_compiler_builtins(LOCAL_CRATE) { + return None; + } + match self.def { InstanceDef::Item(def) => tcx .upstream_monomorphizations_for(def) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 11fd73c9094..66078663098 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -2,7 +2,7 @@ use crate::error::UnsupportedFnAbi; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::TyCtxtAt; use crate::ty::normalize_erasing_regions::NormalizationError; -use crate::ty::{self, ConstKind, Ty, TyCtxt, TypeVisitableExt}; +use crate::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_error_messages::DiagMessage; use rustc_errors::{ Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, @@ -328,7 +328,7 @@ impl<'tcx> SizeSkeleton<'tcx> { }; match *ty.kind() { - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { let non_zero = !ty.is_unsafe_ptr(); let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env); match tail.kind() { @@ -345,32 +345,26 @@ impl<'tcx> SizeSkeleton<'tcx> { ty::Array(inner, len) if len.ty() == tcx.types.usize && tcx.features().transmute_generic_consts => { + let len_eval = len.try_eval_target_usize(tcx, param_env); + if len_eval == Some(0) { + return Ok(SizeSkeleton::Known(Size::from_bytes(0))); + } + match SizeSkeleton::compute(inner, tcx, param_env)? { // This may succeed because the multiplication of two types may overflow // but a single size of a nested array will not. SizeSkeleton::Known(s) => { - if let Some(c) = len.try_eval_target_usize(tcx, param_env) { + if let Some(c) = len_eval { let size = s .bytes() .checked_mul(c) .ok_or_else(|| &*tcx.arena.alloc(LayoutError::SizeOverflow(ty)))?; return Ok(SizeSkeleton::Known(Size::from_bytes(size))); } - let len = tcx.expand_abstract_consts(len); - let prev = ty::Const::from_target_usize(tcx, s.bytes()); - let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, prev) else { - return Err(tcx.arena.alloc(LayoutError::SizeOverflow(ty))); - }; - Ok(SizeSkeleton::Generic(gen_size)) + Err(tcx.arena.alloc(LayoutError::Unknown(ty))) } SizeSkeleton::Pointer { .. } => Err(err), - SizeSkeleton::Generic(g) => { - let len = tcx.expand_abstract_consts(len); - let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, g) else { - return Err(tcx.arena.alloc(LayoutError::SizeOverflow(ty))); - }; - Ok(SizeSkeleton::Generic(gen_size)) - } + SizeSkeleton::Generic(_) => Err(tcx.arena.alloc(LayoutError::Unknown(ty))), } } @@ -468,56 +462,6 @@ impl<'tcx> SizeSkeleton<'tcx> { } } -/// When creating the layout for types with abstract consts in their size (i.e. [usize; 4 * N]), -/// to ensure that they have a canonical order and can be compared directly we combine all -/// constants, and sort the other terms. This allows comparison of expressions of sizes, -/// allowing for things like transmuting between types that depend on generic consts. -/// This returns `None` if multiplication of constants overflows. -fn mul_sorted_consts<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, -) -> Option<ty::Const<'tcx>> { - use crate::mir::BinOp::Mul; - - let mut work = vec![a, b]; - let mut done = vec![]; - while let Some(n) = work.pop() { - if let ConstKind::Expr(ty::Expr::Binop(Mul, l, r)) = n.kind() { - work.push(l); - work.push(r) - } else { - done.push(n); - } - } - let mut k = 1; - let mut overflow = false; - done.retain(|c| { - let Some(c) = c.try_eval_target_usize(tcx, param_env) else { - return true; - }; - let Some(next) = c.checked_mul(k) else { - overflow = true; - return false; - }; - k = next; - false - }); - if overflow { - return None; - } - if k != 1 { - done.push(ty::Const::from_target_usize(tcx, k)); - } else if k == 0 { - return Some(ty::Const::from_target_usize(tcx, 0)); - } - done.sort_unstable(); - - // create a single tree from the buffer - done.into_iter().reduce(|acc, n| ty::Const::new_expr(tcx, ty::Expr::Binop(Mul, n, acc), n.ty())) -} - pub trait HasTyCtxt<'tcx>: HasDataLayout { fn tcx(&self) -> TyCtxt<'tcx>; } @@ -803,7 +747,7 @@ where } // Potentially-fat pointers. - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { assert!(i < this.fields.count()); // Reuse the fat `*T` type as its own thin pointer data field. @@ -981,8 +925,8 @@ where let param_env = cx.param_env(); let pointee_info = match *this.ty.kind() { - ty::RawPtr(mt) if offset.bytes() == 0 => { - tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo { + ty::RawPtr(p_ty, _) if offset.bytes() == 0 => { + tcx.layout_of(param_env.and(p_ty)).ok().map(|layout| PointeeInfo { size: layout.size, align: layout.align.abi, safe: None, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6632d980bff..6ce53ccc8cd 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -280,23 +280,43 @@ pub enum ImplPolarity { Reservation, } -impl ImplPolarity { +impl fmt::Display for ImplPolarity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Positive => f.write_str("positive"), + Self::Negative => f.write_str("negative"), + Self::Reservation => f.write_str("reservation"), + } + } +} + +/// Polarity for a trait predicate. May either be negative or positive. +/// Distinguished from [`ImplPolarity`] since we never compute goals with +/// "reservation" level. +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)] +#[derive(TypeFoldable, TypeVisitable)] +pub enum PredicatePolarity { + /// `Type: Trait` + Positive, + /// `Type: !Trait` + Negative, +} + +impl PredicatePolarity { /// Flips polarity by turning `Positive` into `Negative` and `Negative` into `Positive`. - pub fn flip(&self) -> Option<ImplPolarity> { + pub fn flip(&self) -> PredicatePolarity { match self { - ImplPolarity::Positive => Some(ImplPolarity::Negative), - ImplPolarity::Negative => Some(ImplPolarity::Positive), - ImplPolarity::Reservation => None, + PredicatePolarity::Positive => PredicatePolarity::Negative, + PredicatePolarity::Negative => PredicatePolarity::Positive, } } } -impl fmt::Display for ImplPolarity { +impl fmt::Display for PredicatePolarity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Positive => f.write_str("positive"), Self::Negative => f.write_str("negative"), - Self::Reservation => f.write_str("reservation"), } } } diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 62822505fa5..d3bc7dd22e7 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -11,7 +11,7 @@ use std::cmp::Ordering; use crate::ty::visit::TypeVisitableExt; use crate::ty::{ self, AliasTy, Binder, DebruijnIndex, DebugWithInfcx, EarlyBinder, GenericArg, GenericArgs, - GenericArgsRef, ImplPolarity, Term, Ty, TyCtxt, TypeFlags, WithCachedTypeInfo, + GenericArgsRef, PredicatePolarity, Term, Ty, TyCtxt, TypeFlags, WithCachedTypeInfo, }; pub type ClauseKind<'tcx> = IrClauseKind<TyCtxt<'tcx>>; @@ -70,7 +70,7 @@ impl<'tcx> Predicate<'tcx> { polarity, })) => Some(PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { trait_ref, - polarity: polarity.flip()?, + polarity: polarity.flip(), }))), _ => None, @@ -663,7 +663,7 @@ pub struct TraitPredicate<'tcx> { /// exist via a series of predicates.) /// /// If polarity is Reserved: that's a bug. - pub polarity: ImplPolarity, + pub polarity: PredicatePolarity, } pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; @@ -693,7 +693,7 @@ impl<'tcx> PolyTraitPredicate<'tcx> { } #[inline] - pub fn polarity(self) -> ImplPolarity { + pub fn polarity(self) -> PredicatePolarity { self.skip_binder().polarity } } @@ -907,7 +907,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> { - TraitPredicate { trait_ref: self, polarity: ImplPolarity::Positive } + TraitPredicate { trait_ref: self, polarity: PredicatePolarity::Positive } } } @@ -940,7 +940,7 @@ 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, - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }) } } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 520fc1dd7aa..d9aa7f9e5c4 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -263,7 +263,7 @@ fn characteristic_def_id_of_type_cached<'a>( characteristic_def_id_of_type_cached(subty, visited) } - ty::RawPtr(mt) => characteristic_def_id_of_type_cached(mt.ty, visited), + ty::RawPtr(ty, _) => characteristic_def_id_of_type_cached(ty, visited), ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited), diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 995b439d10a..3f0a3a1a7bf 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -667,15 +667,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Int(t) => p!(write("{}", t.name_str())), ty::Uint(t) => p!(write("{}", t.name_str())), ty::Float(t) => p!(write("{}", t.name_str())), - ty::RawPtr(ref tm) => { + ty::RawPtr(ty, mutbl) => { p!(write( "*{} ", - match tm.mutbl { + match mutbl { hir::Mutability::Mut => "mut", hir::Mutability::Not => "const", } )); - p!(print(tm.ty)) + p!(print(ty)) } ty::Ref(r, ty, mutbl) => { p!("&"); @@ -995,11 +995,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // Don't print `+ Sized`, but rather `+ ?Sized` if absent. if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() { match pred.polarity { - ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => { + ty::PredicatePolarity::Positive => { has_sized_bound = true; continue; } - ty::ImplPolarity::Negative => has_negative_sized_bound = true, + ty::PredicatePolarity::Negative => has_negative_sized_bound = true, } } @@ -1020,7 +1020,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.insert_trait_and_projection( trait_ref, - ty::ImplPolarity::Positive, + ty::PredicatePolarity::Positive, Some(proj_ty), &mut traits, &mut fn_traits, @@ -1085,7 +1085,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { _ => { if entry.has_fn_once { traits - .entry((fn_once_trait_ref, ty::ImplPolarity::Positive)) + .entry((fn_once_trait_ref, ty::PredicatePolarity::Positive)) .or_default() .extend( // Group the return ty with its def id, if we had one. @@ -1095,10 +1095,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ); } if let Some(trait_ref) = entry.fn_mut_trait_ref { - traits.entry((trait_ref, ty::ImplPolarity::Positive)).or_default(); + traits.entry((trait_ref, ty::PredicatePolarity::Positive)).or_default(); } if let Some(trait_ref) = entry.fn_trait_ref { - traits.entry((trait_ref, ty::ImplPolarity::Positive)).or_default(); + traits.entry((trait_ref, ty::PredicatePolarity::Positive)).or_default(); } } } @@ -1114,7 +1114,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { self.wrap_binder(&trait_ref, |trait_ref, cx| { define_scoped_cx!(cx); - if polarity == ty::ImplPolarity::Negative { + if polarity == ty::PredicatePolarity::Negative { p!("!"); } p!(print(trait_ref.print_only_trait_name())); @@ -1223,10 +1223,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { fn insert_trait_and_projection( &mut self, trait_ref: ty::PolyTraitRef<'tcx>, - polarity: ty::ImplPolarity, + polarity: ty::PredicatePolarity, proj_ty: Option<(DefId, ty::Binder<'tcx, Term<'tcx>>)>, traits: &mut FxIndexMap< - (ty::PolyTraitRef<'tcx>, ty::ImplPolarity), + (ty::PolyTraitRef<'tcx>, ty::PredicatePolarity), FxIndexMap<DefId, ty::Binder<'tcx, Term<'tcx>>>, >, fn_traits: &mut FxIndexMap<ty::PolyTraitRef<'tcx>, OpaqueFnEntry<'tcx>>, @@ -1236,7 +1236,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // If our trait_ref is FnOnce or any of its children, project it onto the parent FnOnce // super-trait ref and record it there. // We skip negative Fn* bounds since they can't use parenthetical notation anyway. - if polarity == ty::ImplPolarity::Positive + if polarity == ty::PredicatePolarity::Positive && let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait() { // If we have a FnOnce, then insert it into @@ -1752,7 +1752,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { p!(write("{:?}", char::try_from(int).unwrap())) } // Pointer types - ty::Ref(..) | ty::RawPtr(_) | ty::FnPtr(_) => { + ty::Ref(..) | ty::RawPtr(_, _) | ty::FnPtr(_) => { let data = int.assert_bits(self.tcx().data_layout.pointer_size); self.typed_value( |this| { @@ -3139,7 +3139,7 @@ define_print_and_forward_display! { TraitPredPrintModifiersAndPath<'tcx> { p!(pretty_print_bound_constness(self.0.trait_ref)); - if let ty::ImplPolarity::Negative = self.0.polarity { + if let ty::PredicatePolarity::Negative = self.0.polarity { p!("!") } p!(print(self.0.trait_ref.print_only_trait_path())); @@ -3172,7 +3172,7 @@ define_print_and_forward_display! { ty::TraitPredicate<'tcx> { p!(print(self.trait_ref.self_ty()), ": "); p!(pretty_print_bound_constness(self.trait_ref)); - if let ty::ImplPolarity::Negative = self.polarity { + if let ty::PredicatePolarity::Negative = self.polarity { p!("!"); } p!(print(self.trait_ref.print_trait_sugared())) diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index d21f0e6385c..c66b9864e46 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -251,6 +251,7 @@ impl<'tcx> Region<'tcx> { } ty::ReError(_) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; + flags = flags | TypeFlags::HAS_ERROR; } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 990e78aff8a..cf7caafcebb 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -94,28 +94,6 @@ pub trait Relate<'tcx>: TypeFoldable<TyCtxt<'tcx>> + PartialEq + Copy { /////////////////////////////////////////////////////////////////////////// // Relate impls -pub fn relate_type_and_mut<'tcx, R: TypeRelation<'tcx>>( - relation: &mut R, - a: ty::TypeAndMut<'tcx>, - b: ty::TypeAndMut<'tcx>, - base_ty: Ty<'tcx>, -) -> RelateResult<'tcx, ty::TypeAndMut<'tcx>> { - debug!("{}.mts({:?}, {:?})", relation.tag(), a, b); - if a.mutbl != b.mutbl { - Err(TypeError::Mutability) - } else { - let mutbl = a.mutbl; - let (variance, info) = match mutbl { - hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None), - hir::Mutability::Mut => { - (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: base_ty, param_index: 0 }) - } - }; - let ty = relation.relate_with_variance(variance, info, a.ty, b.ty)?; - Ok(ty::TypeAndMut { ty, mutbl }) - } -} - #[inline] pub fn relate_args_invariantly<'tcx, R: TypeRelation<'tcx>>( relation: &mut R, @@ -465,17 +443,39 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>( Ok(Ty::new_coroutine_closure(tcx, a_id, args)) } - (&ty::RawPtr(a_mt), &ty::RawPtr(b_mt)) => { - let mt = relate_type_and_mut(relation, a_mt, b_mt, a)?; - Ok(Ty::new_ptr(tcx, mt)) + (&ty::RawPtr(a_ty, a_mutbl), &ty::RawPtr(b_ty, b_mutbl)) => { + if a_mutbl != b_mutbl { + return Err(TypeError::Mutability); + } + + let (variance, info) = match a_mutbl { + hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None), + hir::Mutability::Mut => { + (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) + } + }; + + let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; + + Ok(Ty::new_ptr(tcx, ty, a_mutbl)) } (&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => { + if a_mutbl != b_mutbl { + return Err(TypeError::Mutability); + } + + let (variance, info) = match a_mutbl { + hir::Mutability::Not => (ty::Covariant, ty::VarianceDiagInfo::None), + hir::Mutability::Mut => { + (ty::Invariant, ty::VarianceDiagInfo::Invariant { ty: a, param_index: 0 }) + } + }; + let r = relation.relate(a_r, b_r)?; - let a_mt = ty::TypeAndMut { ty: a_ty, mutbl: a_mutbl }; - let b_mt = ty::TypeAndMut { ty: b_ty, mutbl: b_mutbl }; - let mt = relate_type_and_mut(relation, a_mt, b_mt, a)?; - Ok(Ty::new_ref(tcx, r, mt)) + let ty = relation.relate_with_variance(variance, info, a_ty, b_ty)?; + + Ok(Ty::new_ref(tcx, r, ty, a_mutbl)) } (&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => { @@ -769,12 +769,12 @@ impl<'tcx> Relate<'tcx> for GenericArg<'tcx> { } } -impl<'tcx> Relate<'tcx> for ty::ImplPolarity { +impl<'tcx> Relate<'tcx> for ty::PredicatePolarity { fn relate<R: TypeRelation<'tcx>>( _relation: &mut R, - a: ty::ImplPolarity, - b: ty::ImplPolarity, - ) -> RelateResult<'tcx, ty::ImplPolarity> { + a: ty::PredicatePolarity, + b: ty::PredicatePolarity, + ) -> RelateResult<'tcx, ty::PredicatePolarity> { if a != b { Err(TypeError::PolarityMismatch(expected_found(a, b))) } else { Ok(a) } } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 4b015640f91..f14ca7ae4b7 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -560,7 +560,7 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for Ty<'tcx> { folder: &mut F, ) -> Result<Self, F::Error> { let kind = match *self.kind() { - ty::RawPtr(tm) => ty::RawPtr(tm.try_fold_with(folder)?), + ty::RawPtr(ty, mutbl) => ty::RawPtr(ty.try_fold_with(folder)?, mutbl), ty::Array(typ, sz) => ty::Array(typ.try_fold_with(folder)?, sz.try_fold_with(folder)?), ty::Slice(typ) => ty::Slice(typ.try_fold_with(folder)?), ty::Adt(tid, args) => ty::Adt(tid, args.try_fold_with(folder)?), @@ -607,7 +607,7 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for Ty<'tcx> { impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for Ty<'tcx> { fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result { match self.kind() { - ty::RawPtr(ref tm) => tm.visit_with(visitor), + ty::RawPtr(ty, _mutbl) => ty.visit_with(visitor), ty::Array(typ, sz) => { try_visit!(typ.visit_with(visitor)); sz.visit_with(visitor) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 6e0a9eb86dd..c85ee140fa4 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1587,33 +1587,38 @@ impl<'tcx> Ty<'tcx> { } #[inline] - pub fn new_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> { - Ty::new(tcx, Ref(r, tm.ty, tm.mutbl)) + pub fn new_ref( + tcx: TyCtxt<'tcx>, + r: Region<'tcx>, + ty: Ty<'tcx>, + mutbl: ty::Mutability, + ) -> Ty<'tcx> { + Ty::new(tcx, Ref(r, ty, mutbl)) } #[inline] pub fn new_mut_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - Ty::new_ref(tcx, r, TypeAndMut { ty, mutbl: hir::Mutability::Mut }) + Ty::new_ref(tcx, r, ty, hir::Mutability::Mut) } #[inline] pub fn new_imm_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - Ty::new_ref(tcx, r, TypeAndMut { ty, mutbl: hir::Mutability::Not }) + Ty::new_ref(tcx, r, ty, hir::Mutability::Not) } #[inline] - pub fn new_ptr(tcx: TyCtxt<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> { - Ty::new(tcx, RawPtr(tm)) + pub fn new_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, mutbl: ty::Mutability) -> Ty<'tcx> { + Ty::new(tcx, ty::RawPtr(ty, mutbl)) } #[inline] pub fn new_mut_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - Ty::new_ptr(tcx, TypeAndMut { ty, mutbl: hir::Mutability::Mut }) + Ty::new_ptr(tcx, ty, hir::Mutability::Mut) } #[inline] pub fn new_imm_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - Ty::new_ptr(tcx, TypeAndMut { ty, mutbl: hir::Mutability::Not }) + Ty::new_ptr(tcx, ty, hir::Mutability::Not) } #[inline] @@ -1910,7 +1915,7 @@ impl<'tcx> Ty<'tcx> { pub fn is_array_slice(self) -> bool { match self.kind() { Slice(_) => true, - RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => matches!(ty.kind(), Slice(_)), + ty::RawPtr(ty, _) | Ref(_, ty, _) => matches!(ty.kind(), Slice(_)), _ => false, } } @@ -1964,11 +1969,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn is_mutable_ptr(self) -> bool { - matches!( - self.kind(), - RawPtr(TypeAndMut { mutbl: hir::Mutability::Mut, .. }) - | Ref(_, _, hir::Mutability::Mut) - ) + matches!(self.kind(), RawPtr(_, hir::Mutability::Mut) | Ref(_, _, hir::Mutability::Mut)) } /// Get the mutability of the reference or `None` when not a reference @@ -1982,7 +1983,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn is_unsafe_ptr(self) -> bool { - matches!(self.kind(), RawPtr(_)) + matches!(self.kind(), RawPtr(_, _)) } /// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer). @@ -2038,7 +2039,7 @@ impl<'tcx> Ty<'tcx> { | Uint(_) | FnDef(..) | FnPtr(_) - | RawPtr(_) + | RawPtr(_, _) | Infer(IntVar(_) | FloatVar(_)) ) } @@ -2174,7 +2175,7 @@ impl<'tcx> Ty<'tcx> { Some(TypeAndMut { ty: self.boxed_ty(), mutbl: hir::Mutability::Not }) } Ref(_, ty, mutbl) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }), - RawPtr(mt) if explicit => Some(*mt), + RawPtr(ty, mutbl) if explicit => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }), _ => None, } } @@ -2293,7 +2294,7 @@ impl<'tcx> Ty<'tcx> { | ty::Str | ty::Array(..) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(..) @@ -2636,7 +2637,7 @@ impl<'tcx> Ty<'tcx> { | Str | Array(_, _) | Slice(_) - | RawPtr(_) + | RawPtr(_, _) | Ref(_, _, _) | FnDef(_, _) | FnPtr(_) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index a6526b06851..f74bba134ab 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -682,6 +682,9 @@ impl<'tcx> TyCtxt<'tcx> { /// Return the set of types that should be taken into account when checking /// trait bounds on a coroutine's internal state. + // FIXME(compiler-errors): We should remove this when the old solver goes away; + // and all other usages of this function should go through `bound_coroutine_hidden_types` + // instead. pub fn coroutine_hidden_types( self, def_id: DefId, @@ -694,6 +697,33 @@ impl<'tcx> TyCtxt<'tcx> { .map(|decl| ty::EarlyBinder::bind(decl.ty)) } + /// Return the set of types that should be taken into account when checking + /// trait bounds on a coroutine's internal state. This properly replaces + /// `ReErased` with new existential bound lifetimes. + pub fn bound_coroutine_hidden_types( + self, + def_id: DefId, + ) -> impl Iterator<Item = ty::EarlyBinder<ty::Binder<'tcx, Ty<'tcx>>>> { + let coroutine_layout = self.mir_coroutine_witnesses(def_id); + coroutine_layout + .as_ref() + .map_or_else(|| [].iter(), |l| l.field_tys.iter()) + .filter(|decl| !decl.ignore_for_traits) + .map(move |decl| { + let mut vars = vec![]; + let ty = self.fold_regions(decl.ty, |re, debruijn| { + assert_eq!(re, self.lifetimes.re_erased); + let var = ty::BoundVar::from_usize(vars.len()); + vars.push(ty::BoundVariableKind::Region(ty::BrAnon)); + ty::Region::new_bound(self, debruijn, ty::BoundRegion { var, kind: ty::BrAnon }) + }); + ty::EarlyBinder::bind(ty::Binder::bind_with_vars( + ty, + self.mk_bound_variable_kinds(&vars), + )) + }) + } + /// Expands the given impl trait type, stopping if the type is recursive. #[instrument(skip(self), level = "debug", ret)] pub fn try_expand_impl_trait_type( @@ -998,8 +1028,10 @@ impl<'tcx> OpaqueTypeExpander<'tcx> { Some(expanded_ty) => *expanded_ty, None => { if matches!(self.inspect_coroutine_fields, InspectCoroutineFields::Yes) { - for bty in self.tcx.coroutine_hidden_types(def_id) { - let hidden_ty = bty.instantiate(self.tcx, args); + for bty in self.tcx.bound_coroutine_hidden_types(def_id) { + let hidden_ty = self.tcx.instantiate_bound_regions_with_erased( + bty.instantiate(self.tcx, args), + ); self.fold_ty(hidden_ty); } } @@ -1205,7 +1237,7 @@ impl<'tcx> Ty<'tcx> { | ty::Str | ty::Never | ty::Ref(..) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::FnDef(..) | ty::Error(_) | ty::FnPtr(_) => true, @@ -1245,7 +1277,7 @@ impl<'tcx> Ty<'tcx> { | ty::Str | ty::Never | ty::Ref(..) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::FnDef(..) | ty::Error(_) | ty::FnPtr(_) => true, @@ -1369,7 +1401,7 @@ impl<'tcx> Ty<'tcx> { ty::Ref(..) | ty::Array(..) | ty::Slice(_) | ty::Tuple(..) => true, // Raw pointers use bitwise comparison. - ty::RawPtr(_) | ty::FnPtr(_) => true, + ty::RawPtr(_, _) | ty::FnPtr(_) => true, // Floating point numbers are not `Eq`. ty::Float(_) => false, @@ -1462,7 +1494,7 @@ impl<'tcx> ExplicitSelf<'tcx> { match *self_arg_ty.kind() { _ if is_self_ty(self_arg_ty) => ByValue, ty::Ref(region, ty, mutbl) if is_self_ty(ty) => ByReference(region, mutbl), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) if is_self_ty(ty) => ByRawPointer(mutbl), + ty::RawPtr(ty, mutbl) if is_self_ty(ty) => ByRawPointer(mutbl), ty::Adt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox, _ => Other, } @@ -1487,7 +1519,7 @@ pub fn needs_drop_components<'tcx>( | ty::FnDef(..) | ty::FnPtr(_) | ty::Char - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::Str => Ok(SmallVec::new()), @@ -1542,7 +1574,7 @@ pub fn is_trivially_const_drop(ty: Ty<'_>) -> bool { | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::Str - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 46c26241c3e..9e7bf980237 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -158,8 +158,8 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>) ty::Slice(ty) => { stack.push(ty.into()); } - ty::RawPtr(mt) => { - stack.push(mt.ty.into()); + ty::RawPtr(ty, _) => { + stack.push(ty.into()); } ty::Ref(lt, ty, _) => { stack.push(ty.into()); diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index 288b787798b..109ffedec55 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -56,6 +56,7 @@ pub(super) fn build_custom_mir<'tcx>( var_debug_info: Vec::new(), span, required_consts: Vec::new(), + mentioned_items: Vec::new(), is_polymorphic: false, tainted_by_errors: None, injection_phase: None, diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index d2cbbf9be32..f4f452d474f 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -879,6 +879,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.visit_primary_bindings(subpattern, pattern_user_ty.deref(), f); } + PatKind::DerefPattern { ref subpattern } => { + self.visit_primary_bindings(subpattern, UserTypeProjections::none(), f); + } + PatKind::AscribeUserType { ref subpattern, ascription: thir::Ascription { ref annotation, variance: _ }, diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index d0d49c13f13..1148cd19a01 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -256,6 +256,12 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { subpairs.push(MatchPair::new(place_builder, subpattern, cx)); default_irrefutable() } + + PatKind::DerefPattern { .. } => { + // FIXME(deref_patterns) + // Treat it like a wildcard for now. + default_irrefutable() + } }; MatchPair { place, test_case, subpairs, pattern } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 1ce8da162bf..e04fe31a76f 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -250,6 +250,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { | PatKind::Variant { .. } | PatKind::Leaf { .. } | PatKind::Deref { .. } + | PatKind::DerefPattern { .. } | PatKind::Range { .. } | PatKind::Slice { .. } | PatKind::Array { .. } => { @@ -310,7 +311,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } visit::walk_pat(self, pat); } - PatKind::Deref { .. } => { + PatKind::Deref { .. } | PatKind::DerefPattern { .. } => { let old_inside_adt = std::mem::replace(&mut self.inside_adt, false); visit::walk_pat(self, pat); self.inside_adt = old_inside_adt; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 52d32a3b626..1e508ffc1e7 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -125,11 +125,7 @@ impl<'tcx> Cx<'tcx> { expr = Expr { temp_lifetime, - ty: Ty::new_ref( - self.tcx, - deref.region, - ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }, - ), + ty: Ty::new_ref(self.tcx, deref.region, expr.ty, deref.mutbl), span, kind: ExprKind::Borrow { borrow_kind: deref.mutbl.to_borrow_kind(), @@ -1021,7 +1017,7 @@ impl<'tcx> Cx<'tcx> { let ty::Ref(region, _, mutbl) = *self.thir[args[0]].ty.kind() else { span_bug!(span, "overloaded_place: receiver is not a reference"); }; - let ref_ty = Ty::new_ref(self.tcx, region, ty::TypeAndMut { ty: place_ty, mutbl }); + let ref_ty = Ty::new_ref(self.tcx, region, place_ty, mutbl); // construct the complete expression `foo()` for the overloaded call, // which will yield the &T type diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index cfa853417ca..434ed16d5c6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -481,6 +481,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { // when the iterator is an uninhabited type. unreachable_code will trigger instead. hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {} hir::MatchSource::ForLoopDesugar + | hir::MatchSource::Postfix | hir::MatchSource::Normal | hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report), // Unreachable patterns in try and await expressions occur when one of diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 03eda9a9322..0a7e9653377 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -257,6 +257,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { return self.lower_path(qpath, pat.hir_id, pat.span); } + hir::PatKind::Deref(subpattern) => { + PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern) } + } hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => { PatKind::Deref { subpattern: self.lower_pattern(subpattern) } } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index d53704f89e7..16c4248a159 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -688,6 +688,12 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_pat(subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } + PatKind::DerefPattern { subpattern } => { + print_indented!(self, "DerefPattern { ", depth_lvl + 1); + print_indented!(self, "subpattern:", depth_lvl + 2); + self.print_pat(subpattern, depth_lvl + 2); + print_indented!(self, "}", depth_lvl + 1); + } PatKind::Constant { value } => { print_indented!(self, "Constant {", depth_lvl + 1); print_indented!(self, format!("value: {:?}", value), depth_lvl + 2); diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 1b2f2cd9477..256add3153c 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -1,4 +1,3 @@ -use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_index::Idx; use rustc_middle::mir::patch::MirPatch; @@ -629,11 +628,7 @@ where let drop_fn = tcx.associated_item_def_ids(drop_trait)[0]; let ty = self.place_ty(self.place); - let ref_ty = Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }, - ); + let ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty); let ref_place = self.new_temp(ref_ty); let unit_temp = Place::from(self.new_temp(Ty::new_unit(tcx))); @@ -700,7 +695,7 @@ where let move_ = |place: Place<'tcx>| Operand::Move(place); let tcx = self.tcx(); - let ptr_ty = Ty::new_ptr(tcx, ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut }); + let ptr_ty = Ty::new_mut_ptr(tcx, ety); let ptr = Place::from(self.new_temp(ptr_ty)); let can_go = Place::from(self.new_temp(tcx.types.bool)); let one = self.constant_usize(1); diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 0f900e6a557..e73d945e0bb 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -194,7 +194,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -433,7 +433,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | Rvalue::Discriminant(..) | Rvalue::Len(..) | Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbCheck(_), + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbChecks, _, ) => {} } diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 2b2af6ee7da..0e85f859ab2 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -843,7 +843,7 @@ impl Map { self.value_count += 1; } - if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ty::TypeAndMut { ty: ref_ty, .. }) = ty.kind() + if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.kind() && let ty::Slice(..) = ref_ty.kind() { assert!(self.places[place].value_index.is_none(), "slices are not scalars"); diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index 9eec724ef21..b71c5894ff7 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::{ interpret::Scalar, visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}, }; -use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeAndMut}; +use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; use rustc_session::Session; pub struct CheckAlignment; @@ -157,7 +157,7 @@ fn insert_alignment_check<'tcx>( new_block: BasicBlock, ) { // Cast the pointer to a *const () - let const_raw_ptr = Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not }); + let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr); let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into(); block_data diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 5b4bc4fa134..aaf2035fc21 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -5,15 +5,20 @@ //! - [`AscribeUserType`] //! - [`FakeRead`] //! - [`Assign`] statements with a [`Fake`] borrow +//! - [`Coverage`] statements of kind [`BlockMarker`] or [`SpanMarker`] //! //! [`AscribeUserType`]: rustc_middle::mir::StatementKind::AscribeUserType //! [`Assign`]: rustc_middle::mir::StatementKind::Assign //! [`FakeRead`]: rustc_middle::mir::StatementKind::FakeRead //! [`Nop`]: rustc_middle::mir::StatementKind::Nop //! [`Fake`]: rustc_middle::mir::BorrowKind::Fake +//! [`Coverage`]: rustc_middle::mir::StatementKind::Coverage +//! [`BlockMarker`]: rustc_middle::mir::coverage::CoverageKind::BlockMarker +//! [`SpanMarker`]: rustc_middle::mir::coverage::CoverageKind::SpanMarker use crate::MirPass; -use rustc_middle::mir::{Body, BorrowKind, Rvalue, StatementKind, TerminatorKind}; +use rustc_middle::mir::coverage::CoverageKind; +use rustc_middle::mir::{Body, BorrowKind, Coverage, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::TyCtxt; pub struct CleanupPostBorrowck; @@ -25,6 +30,12 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck { match statement.kind { StatementKind::AscribeUserType(..) | StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Fake, _))) + | StatementKind::Coverage(box Coverage { + // These kinds of coverage statements are markers inserted during + // MIR building, and are not needed after InstrumentCoverage. + kind: CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, + .. + }) | StatementKind::FakeRead(..) => statement.make_nop(), _ => (), } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 0d18d4fd69e..f0a13f66555 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -570,11 +570,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { fn make_coroutine_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let coroutine_ty = body.local_decls.raw[1].ty; - let ref_coroutine_ty = Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty: coroutine_ty, mutbl: Mutability::Mut }, - ); + let ref_coroutine_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty); // Replace the by value coroutine argument body.local_decls.raw[1].ty = ref_coroutine_ty; @@ -1265,10 +1261,8 @@ fn create_coroutine_drop_shim<'tcx>( make_coroutine_state_argument_indirect(tcx, &mut body); // Change the coroutine argument from &mut to *mut - body.local_decls[SELF_ARG] = LocalDecl::with_source_info( - Ty::new_ptr(tcx, ty::TypeAndMut { ty: coroutine_ty, mutbl: hir::Mutability::Mut }), - source_info, - ); + body.local_decls[SELF_ARG] = + LocalDecl::with_source_info(Ty::new_mut_ptr(tcx, coroutine_ty), source_info); // Make sure we remove dead blocks to remove // unrelated code from the resume part of the function diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 000b96ee801..e0bbd582d88 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -64,12 +64,9 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { let mut by_move_body = body.clone(); MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body); dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); - by_move_body.source = mir::MirSource { - instance: InstanceDef::CoroutineKindShim { - coroutine_def_id: coroutine_def_id.to_def_id(), - }, - promoted: None, - }; + by_move_body.source = mir::MirSource::from_instance(InstanceDef::CoroutineKindShim { + coroutine_def_id: coroutine_def_id.to_def_id(), + }); body.coroutine.as_mut().unwrap().by_move_body = Some(by_move_body); } } diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 9a1d8bae6b4..69dc4f2ddea 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -1,13 +1,12 @@ +use std::fmt::{self, Debug}; + use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::WithNumNodes; -use rustc_index::bit_set::BitSet; use rustc_index::IndexVec; -use rustc_middle::mir::coverage::*; +use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op}; -use super::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops}; - -use std::fmt::{self, Debug}; +use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, TraverseCoverageGraphWithLoops}; /// The coverage counter or counter expression associated with a particular /// BCB node or BCB edge. @@ -18,10 +17,6 @@ pub(super) enum BcbCounter { } impl BcbCounter { - fn is_expression(&self) -> bool { - matches!(self, Self::Expression { .. }) - } - pub(super) fn as_term(&self) -> CovTerm { match *self { BcbCounter::Counter { id, .. } => CovTerm::Counter(id), @@ -60,10 +55,6 @@ pub(super) struct CoverageCounters { /// We currently don't iterate over this map, but if we do in the future, /// switch it back to `FxIndexMap` to avoid query stability hazards. bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>, - /// Tracks which BCBs have a counter associated with some incoming edge. - /// Only used by assertions, to verify that BCBs with incoming edge - /// counters do not have their own physical counters (expressions are allowed). - bcb_has_incoming_edge_counters: BitSet<BasicCoverageBlock>, /// Table of expression data, associating each expression ID with its /// corresponding operator (+ or -) and its LHS/RHS operands. expressions: IndexVec<ExpressionId, Expression>, @@ -83,7 +74,6 @@ impl CoverageCounters { counter_increment_sites: IndexVec::new(), bcb_counters: IndexVec::from_elem_n(None, num_bcbs), bcb_edge_counters: FxHashMap::default(), - bcb_has_incoming_edge_counters: BitSet::new_empty(num_bcbs), expressions: IndexVec::new(), }; @@ -122,14 +112,6 @@ impl CoverageCounters { } fn set_bcb_counter(&mut self, bcb: BasicCoverageBlock, counter_kind: BcbCounter) -> BcbCounter { - assert!( - // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also - // have an expression (to be injected into an existing `BasicBlock` represented by this - // `BasicCoverageBlock`). - counter_kind.is_expression() || !self.bcb_has_incoming_edge_counters.contains(bcb), - "attempt to add a `Counter` to a BCB target with existing incoming edge counters" - ); - if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) { bug!( "attempt to set a BasicCoverageBlock coverage counter more than once; \ @@ -146,19 +128,6 @@ impl CoverageCounters { to_bcb: BasicCoverageBlock, counter_kind: BcbCounter, ) -> BcbCounter { - // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also - // have an expression (to be injected into an existing `BasicBlock` represented by this - // `BasicCoverageBlock`). - if let Some(node_counter) = self.bcb_counter(to_bcb) - && !node_counter.is_expression() - { - bug!( - "attempt to add an incoming edge counter from {from_bcb:?} \ - when the target BCB already has {node_counter:?}" - ); - } - - self.bcb_has_incoming_edge_counters.insert(to_bcb); if let Some(replaced) = self.bcb_edge_counters.insert((from_bcb, to_bcb), counter_kind) { bug!( "attempt to set an edge counter more than once; from_bcb: \ diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 3389305e7ee..3e9c1459f1c 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -2,52 +2,22 @@ //! //! Currently, this pass only propagates scalar values. -use rustc_const_eval::interpret::{ - HasStaticRootDefId, ImmTy, Immediate, InterpCx, OpTy, PlaceTy, PointerArithmetic, Projectable, -}; +use rustc_const_eval::const_eval::{throw_machine_stop_str, DummyMachine}; +use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, PlaceTy, Projectable}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; -use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult, Scalar}; +use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; -use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::value_analysis::{ Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, }; use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor}; -use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; use rustc_target::abi::{Abi, FieldIdx, Size, VariantIdx, FIRST_VARIANT}; -/// Macro for machine-specific `InterpError` without allocation. -/// (These will never be shown to the user, but they help diagnose ICEs.) -pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{ - // We make a new local type for it. The type itself does not carry any information, - // but its vtable (for the `MachineStopType` trait) does. - #[derive(Debug)] - struct Zst; - // Printing this type shows the desired string. - impl std::fmt::Display for Zst { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, $($tt)*) - } - } - - impl rustc_middle::mir::interpret::MachineStopType for Zst { - fn diagnostic_message(&self) -> rustc_errors::DiagMessage { - self.to_string().into() - } - - fn add_args( - self: Box<Self>, - _: &mut dyn FnMut(rustc_errors::DiagArgName, rustc_errors::DiagArgValue), - ) {} - } - throw_machine_stop!(Zst) -}} - // These constants are somewhat random guesses and have not been optimized. // If `tcx.sess.mir_opt_level() >= 4`, we ignore the limits (this can become very expensive). const BLOCK_LIMIT: usize = 100; @@ -888,165 +858,3 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> { } } } - -pub(crate) struct DummyMachine; - -impl HasStaticRootDefId for DummyMachine { - fn static_def_id(&self) -> Option<rustc_hir::def_id::LocalDefId> { - None - } -} - -impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for DummyMachine { - rustc_const_eval::interpret::compile_time_machine!(<'mir, 'tcx>); - type MemoryKind = !; - const PANIC_ON_ALLOC_FAIL: bool = true; - - #[inline(always)] - fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { - false // no reason to enforce alignment - } - - fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool { - false - } - - fn before_access_global( - _tcx: TyCtxtAt<'tcx>, - _machine: &Self, - _alloc_id: AllocId, - alloc: ConstAllocation<'tcx>, - _static_def_id: Option<DefId>, - is_write: bool, - ) -> InterpResult<'tcx> { - if is_write { - throw_machine_stop_str!("can't write to global"); - } - - // If the static allocation is mutable, then we can't const prop it as its content - // might be different at runtime. - if alloc.inner().mutability.is_mut() { - throw_machine_stop_str!("can't access mutable globals in ConstProp"); - } - - Ok(()) - } - - fn find_mir_or_eval_fn( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _abi: rustc_target::spec::abi::Abi, - _args: &[rustc_const_eval::interpret::FnArg<'tcx, Self::Provenance>], - _destination: &rustc_const_eval::interpret::MPlaceTy<'tcx, Self::Provenance>, - _target: Option<BasicBlock>, - _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> { - unimplemented!() - } - - fn panic_nounwind( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _msg: &str, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn call_intrinsic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _instance: ty::Instance<'tcx>, - _args: &[rustc_const_eval::interpret::OpTy<'tcx, Self::Provenance>], - _destination: &rustc_const_eval::interpret::MPlaceTy<'tcx, Self::Provenance>, - _target: Option<BasicBlock>, - _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn assert_panic( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _msg: &rustc_middle::mir::AssertMessage<'tcx>, - _unwind: UnwindAction, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn binary_ptr_op( - ecx: &InterpCx<'mir, 'tcx, Self>, - bin_op: BinOp, - left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, - right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>, - ) -> interpret::InterpResult<'tcx, (ImmTy<'tcx, Self::Provenance>, bool)> { - use rustc_middle::mir::BinOp::*; - Ok(match bin_op { - Eq | Ne | Lt | Le | Gt | Ge => { - // Types can differ, e.g. fn ptrs with different `for`. - assert_eq!(left.layout.abi, right.layout.abi); - let size = ecx.pointer_size(); - // Just compare the bits. ScalarPairs are compared lexicographically. - // We thus always compare pairs and simply fill scalars up with 0. - // If the pointer has provenance, `to_bits` will return `Err` and we bail out. - let left = match **left { - Immediate::Scalar(l) => (l.to_bits(size)?, 0), - Immediate::ScalarPair(l1, l2) => (l1.to_bits(size)?, l2.to_bits(size)?), - Immediate::Uninit => panic!("we should never see uninit data here"), - }; - let right = match **right { - Immediate::Scalar(r) => (r.to_bits(size)?, 0), - Immediate::ScalarPair(r1, r2) => (r1.to_bits(size)?, r2.to_bits(size)?), - Immediate::Uninit => panic!("we should never see uninit data here"), - }; - let res = match bin_op { - Eq => left == right, - Ne => left != right, - Lt => left < right, - Le => left <= right, - Gt => left > right, - Ge => left >= right, - _ => bug!(), - }; - (ImmTy::from_bool(res, *ecx.tcx), false) - } - - // Some more operations are possible with atomics. - // The return value always has the provenance of the *left* operand. - Add | Sub | BitOr | BitAnd | BitXor => { - throw_machine_stop_str!("pointer arithmetic is not handled") - } - - _ => span_bug!(ecx.cur_span(), "Invalid operator on pointers: {:?}", bin_op), - }) - } - - fn expose_ptr( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _ptr: interpret::Pointer<Self::Provenance>, - ) -> interpret::InterpResult<'tcx> { - unimplemented!() - } - - fn init_frame_extra( - _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _frame: rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance>, - ) -> interpret::InterpResult< - 'tcx, - rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, - > { - unimplemented!() - } - - fn stack<'a>( - _ecx: &'a InterpCx<'mir, 'tcx, Self>, - ) -> &'a [rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] - { - // Return an empty stack instead of panicking, as `cur_span` uses it to evaluate constants. - &[] - } - - fn stack_mut<'a>( - _ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - ) -> &'a mut Vec< - rustc_const_eval::interpret::Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>, - > { - unimplemented!() - } -} diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index e935dc7f5eb..30b1ca67800 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -121,7 +121,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { fn is_fn_ref(ty: Ty<'tcx>) -> Option<(DefId, GenericArgsRef<'tcx>)> { let referent_ty = match ty.kind() { ty::Ref(_, referent_ty, _) => Some(referent_ty), - ty::RawPtr(ty_and_mut) => Some(&ty_and_mut.ty), + ty::RawPtr(referent_ty, _) => Some(referent_ty), _ => None, }; referent_ty diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 87dff49e0be..59d6d89cf1f 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -82,6 +82,7 @@ //! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const` //! that contain `AllocId`s. +use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{intern_const_alloc_for_constprop, MemoryKind}; use rustc_const_eval::interpret::{ImmTy, InterpCx, OpTy, Projectable, Scalar}; use rustc_data_structures::fx::FxIndexSet; @@ -94,14 +95,13 @@ use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; use rustc_middle::mir::*; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeAndMut}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::DefId; use rustc_span::DUMMY_SP; use rustc_target::abi::{self, Abi, Size, VariantIdx, FIRST_VARIANT}; use smallvec::SmallVec; use std::borrow::Cow; -use crate::dataflow_const_prop::DummyMachine; use crate::ssa::{AssignedValue, SsaLocals}; use either::Either; @@ -131,7 +131,7 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |local, value, location| { let value = match value { // We do not know anything of this assigned value. - AssignedValue::Arg | AssignedValue::Terminator(_) => None, + AssignedValue::Arg | AssignedValue::Terminator => None, // Try to get some insight. AssignedValue::Rvalue(rvalue) => { let value = state.simplify_rvalue(rvalue, location); @@ -451,11 +451,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { AddressKind::Ref(bk) => Ty::new_ref( self.tcx, self.tcx.lifetimes.re_erased, - ty::TypeAndMut { ty: mplace.layout.ty, mutbl: bk.to_mutbl_lossy() }, + mplace.layout.ty, + bk.to_mutbl_lossy(), ), - AddressKind::Address(mutbl) => { - Ty::new_ptr(self.tcx, TypeAndMut { ty: mplace.layout.ty, mutbl }) - } + AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl), }; let layout = self.ecx.layout_of(ty).ok()?; ImmTy::from_immediate(pointer, layout).into() @@ -488,7 +487,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { NullOp::OffsetOf(fields) => { layout.offset_of_subfield(&self.ecx, fields.iter()).bytes() } - NullOp::UbCheck(_) => return None, + NullOp::UbChecks => return None, }; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let imm = ImmTy::try_from_uint(val, usize_layout)?; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index f6a0945c222..78c0615b165 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -213,6 +213,7 @@ impl<'tcx> Inliner<'tcx> { MirPhase::Runtime(RuntimePhase::Optimized), self.param_env, &callee_body, + &caller_body, ) .is_empty() { @@ -565,7 +566,8 @@ impl<'tcx> Inliner<'tcx> { mut callee_body: Body<'tcx>, ) { let terminator = caller_body[callsite.block].terminator.take().unwrap(); - let TerminatorKind::Call { args, destination, unwind, target, .. } = terminator.kind else { + let TerminatorKind::Call { func, args, destination, unwind, target, .. } = terminator.kind + else { bug!("unexpected terminator kind {:?}", terminator.kind); }; @@ -717,6 +719,24 @@ impl<'tcx> Inliner<'tcx> { Const::Val(..) | Const::Unevaluated(..) => true, }, )); + // Now that we incorporated the callee's `required_consts`, we can remove the callee from + // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does + // some extra work here to save the monomorphization collector work later. It helps a lot, + // since monomorphization can avoid a lot of work when the "mentioned items" are similar to + // the actually used items. By doing this we can entirely avoid visiting the callee! + // We need to reconstruct the `required_item` for the callee so that we can find and + // remove it. + let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx)); + if let Some(idx) = + caller_body.mentioned_items.iter().position(|item| item.node == callee_item) + { + // We found the callee, so remove it and add its items instead. + caller_body.mentioned_items.remove(idx); + caller_body.mentioned_items.extend(callee_body.mentioned_items); + } else { + // If we can't find the callee, there's no point in adding its items. + // Probably it already got removed by being inlined elsewhere in the same function. + } } fn make_call_args( diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 116d6f48456..a458297210d 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -36,6 +36,7 @@ //! cost by `MAX_COST`. use rustc_arena::DroplessArena; +use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable}; use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::BitSet; @@ -50,7 +51,6 @@ use rustc_span::DUMMY_SP; use rustc_target::abi::{TagEncoding, Variants}; use crate::cost_checker::CostChecker; -use crate::dataflow_const_prop::DummyMachine; pub struct JumpThreading; diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index f19b78a3a5c..a20958e74df 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -6,6 +6,7 @@ use std::fmt::Debug; +use rustc_const_eval::const_eval::DummyMachine; use rustc_const_eval::interpret::{ format_interp_error, ImmTy, InterpCx, InterpResult, Projectable, Scalar, }; @@ -20,7 +21,6 @@ use rustc_middle::ty::{self, ConstInt, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisi use rustc_span::Span; use rustc_target::abi::{Abi, FieldIdx, HasDataLayout, Size, TargetDataLayout, VariantIdx}; -use crate::dataflow_const_prop::DummyMachine; use crate::errors::{AssertLint, AssertLintKind}; use crate::MirLint; @@ -639,7 +639,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { NullOp::OffsetOf(fields) => { op_layout.offset_of_subfield(self, fields.iter()).bytes() } - NullOp::UbCheck(_) => return None, + NullOp::UbChecks => return None, }; ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 5d89015d61c..24bc263e5a7 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -88,6 +88,7 @@ mod lint; mod lower_intrinsics; mod lower_slice_len; mod match_branches; +mod mentioned_items; mod multiple_return_terminators; mod normalize_array_len; mod nrvo; @@ -565,6 +566,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { tcx, body, &[ + // Before doing anything, remember which items are being mentioned so that the set of items + // visited does not depend on the optimization level. + &mentioned_items::MentionedItems, + // Add some UB checks before any UB gets optimized away. &check_alignment::CheckAlignment, // Before inlining: trim down MIR with passes to reduce inlining work. diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 1bab240ef50..7d4c1b9c21a 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -20,30 +20,13 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::unreachable => { terminator.kind = TerminatorKind::Unreachable; } - sym::check_language_ub => { + sym::ub_checks => { let target = target.unwrap(); block.statements.push(Statement { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::NullaryOp( - NullOp::UbCheck(UbKind::LanguageUb), - tcx.types.bool, - ), - ))), - }); - terminator.kind = TerminatorKind::Goto { target }; - } - sym::check_library_ub => { - let target = target.unwrap(); - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::NullaryOp( - NullOp::UbCheck(UbKind::LibraryUb), - tcx.types.bool, - ), + Rvalue::NullaryOp(NullOp::UbChecks, tcx.types.bool), ))), }); terminator.kind = TerminatorKind::Goto { target }; diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs new file mode 100644 index 00000000000..57b6126dece --- /dev/null +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -0,0 +1,117 @@ +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{self, Location, MentionedItem, MirPass}; +use rustc_middle::ty::{self, adjustment::PointerCoercion, TyCtxt}; +use rustc_session::Session; +use rustc_span::source_map::Spanned; + +pub struct MentionedItems; + +struct MentionedItemsVisitor<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a mir::Body<'tcx>, + mentioned_items: &'a mut Vec<Spanned<MentionedItem<'tcx>>>, +} + +impl<'tcx> MirPass<'tcx> for MentionedItems { + fn is_enabled(&self, _sess: &Session) -> bool { + // If this pass is skipped the collector assume that nothing got mentioned! We could + // potentially skip it in opt-level 0 if we are sure that opt-level will never *remove* uses + // of anything, but that still seems fragile. Furthermore, even debug builds use level 1, so + // special-casing level 0 is just not worth it. + true + } + + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { + debug_assert!(body.mentioned_items.is_empty()); + let mut mentioned_items = Vec::new(); + MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body); + body.mentioned_items = mentioned_items; + } +} + +// This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are +// visiting the exact same places but then instead of monomorphizing and creating `MonoItems`, we +// have to remain generic and just recording the relevant information in `mentioned_items`, where it +// will then be monomorphized later during "mentioned items" collection. +impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { + fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { + self.super_terminator(terminator, location); + let span = || self.body.source_info(location).span; + match &terminator.kind { + mir::TerminatorKind::Call { func, .. } => { + let callee_ty = func.ty(self.body, self.tcx); + self.mentioned_items + .push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() }); + } + mir::TerminatorKind::Drop { place, .. } => { + let ty = place.ty(self.body, self.tcx).ty; + self.mentioned_items.push(Spanned { node: MentionedItem::Drop(ty), span: span() }); + } + mir::TerminatorKind::InlineAsm { ref operands, .. } => { + for op in operands { + match *op { + mir::InlineAsmOperand::SymFn { ref value } => { + self.mentioned_items.push(Spanned { + node: MentionedItem::Fn(value.const_.ty()), + span: span(), + }); + } + _ => {} + } + } + } + _ => {} + } + } + + fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { + self.super_rvalue(rvalue, location); + let span = || self.body.source_info(location).span; + match *rvalue { + // We need to detect unsizing casts that required vtables. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::Unsize), + ref operand, + target_ty, + ) + | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { + // This isn't monomorphized yet so we can't tell what the actual types are -- just + // add everything that may involve a vtable. + let source_ty = operand.ty(self.body, self.tcx); + let may_involve_vtable = match ( + source_ty.builtin_deref(true).map(|t| t.ty.kind()), + target_ty.builtin_deref(true).map(|t| t.ty.kind()), + ) { + (Some(ty::Array(..)), Some(ty::Str | ty::Slice(..))) => false, // &str/&[T] unsizing + _ => true, + }; + if may_involve_vtable { + self.mentioned_items.push(Spanned { + node: MentionedItem::UnsizeCast { source_ty, target_ty }, + span: span(), + }); + } + } + // Similarly, record closures that are turned into function pointers. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), + ref operand, + _, + ) => { + let source_ty = operand.ty(self.body, self.tcx); + self.mentioned_items + .push(Spanned { node: MentionedItem::Closure(source_ty), span: span() }); + } + // And finally, function pointer reification casts. + mir::Rvalue::Cast( + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer), + ref operand, + _, + ) => { + let fn_ty = operand.ty(self.body, self.tcx); + self.mentioned_items.push(Spanned { node: MentionedItem::Fn(fn_ty), span: span() }); + } + _ => {} + } + } +} diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 9fe8c34a8bf..2951897ebd6 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -446,7 +446,7 @@ impl<'tcx> Validator<'_, 'tcx> { NullOp::SizeOf => {} NullOp::AlignOf => {} NullOp::OffsetOf(_) => {} - NullOp::UbCheck(_) => {} + NullOp::UbChecks => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), @@ -464,7 +464,7 @@ impl<'tcx> Validator<'_, 'tcx> { let op = *op; let lhs_ty = lhs.ty(self.body, self.tcx); - if let ty::RawPtr(_) | ty::FnPtr(..) = lhs_ty.kind() { + if let ty::RawPtr(_, _) | ty::FnPtr(..) = lhs_ty.kind() { // Raw and fn pointer operations are not allowed inside consts and thus not promotable. assert!(matches!( op, @@ -820,11 +820,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let ty = local_decls[place.local].ty; let span = statement.source_info.span; - let ref_ty = Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() }, - ); + let ref_ty = + Ty::new_ref(tcx, tcx.lifetimes.re_erased, ty, borrow_kind.to_mutbl_lossy()); let mut projection = vec![PlaceElem::Deref]; projection.extend(place.projection); diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 28b502e8cab..94a95428ab0 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -17,7 +17,7 @@ use std::iter; use crate::{ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, - pass_manager as pm, remove_noop_landing_pads, simplify, + mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, }; use rustc_middle::mir::patch::MirPatch; use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle}; @@ -112,6 +112,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' tcx, &mut body, &[ + &mentioned_items::MentionedItems, &abort_unwinding_calls::AbortUnwindingCalls, &add_call_guards::CriticalCallEdges, ], @@ -143,6 +144,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' tcx, &mut result, &[ + &mentioned_items::MentionedItems, &add_moves_for_packed_drops::AddMovesForPackedDrops, &deref_separator::Derefer, &remove_noop_landing_pads::RemoveNoopLandingPads, @@ -537,14 +539,8 @@ impl<'tcx> CloneShimBuilder<'tcx> { const_: Const::zero_sized(func_ty), })); - let ref_loc = self.make_place( - Mutability::Not, - Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, - ), - ); + let ref_loc = + self.make_place(Mutability::Not, Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty)); // `let ref_loc: &ty = &src;` let statement = self.make_statement(StatementKind::Assign(Box::new(( @@ -769,11 +765,7 @@ fn build_call_shim<'tcx>( // let rcvr = &mut rcvr; let ref_rcvr = local_decls.push( LocalDecl::new( - Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - ty::TypeAndMut { ty: sig.inputs()[0], mutbl: hir::Mutability::Mut }, - ), + Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, sig.inputs()[0]), span, ) .immutable(), diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs index e4fdbd6ae69..fddc62e6652 100644 --- a/compiler/rustc_mir_transform/src/ssa.rs +++ b/compiler/rustc_mir_transform/src/ssa.rs @@ -29,7 +29,7 @@ pub struct SsaLocals { pub enum AssignedValue<'a, 'tcx> { Arg, Rvalue(&'a mut Rvalue<'tcx>), - Terminator(&'a mut TerminatorKind<'tcx>), + Terminator, } impl SsaLocals { @@ -149,8 +149,7 @@ impl SsaLocals { Set1::One(DefLocation::CallReturn { call, .. }) => { let bb = &mut basic_blocks[call]; let loc = Location { block: call, statement_index: bb.statements.len() }; - let term = bb.terminator_mut(); - f(local, AssignedValue::Terminator(&mut term.kind), loc) + f(local, AssignedValue::Terminator, loc) } _ => {} } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index abe691ba0d8..a51b1c34a1a 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -28,6 +28,7 @@ //! - VTables //! - Object Shims //! +//! The main entry point is `collect_crate_mono_items`, at the bottom of this file. //! //! General Algorithm //! ----------------- @@ -137,21 +138,61 @@ //! just linked to and no node is created; which is exactly what we want, since //! no machine code should be generated in the current crate for such an item. //! -//! Eager and Lazy Collection Mode -//! ------------------------------ -//! Mono item collection can be performed in one of two modes: +//! Eager and Lazy Collection Strategy +//! ---------------------------------- +//! Mono item collection can be performed with one of two strategies: //! -//! - Lazy mode means that items will only be instantiated when actually +//! - Lazy strategy means that items will only be instantiated when actually //! used. The goal is to produce the least amount of machine code //! possible. //! -//! - Eager mode is meant to be used in conjunction with incremental compilation +//! - Eager strategy is meant to be used in conjunction with incremental compilation //! where a stable set of mono items is more important than a minimal -//! one. Thus, eager mode will instantiate drop-glue for every drop-able type +//! one. Thus, eager strategy will instantiate drop-glue for every drop-able type //! in the crate, even if no drop call for that type exists (yet). It will //! also instantiate default implementations of trait methods, something that //! otherwise is only done on demand. //! +//! Collection-time const evaluation and "mentioned" items +//! ------------------------------------------------------ +//! +//! One important role of collection is to evaluate all constants that are used by all the items +//! which are being collected. Codegen can then rely on only encountering constants that evaluate +//! successfully, and if a constant fails to evaluate, the collector has much better context to be +//! able to show where this constant comes up. +//! +//! However, the exact set of "used" items (collected as described above), and therefore the exact +//! set of used constants, can depend on optimizations. Optimizing away dead code may optimize away +//! a function call that uses a failing constant, so an unoptimized build may fail where an +//! optimized build succeeds. This is undesirable. +//! +//! To avoid this, the collector has the concept of "mentioned" items. Some time during the MIR +//! pipeline, before any optimization-level-dependent optimizations, we compute a list of all items +//! that syntactically appear in the code. These are considered "mentioned", and even if they are in +//! dead code and get optimized away (which makes them no longer "used"), they are still +//! "mentioned". For every used item, the collector ensures that all mentioned items, recursively, +//! do not use a failing constant. This is reflected via the [`CollectionMode`], which determines +//! whether we are visiting a used item or merely a mentioned item. +//! +//! The collector and "mentioned items" gathering (which lives in `rustc_mir_transform::mentioned_items`) +//! need to stay in sync in the following sense: +//! +//! - For every item that the collector gather that could eventually lead to build failure (most +//! likely due to containing a constant that fails to evaluate), a corresponding mentioned item +//! must be added. This should use the exact same strategy as the ecollector to make sure they are +//! in sync. However, while the collector works on monomorphized types, mentioned items are +//! collected on generic MIR -- so any time the collector checks for a particular type (such as +//! `ty::FnDef`), we have to just onconditionally add this as a mentioned item. +//! - In `visit_mentioned_item`, we then do with that mentioned item exactly what the collector +//! would have done during regular MIR visiting. Basically you can think of the collector having +//! two stages, a pre-monomorphization stage and a post-monomorphization stage (usually quite +//! literally separated by a call to `self.monomorphize`); the pre-monomorphizationn stage is +//! duplicated in mentioned items gathering and the post-monomorphization stage is duplicated in +//! `visit_mentioned_item`. +//! - Finally, as a performance optimization, the collector should fill `used_mentioned_item` during +//! its MIR traversal with exactly what mentioned item gathering would have added in the same +//! situation. This detects mentioned items that have *not* been optimized away and hence don't +//! need a dedicated traversal. //! //! Open Issues //! ----------- @@ -165,15 +206,16 @@ //! regardless of whether it is actually needed or not. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::sync::{par_for_each_in, MTLock, MTLockRef}; +use rustc_data_structures::sync::{par_for_each_in, LRef, MTLock}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::lang_items::LangItem; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar}; use rustc_middle::mir::mono::{InstantiationMode, MonoItem}; use rustc_middle::mir::visit::Visitor as MirVisitor; -use rustc_middle::mir::{self, Location}; +use rustc_middle::mir::{self, Location, MentionedItem}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion}; use rustc_middle::ty::layout::ValidityRequirement; @@ -183,7 +225,6 @@ use rustc_middle::ty::{ TypeVisitableExt, VtblEntry, }; use rustc_middle::ty::{GenericArgKind, GenericArgs}; -use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext}; use rustc_session::config::EntryFnType; use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; use rustc_session::Limit; @@ -199,7 +240,7 @@ use crate::errors::{ }; #[derive(PartialEq)] -pub enum MonoItemCollectionMode { +pub enum MonoItemCollectionStrategy { Eager, Lazy, } @@ -214,6 +255,35 @@ pub struct UsageMap<'tcx> { type MonoItems<'tcx> = Vec<Spanned<MonoItem<'tcx>>>; +/// The state that is shared across the concurrent threads that are doing collection. +struct SharedState<'tcx> { + /// Items that have been or are currently being recursively collected. + visited: MTLock<FxHashSet<MonoItem<'tcx>>>, + /// Items that have been or are currently being recursively treated as "mentioned", i.e., their + /// consts are evaluated but nothing is added to the collection. + mentioned: MTLock<FxHashSet<MonoItem<'tcx>>>, + /// Which items are being used where, for better errors. + usage_map: MTLock<UsageMap<'tcx>>, +} + +/// See module-level docs on some contect for "mentioned" items. +#[derive(Copy, Clone, Debug, PartialEq)] +enum CollectionMode { + /// Collect items that are used, i.e., actually needed for codegen. + /// + /// Which items are used can depend on optimization levels, as MIR optimizations can remove + /// uses. + UsedItems, + /// Collect items that are mentioned. The goal of this mode is that it is independent of + /// optimizations: the set of "mentioned" items is computed before optimizations are run. + /// + /// The exact contents of this set are *not* a stable guarantee. (For instance, it is currently + /// computed after drop-elaboration. If we ever do some optimizations even in debug builds, we + /// might decide to run them before computing mentioned items.) The key property of this set is + /// that it is optimization-independent. + MentionedItems, +} + impl<'tcx> UsageMap<'tcx> { fn new() -> UsageMap<'tcx> { UsageMap { used_map: FxHashMap::default(), user_map: FxHashMap::default() } @@ -253,99 +323,40 @@ impl<'tcx> UsageMap<'tcx> { } } -#[instrument(skip(tcx, mode), level = "debug")] -pub fn collect_crate_mono_items( - tcx: TyCtxt<'_>, - mode: MonoItemCollectionMode, -) -> (FxHashSet<MonoItem<'_>>, UsageMap<'_>) { - let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); - - let roots = - tcx.sess.time("monomorphization_collector_root_collections", || collect_roots(tcx, mode)); - - debug!("building mono item graph, beginning at roots"); - - let mut visited = MTLock::new(FxHashSet::default()); - let mut usage_map = MTLock::new(UsageMap::new()); - let recursion_limit = tcx.recursion_limit(); - - { - let visited: MTLockRef<'_, _> = &mut visited; - let usage_map: MTLockRef<'_, _> = &mut usage_map; - - tcx.sess.time("monomorphization_collector_graph_walk", || { - par_for_each_in(roots, |root| { - let mut recursion_depths = DefIdMap::default(); - collect_items_rec( - tcx, - dummy_spanned(root), - visited, - &mut recursion_depths, - recursion_limit, - usage_map, - ); - }); - }); - } - - (visited.into_inner(), usage_map.into_inner()) -} - -// Find all non-generic items by walking the HIR. These items serve as roots to -// start monomorphizing from. -#[instrument(skip(tcx, mode), level = "debug")] -fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec<MonoItem<'_>> { - debug!("collecting roots"); - let mut roots = Vec::new(); - - { - let entry_fn = tcx.entry_fn(()); - - debug!("collect_roots: entry_fn = {:?}", entry_fn); - - let mut collector = RootCollector { tcx, mode, entry_fn, output: &mut roots }; - - let crate_items = tcx.hir_crate_items(()); - - for id in crate_items.items() { - collector.process_item(id); - } - - for id in crate_items.impl_items() { - collector.process_impl_item(id); - } - - collector.push_extra_entry_roots(); - } - - // We can only codegen items that are instantiable - items all of - // whose predicates hold. Luckily, items that aren't instantiable - // can't actually be used, so we can just skip codegenning them. - roots - .into_iter() - .filter_map(|Spanned { node: mono_item, .. }| { - mono_item.is_instantiable(tcx).then_some(mono_item) - }) - .collect() -} - /// Collect all monomorphized items reachable from `starting_point`, and emit a note diagnostic if a /// post-monomorphization error is encountered during a collection step. -#[instrument(skip(tcx, visited, recursion_depths, recursion_limit, usage_map), level = "debug")] +/// +/// `mode` determined whether we are scanning for [used items][CollectionMode::UsedItems] +/// or [mentioned items][CollectionMode::MentionedItems]. +#[instrument(skip(tcx, state, recursion_depths, recursion_limit), level = "debug")] fn collect_items_rec<'tcx>( tcx: TyCtxt<'tcx>, starting_item: Spanned<MonoItem<'tcx>>, - visited: MTLockRef<'_, FxHashSet<MonoItem<'tcx>>>, + state: LRef<'_, SharedState<'tcx>>, recursion_depths: &mut DefIdMap<usize>, recursion_limit: Limit, - usage_map: MTLockRef<'_, UsageMap<'tcx>>, + mode: CollectionMode, ) { - if !visited.lock_mut().insert(starting_item.node) { - // We've been here already, no need to search again. - return; + if mode == CollectionMode::UsedItems { + if !state.visited.lock_mut().insert(starting_item.node) { + // We've been here already, no need to search again. + return; + } + } else { + if state.visited.lock().contains(&starting_item.node) { + // We've already done a *full* visit on this one, no need to do the "mention" visit. + return; + } + if !state.mentioned.lock_mut().insert(starting_item.node) { + // We've been here already, no need to search again. + return; + } + // There's some risk that we first do a 'mention' visit and then a full visit. But there's no + // harm in that, the mention visit will trigger all the queries and the results are cached. } - let mut used_items = Vec::new(); + let mut used_items = MonoItems::new(); + let mut mentioned_items = MonoItems::new(); let recursion_depth_reset; // Post-monomorphization errors MVP @@ -373,37 +384,48 @@ fn collect_items_rec<'tcx>( // FIXME: don't rely on global state, instead bubble up errors. Note: this is very hard to do. let error_count = tcx.dcx().err_count(); + // In `mentioned_items` we collect items that were mentioned in this MIR but possibly do not + // need to be monomorphized. This is done to ensure that optimizing away function calls does not + // hide const-eval errors that those calls would otherwise have triggered. match starting_item.node { MonoItem::Static(def_id) => { - let instance = Instance::mono(tcx, def_id); + recursion_depth_reset = None; - // Sanity check whether this ended up being collected accidentally - debug_assert!(should_codegen_locally(tcx, &instance)); + // Statics always get evaluted (which is possible because they can't be generic), so for + // `MentionedItems` collection there's nothing to do here. + if mode == CollectionMode::UsedItems { + let instance = Instance::mono(tcx, def_id); - let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() }; - // Nested statics have no type. - if !nested { - let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items); - } + // Sanity check whether this ended up being collected accidentally + debug_assert!(should_codegen_locally(tcx, &instance)); - recursion_depth_reset = None; + let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() }; + // Nested statics have no type. + if !nested { + let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items); + } + + if let Ok(alloc) = tcx.eval_static_initializer(def_id) { + for &prov in alloc.inner().provenance().ptrs().values() { + collect_alloc(tcx, prov.alloc_id(), &mut used_items); + } + } - if let Ok(alloc) = tcx.eval_static_initializer(def_id) { - for &prov in alloc.inner().provenance().ptrs().values() { - collect_alloc(tcx, prov.alloc_id(), &mut used_items); + if tcx.needs_thread_local_shim(def_id) { + used_items.push(respan( + starting_item.span, + MonoItem::Fn(Instance { + def: InstanceDef::ThreadLocalShim(def_id), + args: GenericArgs::empty(), + }), + )); } } - if tcx.needs_thread_local_shim(def_id) { - used_items.push(respan( - starting_item.span, - MonoItem::Fn(Instance { - def: InstanceDef::ThreadLocalShim(def_id), - args: GenericArgs::empty(), - }), - )); - } + // mentioned_items stays empty since there's no codegen for statics. statics don't get + // optimized, and if they did then the const-eval interpreter would have to worry about + // mentioned_items. } MonoItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally @@ -420,10 +442,20 @@ fn collect_items_rec<'tcx>( check_type_length_limit(tcx, instance); rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_used_items(tcx, instance, &mut used_items); + collect_items_of_instance( + tcx, + instance, + &mut used_items, + &mut mentioned_items, + mode, + ) }); } MonoItem::GlobalAsm(item_id) => { + assert!( + mode == CollectionMode::UsedItems, + "should never encounter global_asm when collecting mentioned items" + ); recursion_depth_reset = None; let item = tcx.hir().item(item_id); @@ -459,8 +491,10 @@ fn collect_items_rec<'tcx>( } else { span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") } + + // mention_items stays empty as nothing gets optimized here. } - } + }; // Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the // mono item graph. @@ -474,10 +508,41 @@ fn collect_items_rec<'tcx>( formatted_item, }); } - usage_map.lock_mut().record_used(starting_item.node, &used_items); + // Only updating `usage_map` for used items as otherwise we may be inserting the same item + // multiple times (if it is first 'mentioned' and then later actuall used), and the usage map + // logic does not like that. + // This is part of the output of collection and hence only relevant for "used" items. + // ("Mentioned" items are only considered internally during collection.) + if mode == CollectionMode::UsedItems { + state.usage_map.lock_mut().record_used(starting_item.node, &used_items); + } - for used_item in used_items { - collect_items_rec(tcx, used_item, visited, recursion_depths, recursion_limit, usage_map); + if mode == CollectionMode::MentionedItems { + assert!(used_items.is_empty(), "'mentioned' collection should never encounter used items"); + } else { + for used_item in used_items { + collect_items_rec( + tcx, + used_item, + state, + recursion_depths, + recursion_limit, + CollectionMode::UsedItems, + ); + } + } + + // Walk over mentioned items *after* used items, so that if an item is both mentioned and used then + // the loop above has fully collected it, so this loop will skip it. + for mentioned_item in mentioned_items { + collect_items_rec( + tcx, + mentioned_item, + state, + recursion_depths, + recursion_limit, + CollectionMode::MentionedItems, + ); } if let Some((def_id, depth)) = recursion_depth_reset { @@ -596,7 +661,10 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { struct MirUsedCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, - output: &'a mut MonoItems<'tcx>, + used_items: &'a mut MonoItems<'tcx>, + /// See the comment in `collect_items_of_instance` for the purpose of this set. + /// Note that this contains *not-monomorphized* items! + used_mentioned_items: &'a mut FxHashSet<MentionedItem<'tcx>>, instance: Instance<'tcx>, /// Spans for move size lints already emitted. Helps avoid duplicate lints. move_size_spans: Vec<Span>, @@ -606,11 +674,11 @@ struct MirUsedCollector<'a, 'tcx> { } impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { - pub fn monomorphize<T>(&self, value: T) -> T + fn monomorphize<T>(&self, value: T) -> T where T: TypeFoldable<TyCtxt<'tcx>>, { - debug!("monomorphize: self.instance={:?}", self.instance); + trace!("monomorphize: self.instance={:?}", self.instance); self.instance.instantiate_mir_and_normalize_erasing_regions( self.tcx, ty::ParamEnv::reveal_all(), @@ -735,6 +803,31 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { ); self.move_size_spans.push(span); } + + /// Evaluates a *not yet monomorphized* constant. + fn eval_constant( + &mut self, + constant: &mir::ConstOperand<'tcx>, + ) -> Option<mir::ConstValue<'tcx>> { + let const_ = self.monomorphize(constant.const_); + let param_env = ty::ParamEnv::reveal_all(); + // Evaluate the constant. This makes const eval failure a collection-time error (rather than + // a codegen-time error). rustc stops after collection if there was an error, so this + // ensures codegen never has to worry about failing consts. + // (codegen relies on this and ICEs will happen if this is violated.) + match const_.eval(self.tcx, param_env, constant.span) { + Ok(v) => Some(v), + Err(ErrorHandled::TooGeneric(..)) => span_bug!( + constant.span, + "collection encountered polymorphic constant: {:?}", + const_ + ), + Err(err @ ErrorHandled::Reported(..)) => { + err.emit_note(self.tcx); + return None; + } + } + } } impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { @@ -753,8 +846,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { target_ty, ) | mir::Rvalue::Cast(mir::CastKind::DynStar, ref operand, target_ty) => { - let target_ty = self.monomorphize(target_ty); let source_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items + .insert(MentionedItem::UnsizeCast { source_ty, target_ty }); + let target_ty = self.monomorphize(target_ty); let source_ty = self.monomorphize(source_ty); let (source_ty, target_ty) = find_vtable_types_for_unsizing(self.tcx.at(span), source_ty, target_ty); @@ -769,7 +865,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { target_ty, source_ty, span, - self.output, + self.used_items, ); } } @@ -779,8 +875,10 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { _, ) => { let fn_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Fn(fn_ty)); let fn_ty = self.monomorphize(fn_ty); - visit_fn_use(self.tcx, fn_ty, false, span, self.output); + visit_fn_use(self.tcx, fn_ty, false, span, self.used_items); } mir::Rvalue::Cast( mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)), @@ -788,20 +886,17 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { _, ) => { let source_ty = operand.ty(self.body, self.tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Closure(source_ty)); let source_ty = self.monomorphize(source_ty); - match *source_ty.kind() { - ty::Closure(def_id, args) => { - let instance = Instance::resolve_closure( - self.tcx, - def_id, - args, - ty::ClosureKind::FnOnce, - ); - if should_codegen_locally(self.tcx, &instance) { - self.output.push(create_fn_mono_item(self.tcx, instance, span)); - } + if let ty::Closure(def_id, args) = *source_ty.kind() { + let instance = + Instance::resolve_closure(self.tcx, def_id, args, ty::ClosureKind::FnOnce); + if should_codegen_locally(self.tcx, &instance) { + self.used_items.push(create_fn_mono_item(self.tcx, instance, span)); } - _ => bug!(), + } else { + bug!() } } mir::Rvalue::ThreadLocalRef(def_id) => { @@ -809,7 +904,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let instance = Instance::mono(self.tcx, def_id); if should_codegen_locally(self.tcx, &instance) { trace!("collecting thread-local static {:?}", def_id); - self.output.push(respan(span, MonoItem::Static(def_id))); + self.used_items.push(respan(span, MonoItem::Static(def_id))); } } _ => { /* not interesting */ } @@ -822,26 +917,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { /// to ensure that the constant evaluates successfully and walk the result. #[instrument(skip(self), level = "debug")] fn visit_constant(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) { - let const_ = self.monomorphize(constant.const_); - let param_env = ty::ParamEnv::reveal_all(); - // Evaluate the constant. This makes const eval failure a collection-time error (rather than - // a codegen-time error). rustc stops after collection if there was an error, so this - // ensures codegen never has to worry about failing consts. - // (codegen relies on this and ICEs will happen if this is violated.) - let val = match const_.eval(self.tcx, param_env, constant.span) { - Ok(v) => v, - Err(ErrorHandled::TooGeneric(..)) => span_bug!( - self.body.source_info(location).span, - "collection encountered polymorphic constant: {:?}", - const_ - ), - Err(err @ ErrorHandled::Reported(..)) => { - err.emit_note(self.tcx); - return; - } - }; - collect_const_value(self.tcx, val, self.output); - MirVisitor::visit_ty(self, const_.ty(), TyContext::Location(location)); + // No `super_constant` as we don't care about `visit_ty`/`visit_ty_const`. + let Some(val) = self.eval_constant(constant) else { return }; + collect_const_value(self.tcx, val, self.used_items); } fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) { @@ -852,34 +930,41 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let push_mono_lang_item = |this: &mut Self, lang_item: LangItem| { let instance = Instance::mono(tcx, tcx.require_lang_item(lang_item, Some(source))); if should_codegen_locally(tcx, &instance) { - this.output.push(create_fn_mono_item(tcx, instance, source)); + this.used_items.push(create_fn_mono_item(tcx, instance, source)); } }; match terminator.kind { mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => { let callee_ty = func.ty(self.body, tcx); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty)); let callee_ty = self.monomorphize(callee_ty); self.check_fn_args_move_size(callee_ty, args, *fn_span, location); - visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output) + visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items) } mir::TerminatorKind::Drop { ref place, .. } => { let ty = place.ty(self.body, self.tcx).ty; + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Drop(ty)); let ty = self.monomorphize(ty); - visit_drop_use(self.tcx, ty, true, source, self.output); + visit_drop_use(self.tcx, ty, true, source, self.used_items); } mir::TerminatorKind::InlineAsm { ref operands, .. } => { for op in operands { match *op { mir::InlineAsmOperand::SymFn { ref value } => { - let fn_ty = self.monomorphize(value.const_.ty()); - visit_fn_use(self.tcx, fn_ty, false, source, self.output); + let fn_ty = value.const_.ty(); + // *Before* monomorphizing, record that we already handled this mention. + self.used_mentioned_items.insert(MentionedItem::Fn(fn_ty)); + let fn_ty = self.monomorphize(fn_ty); + visit_fn_use(self.tcx, fn_ty, false, source, self.used_items); } mir::InlineAsmOperand::SymStatic { def_id } => { let instance = Instance::mono(self.tcx, def_id); if should_codegen_locally(self.tcx, &instance) { trace!("collecting asm sym static {:?}", def_id); - self.output.push(respan(source, MonoItem::Static(def_id))); + self.used_items.push(respan(source, MonoItem::Static(def_id))); } } _ => {} @@ -936,6 +1021,8 @@ fn visit_drop_use<'tcx>( visit_instance_use(tcx, instance, is_direct_call, source, output); } +/// For every call of this function in the visitor, make sure there is a matching call in the +/// `mentioned_items` pass! fn visit_fn_use<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, @@ -1020,7 +1107,7 @@ fn visit_instance_use<'tcx>( /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. -fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { +pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool { let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else { return true; }; @@ -1124,10 +1211,8 @@ fn find_vtable_types_for_unsizing<'tcx>( }; match (&source_ty.kind(), &target_ty.kind()) { - (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) - | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { - ptr_vtable(*a, *b) - } + (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _)) + | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => ptr_vtable(*a, *b), (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { ptr_vtable(source_ty.boxed_ty(), target_ty.boxed_ty()) } @@ -1197,34 +1282,225 @@ fn create_mono_items_for_vtable_methods<'tcx>( ) { assert!(!trait_ty.has_escaping_bound_vars() && !impl_ty.has_escaping_bound_vars()); - if let ty::Dynamic(trait_ty, ..) = trait_ty.kind() { - if let Some(principal) = trait_ty.principal() { - let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); - assert!(!poly_trait_ref.has_escaping_bound_vars()); - - // Walk all methods of the trait, including those of its supertraits - let entries = tcx.vtable_entries(poly_trait_ref); - let methods = entries - .iter() - .filter_map(|entry| match entry { - VtblEntry::MetadataDropInPlace - | VtblEntry::MetadataSize - | VtblEntry::MetadataAlign - | VtblEntry::Vacant => None, - VtblEntry::TraitVPtr(_) => { - // all super trait items already covered, so skip them. - None - } - VtblEntry::Method(instance) => { - Some(*instance).filter(|instance| should_codegen_locally(tcx, instance)) + let ty::Dynamic(trait_ty, ..) = trait_ty.kind() else { + bug!("create_mono_items_for_vtable_methods: {trait_ty:?} not a trait type"); + }; + if let Some(principal) = trait_ty.principal() { + let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); + assert!(!poly_trait_ref.has_escaping_bound_vars()); + + // Walk all methods of the trait, including those of its supertraits + let entries = tcx.vtable_entries(poly_trait_ref); + debug!(?entries); + let methods = entries + .iter() + .filter_map(|entry| match entry { + VtblEntry::MetadataDropInPlace + | VtblEntry::MetadataSize + | VtblEntry::MetadataAlign + | VtblEntry::Vacant => None, + VtblEntry::TraitVPtr(_) => { + // all super trait items already covered, so skip them. + None + } + VtblEntry::Method(instance) => { + Some(*instance).filter(|instance| should_codegen_locally(tcx, instance)) + } + }) + .map(|item| create_fn_mono_item(tcx, item, source)); + output.extend(methods); + } + + // Also add the destructor. + visit_drop_use(tcx, impl_ty, false, source, output); +} + +/// Scans the CTFE alloc in order to find function pointers and statics that must be monomorphized. +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)); + let instance = Instance::mono(tcx, def_id); + if should_codegen_locally(tcx, &instance) { + trace!("collecting static {:?}", def_id); + output.push(dummy_spanned(MonoItem::Static(def_id))); + } + } + GlobalAlloc::Memory(alloc) => { + trace!("collecting {:?} with {:#?}", alloc_id, alloc); + let ptrs = alloc.inner().provenance().ptrs(); + // avoid `ensure_sufficient_stack` in the common case of "no pointers" + if !ptrs.is_empty() { + rustc_data_structures::stack::ensure_sufficient_stack(move || { + for &prov in ptrs.values() { + collect_alloc(tcx, prov.alloc_id(), output); } - }) - .map(|item| create_fn_mono_item(tcx, item, source)); - output.extend(methods); + }); + } + } + GlobalAlloc::Function(fn_instance) => { + if should_codegen_locally(tcx, &fn_instance) { + trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); + output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); + } + } + GlobalAlloc::VTable(ty, trait_ref) => { + let alloc_id = tcx.vtable_allocation((ty, trait_ref)); + collect_alloc(tcx, alloc_id, output) + } + } +} + +fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option<DefId> { + for impl_def_id in tcx.inherent_impls(def_id).ok()? { + if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( + tcx, + fn_ident, + AssocKind::Fn, + def_id, + ) { + return Some(new.def_id); + } + } + return None; +} + +fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec<DefId> { + let fns = [ + (tcx.lang_items().owned_box(), "new"), + (tcx.get_diagnostic_item(sym::Rc), "new"), + (tcx.get_diagnostic_item(sym::Arc), "new"), + ]; + fns.into_iter() + .filter_map(|(def_id, fn_name)| { + def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, Ident::from_str(fn_name))) + }) + .collect::<Vec<_>>() +} + +/// Scans the MIR in order to find function calls, closures, and drop-glue. +/// +/// Anything that's found is added to `output`. Furthermore the "mentioned items" of the MIR are returned. +#[instrument(skip(tcx, used_items, mentioned_items), level = "debug")] +fn collect_items_of_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + used_items: &mut MonoItems<'tcx>, + mentioned_items: &mut MonoItems<'tcx>, + mode: CollectionMode, +) { + let body = tcx.instance_mir(instance.def); + // Naively, in "used" collection mode, all functions get added to *both* `used_items` and + // `mentioned_items`. Mentioned items processing will then notice that they have already been + // visited, but at that point each mentioned item has been monomorphized, added to the + // `mentioned_items` worklist, and checked in the global set of visited items. To remove that + // overhead, we have a special optimization that avoids adding items to `mentioned_items` when + // they are already added in `used_items`. We could just scan `used_items`, but that's a linear + // scan and not very efficient. Furthermore we can only do that *after* monomorphizing the + // mentioned item. So instead we collect all pre-monomorphized `MentionedItem` that were already + // added to `used_items` in a hash set, which can efficiently query in the + // `body.mentioned_items` loop below without even having to monomorphize the item. + let mut used_mentioned_items = FxHashSet::<MentionedItem<'tcx>>::default(); + let mut collector = MirUsedCollector { + tcx, + body, + used_items, + used_mentioned_items: &mut used_mentioned_items, + instance, + move_size_spans: vec![], + visiting_call_terminator: false, + skip_move_check_fns: None, + }; + + if mode == CollectionMode::UsedItems { + // Visit everything. Here we rely on the visitor also visiting `required_consts`, so that we + // evaluate them and abort compilation if any of them errors. + collector.visit_body(body); + } else { + // We only need to evaluate all constants, but can ignore the rest of the MIR. + for const_op in &body.required_consts { + if let Some(val) = collector.eval_constant(const_op) { + collect_const_value(tcx, val, mentioned_items); + } } + } - // Also add the destructor. - visit_drop_use(tcx, impl_ty, false, source, output); + // Always gather mentioned items. We try to avoid processing items that we have already added to + // `used_items` above. + for item in &body.mentioned_items { + if !collector.used_mentioned_items.contains(&item.node) { + let item_mono = collector.monomorphize(item.node); + visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items); + } + } +} + +/// `item` must be already monomorphized. +#[instrument(skip(tcx, span, output), level = "debug")] +fn visit_mentioned_item<'tcx>( + tcx: TyCtxt<'tcx>, + item: &MentionedItem<'tcx>, + span: Span, + output: &mut MonoItems<'tcx>, +) { + match *item { + MentionedItem::Fn(ty) => { + if let ty::FnDef(def_id, args) = *ty.kind() { + let instance = + Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args); + // `visit_instance_use` was written for "used" item collection but works just as well + // for "mentioned" item collection. + // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway + // can't have their own failing constants. + visit_instance_use(tcx, instance, /*is_direct_call*/ true, span, output); + } + } + MentionedItem::Drop(ty) => { + visit_drop_use(tcx, ty, /*is_direct_call*/ true, span, output); + } + MentionedItem::UnsizeCast { source_ty, target_ty } => { + let (source_ty, target_ty) = + find_vtable_types_for_unsizing(tcx.at(span), source_ty, target_ty); + // This could also be a different Unsize instruction, like + // from a fixed sized array to a slice. But we are only + // interested in things that produce a vtable. + if (target_ty.is_trait() && !source_ty.is_trait()) + || (target_ty.is_dyn_star() && !source_ty.is_dyn_star()) + { + create_mono_items_for_vtable_methods(tcx, target_ty, source_ty, span, output); + } + } + MentionedItem::Closure(source_ty) => { + if let ty::Closure(def_id, args) = *source_ty.kind() { + let instance = + Instance::resolve_closure(tcx, def_id, args, ty::ClosureKind::FnOnce); + if should_codegen_locally(tcx, &instance) { + output.push(create_fn_mono_item(tcx, instance, span)); + } + } else { + bug!() + } + } + } +} + +#[instrument(skip(tcx, output), level = "debug")] +fn collect_const_value<'tcx>( + tcx: TyCtxt<'tcx>, + value: mir::ConstValue<'tcx>, + output: &mut MonoItems<'tcx>, +) { + match value { + mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => { + collect_alloc(tcx, ptr.provenance.alloc_id(), output) + } + mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output), + mir::ConstValue::Slice { data, meta: _ } => { + for &prov in data.inner().provenance().ptrs().values() { + collect_alloc(tcx, prov.alloc_id(), output); + } + } + _ => {} } } @@ -1232,9 +1508,47 @@ fn create_mono_items_for_vtable_methods<'tcx>( // Root Collection //=----------------------------------------------------------------------------- +// Find all non-generic items by walking the HIR. These items serve as roots to +// start monomorphizing from. +#[instrument(skip(tcx, mode), level = "debug")] +fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionStrategy) -> Vec<MonoItem<'_>> { + debug!("collecting roots"); + let mut roots = Vec::new(); + + { + let entry_fn = tcx.entry_fn(()); + + debug!("collect_roots: entry_fn = {:?}", entry_fn); + + let mut collector = RootCollector { tcx, strategy: mode, entry_fn, output: &mut roots }; + + let crate_items = tcx.hir_crate_items(()); + + for id in crate_items.free_items() { + collector.process_item(id); + } + + for id in crate_items.impl_items() { + collector.process_impl_item(id); + } + + collector.push_extra_entry_roots(); + } + + // We can only codegen items that are instantiable - items all of + // whose predicates hold. Luckily, items that aren't instantiable + // can't actually be used, so we can just skip codegenning them. + roots + .into_iter() + .filter_map(|Spanned { node: mono_item, .. }| { + mono_item.is_instantiable(tcx).then_some(mono_item) + }) + .collect() +} + struct RootCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, - mode: MonoItemCollectionMode, + strategy: MonoItemCollectionStrategy, output: &'a mut MonoItems<'tcx>, entry_fn: Option<(DefId, EntryFnType)>, } @@ -1243,7 +1557,7 @@ impl<'v> RootCollector<'_, 'v> { fn process_item(&mut self, id: hir::ItemId) { match self.tcx.def_kind(id.owner_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { - if self.mode == MonoItemCollectionMode::Eager + if self.strategy == MonoItemCollectionStrategy::Eager && self.tcx.generics_of(id.owner_id).count() == 0 { debug!("RootCollector: ADT drop-glue for `{id:?}`",); @@ -1274,7 +1588,7 @@ impl<'v> RootCollector<'_, 'v> { } } DefKind::Impl { .. } => { - if self.mode == MonoItemCollectionMode::Eager { + if self.strategy == MonoItemCollectionStrategy::Eager { create_mono_items_for_default_impls(self.tcx, id, self.output); } } @@ -1293,9 +1607,9 @@ impl<'v> RootCollector<'_, 'v> { fn is_root(&self, def_id: LocalDefId) -> bool { !self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) - && match self.mode { - MonoItemCollectionMode::Eager => true, - MonoItemCollectionMode::Lazy => { + && match self.strategy { + MonoItemCollectionStrategy::Eager => true, + MonoItemCollectionStrategy::Lazy => { self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id) || self.tcx.is_reachable_non_generic(def_id) || self @@ -1428,108 +1742,47 @@ fn create_mono_items_for_default_impls<'tcx>( } } -/// Scans the CTFE alloc in order to find function pointers and statics that must be monomorphized. -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)); - let instance = Instance::mono(tcx, def_id); - if should_codegen_locally(tcx, &instance) { - trace!("collecting static {:?}", def_id); - output.push(dummy_spanned(MonoItem::Static(def_id))); - } - } - GlobalAlloc::Memory(alloc) => { - trace!("collecting {:?} with {:#?}", alloc_id, alloc); - let ptrs = alloc.inner().provenance().ptrs(); - // avoid `ensure_sufficient_stack` in the common case of "no pointers" - if !ptrs.is_empty() { - rustc_data_structures::stack::ensure_sufficient_stack(move || { - for &prov in ptrs.values() { - collect_alloc(tcx, prov.alloc_id(), output); - } - }); - } - } - GlobalAlloc::Function(fn_instance) => { - if should_codegen_locally(tcx, &fn_instance) { - trace!("collecting {:?} with {:#?}", alloc_id, fn_instance); - output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP)); - } - } - GlobalAlloc::VTable(ty, trait_ref) => { - let alloc_id = tcx.vtable_allocation((ty, trait_ref)); - collect_alloc(tcx, alloc_id, output) - } - } -} +//=----------------------------------------------------------------------------- +// Top-level entry point, tying it all together +//=----------------------------------------------------------------------------- -fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option<DefId> { - for impl_def_id in tcx.inherent_impls(def_id).ok()? { - if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind( - tcx, - fn_ident, - AssocKind::Fn, - def_id, - ) { - return Some(new.def_id); - } - } - return None; -} +#[instrument(skip(tcx, strategy), level = "debug")] +pub fn collect_crate_mono_items( + tcx: TyCtxt<'_>, + strategy: MonoItemCollectionStrategy, +) -> (FxHashSet<MonoItem<'_>>, UsageMap<'_>) { + let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); -fn build_skip_move_check_fns(tcx: TyCtxt<'_>) -> Vec<DefId> { - let fns = [ - (tcx.lang_items().owned_box(), "new"), - (tcx.get_diagnostic_item(sym::Rc), "new"), - (tcx.get_diagnostic_item(sym::Arc), "new"), - ]; - fns.into_iter() - .filter_map(|(def_id, fn_name)| { - def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, Ident::from_str(fn_name))) - }) - .collect::<Vec<_>>() -} + let roots = tcx + .sess + .time("monomorphization_collector_root_collections", || collect_roots(tcx, strategy)); -/// Scans the MIR in order to find function calls, closures, and drop-glue. -#[instrument(skip(tcx, output), level = "debug")] -fn collect_used_items<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, - output: &mut MonoItems<'tcx>, -) { - let body = tcx.instance_mir(instance.def); + debug!("building mono item graph, beginning at roots"); - // Here we rely on the visitor also visiting `required_consts`, so that we evaluate them - // and abort compilation if any of them errors. - MirUsedCollector { - tcx, - body: body, - output, - instance, - move_size_spans: vec![], - visiting_call_terminator: false, - skip_move_check_fns: None, - } - .visit_body(body); -} + let mut state = SharedState { + visited: MTLock::new(FxHashSet::default()), + mentioned: MTLock::new(FxHashSet::default()), + usage_map: MTLock::new(UsageMap::new()), + }; + let recursion_limit = tcx.recursion_limit(); -#[instrument(skip(tcx, output), level = "debug")] -fn collect_const_value<'tcx>( - tcx: TyCtxt<'tcx>, - value: mir::ConstValue<'tcx>, - output: &mut MonoItems<'tcx>, -) { - match value { - mir::ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => { - collect_alloc(tcx, ptr.provenance.alloc_id(), output) - } - mir::ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output), - mir::ConstValue::Slice { data, meta: _ } => { - for &prov in data.inner().provenance().ptrs().values() { - collect_alloc(tcx, prov.alloc_id(), output); - } - } - _ => {} + { + let state: LRef<'_, _> = &mut state; + + tcx.sess.time("monomorphization_collector_graph_walk", || { + par_for_each_in(roots, |root| { + let mut recursion_depths = DefIdMap::default(); + collect_items_rec( + tcx, + dummy_spanned(root), + state, + &mut recursion_depths, + recursion_limit, + CollectionMode::UsedItems, + ); + }); + }); } + + (state.visited.into_inner(), state.usage_map.into_inner()) } diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 7f36ae91f1a..4ec842e8f85 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -11,7 +11,10 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::query::{Providers, TyCtxtAt}; use rustc_middle::traits; use rustc_middle::ty::adjustment::CustomCoerceUnsized; +use rustc_middle::ty::Instance; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty}; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::ErrorGuaranteed; mod collector; @@ -20,6 +23,8 @@ mod partitioning; mod polymorphize; mod util; +use collector::should_codegen_locally; + rustc_fluent_macro::fluent_messages! { "../messages.ftl" } fn custom_coerce_unsize_info<'tcx>( @@ -45,6 +50,23 @@ fn custom_coerce_unsize_info<'tcx>( } } +/// Returns whether a call from the current crate to the [`Instance`] would produce a call +/// from `compiler_builtins` to a symbol the linker must resolve. +/// +/// Such calls from `compiler_bultins` are effectively impossible for the linker to handle. Some +/// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is +/// not guaranteed. So we used this function in codegen backends to ensure we do not generate any +/// unlinkable calls. +pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> bool { + !instance.def_id().is_local() + && tcx.is_compiler_builtins(LOCAL_CRATE) + && tcx.codegen_fn_attrs(instance.def_id()).link_name.is_none() + && !should_codegen_locally(tcx, &instance) +} + pub fn provide(providers: &mut Providers) { partitioning::provide(providers); polymorphize::provide(providers); diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 15041b9cd41..5a92657cb40 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -117,7 +117,7 @@ use rustc_session::CodegenUnits; use rustc_span::symbol::Symbol; use crate::collector::UsageMap; -use crate::collector::{self, MonoItemCollectionMode}; +use crate::collector::{self, MonoItemCollectionStrategy}; use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode}; struct PartitioningCx<'a, 'tcx> { @@ -1087,30 +1087,30 @@ where } fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) { - let collection_mode = match tcx.sess.opts.unstable_opts.print_mono_items { + let collection_strategy = match tcx.sess.opts.unstable_opts.print_mono_items { Some(ref s) => { let mode = s.to_lowercase(); let mode = mode.trim(); if mode == "eager" { - MonoItemCollectionMode::Eager + MonoItemCollectionStrategy::Eager } else { if mode != "lazy" { tcx.dcx().emit_warn(UnknownCguCollectionMode { mode }); } - MonoItemCollectionMode::Lazy + MonoItemCollectionStrategy::Lazy } } None => { if tcx.sess.link_dead_code() { - MonoItemCollectionMode::Eager + MonoItemCollectionStrategy::Eager } else { - MonoItemCollectionMode::Lazy + MonoItemCollectionStrategy::Lazy } } }; - let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_mode); + let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_strategy); // If there was an error during collection (e.g. from one of the constants we evaluated), // then we stop here. This way codegen does not have to worry about failing constants. diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 95b30066662..16d8453ea24 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -239,7 +239,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I> // FIXME: We should investigate the perf implications of not uniquifying // `ReErased`. We may be able to short-circuit registering region // obligations if we encounter a `ReErased` on one side, for example. - ty::ReStatic | ty::ReErased => match self.canonicalize_mode { + ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => return r, }, @@ -277,7 +277,6 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I> } } } - ty::ReError(_) => return r, }; let existing_bound_var = match self.canonicalize_mode { @@ -351,7 +350,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I> | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index a100e2d47bb..aa735f3de1f 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -390,8 +390,6 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else` parse_invalid_identifier_with_leading_number = identifiers cannot start with a number -parse_invalid_interpolated_expression = invalid interpolated expression - parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid .label = invalid suffix `{$suffix}` .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases @@ -570,7 +568,7 @@ parse_more_than_one_char = character literal may only contain one codepoint .remove_non = consider removing the non-printing characters .use_double_quotes = if you meant to write a {$is_byte -> [true] byte string - *[false] `str` + *[false] string } literal, use double quotes parse_multiple_skipped_lines = multiple lines skipped by escaped newline @@ -835,6 +833,7 @@ parse_unknown_prefix = prefix `{$prefix}` is unknown .label = unknown prefix .note = prefixed identifiers and literals are reserved since Rust 2021 .suggestion_br = use `br` for a raw byte string + .suggestion_str = if you meant to write a string literal, use double quotes .suggestion_whitespace = consider inserting whitespace here parse_unknown_start_of_token = unknown start of token: {$escaped} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 32b56bb7e87..20ebfc6691b 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -851,13 +851,6 @@ pub(crate) struct StructLiteralNotAllowedHereSugg { } #[derive(Diagnostic)] -#[diag(parse_invalid_interpolated_expression)] -pub(crate) struct InvalidInterpolatedExpression { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(parse_invalid_literal_suffix_on_tuple_index)] pub(crate) struct InvalidLiteralSuffixOnTupleIndex { #[primary_span] @@ -1994,6 +1987,17 @@ pub enum UnknownPrefixSugg { style = "verbose" )] Whitespace(#[primary_span] Span), + #[multipart_suggestion( + parse_suggestion_str, + applicability = "maybe-incorrect", + style = "verbose" + )] + MeantStr { + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + }, } #[derive(Diagnostic)] @@ -2205,12 +2209,21 @@ pub enum MoreThanOneCharSugg { ch: String, }, #[suggestion(parse_use_double_quotes, code = "{sugg}", applicability = "machine-applicable")] - Quotes { + QuotesFull { #[primary_span] span: Span, is_byte: bool, sugg: String, }, + #[multipart_suggestion(parse_use_double_quotes, applicability = "machine-applicable")] + Quotes { + #[suggestion_part(code = "{prefix}\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + is_byte: bool, + prefix: &'static str, + }, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f57945a52df..63b2b47630b 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -63,6 +63,7 @@ pub(crate) fn parse_token_trees<'psess, 'src>( cursor, override_span, nbsp_is_whitespace: false, + last_lifetime: None, }; let (stream, res, unmatched_delims) = tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); @@ -105,6 +106,10 @@ struct StringReader<'psess, 'src> { /// in this file, it's safe to treat further occurrences of the non-breaking /// space character as whitespace. nbsp_is_whitespace: bool, + + /// Track the `Span` for the leading `'` of the last lifetime. Used for + /// diagnostics to detect possible typo where `"` was meant. + last_lifetime: Option<Span>, } impl<'psess, 'src> StringReader<'psess, 'src> { @@ -130,6 +135,18 @@ impl<'psess, 'src> StringReader<'psess, 'src> { debug!("next_token: {:?}({:?})", token.kind, self.str_from(start)); + if let rustc_lexer::TokenKind::Semi + | rustc_lexer::TokenKind::LineComment { .. } + | rustc_lexer::TokenKind::BlockComment { .. } + | rustc_lexer::TokenKind::CloseParen + | rustc_lexer::TokenKind::CloseBrace + | rustc_lexer::TokenKind::CloseBracket = token.kind + { + // Heuristic: we assume that it is unlikely we're dealing with an unterminated + // string surrounded by single quotes. + self.last_lifetime = None; + } + // Now "cook" the token, converting the simple `rustc_lexer::TokenKind` enum into a // rich `rustc_ast::TokenKind`. This turns strings into interned symbols and runs // additional validation. @@ -247,6 +264,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { // expansion purposes. See #12512 for the gory details of why // this is necessary. let lifetime_name = self.str_from(start); + self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); if starts_with_number { let span = self.mk_sp(start, self.pos); self.dcx().struct_err("lifetimes cannot start with a number") @@ -395,10 +413,21 @@ impl<'psess, 'src> StringReader<'psess, 'src> { match kind { rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { - self.dcx() + let mut err = self + .dcx() .struct_span_fatal(self.mk_sp(start, end), "unterminated character literal") - .with_code(E0762) - .emit() + .with_code(E0762); + if let Some(lt_sp) = self.last_lifetime { + err.multipart_suggestion( + "if you meant to write a string literal, use double quotes", + vec![ + (lt_sp, "\"".to_string()), + (self.mk_sp(start, start + BytePos(1)), "\"".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + err.emit() } self.cook_unicode(token::Char, Mode::Char, start, end, 1, 1) // ' ' } @@ -669,15 +698,33 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let expn_data = prefix_span.ctxt().outer_expn_data(); if expn_data.edition >= Edition::Edition2021 { + let mut silence = false; // In Rust 2021, this is a hard error. let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) } else if expn_data.is_root() { - Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + if self.cursor.first() == '\'' + && let Some(start) = self.last_lifetime + && self.cursor.third() != '\'' + { + // An "unclosed `char`" error will be emitted already, silence redundant error. + silence = true; + Some(errors::UnknownPrefixSugg::MeantStr { + start, + end: self.mk_sp(self.pos, self.pos + BytePos(1)), + }) + } else { + Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + } } else { None }; - self.dcx().emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg }); + let err = errors::UnknownPrefix { span: prefix_span, prefix, sugg }; + if silence { + self.dcx().create_err(err).delay_as_bug(); + } else { + self.dcx().emit_err(err); + } } else { // Before Rust 2021, only emit a lint for migration. self.psess.buffer_lint_with_diagnostic( diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 3ebad6a9fd7..fa242a32a18 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -95,11 +95,21 @@ pub(crate) fn emit_unescape_error( } escaped.push(c); } - let sugg = format!("{prefix}\"{escaped}\""); - MoreThanOneCharSugg::Quotes { - span: full_lit_span, - is_byte: mode == Mode::Byte, - sugg, + if escaped.len() != lit.len() || full_lit_span.is_empty() { + let sugg = format!("{prefix}\"{escaped}\""); + MoreThanOneCharSugg::QuotesFull { + span: full_lit_span, + is_byte: mode == Mode::Byte, + sugg, + } + } else { + MoreThanOneCharSugg::Quotes { + start: full_lit_span + .with_hi(full_lit_span.lo() + BytePos((prefix.len() + 1) as u32)), + end: full_lit_span.with_lo(full_lit_span.hi() - BytePos(1)), + is_byte: mode == Mode::Byte, + prefix, + } } }); dcx.emit_err(UnescapeError::MoreThanOneChar { diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index d08c50b5b06..ab5f51eedc3 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -3,11 +3,12 @@ use crate::errors::{ SuffixedLiteralInAttribute, }; use crate::fluent_generated as fluent; +use crate::maybe_whole; use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle}; use rustc_ast as ast; use rustc_ast::attr; -use rustc_ast::token::{self, Delimiter, Nonterminal}; +use rustc_ast::token::{self, Delimiter}; use rustc_errors::{codes::*, Diag, PResult}; use rustc_span::{sym, BytePos, Span}; use thin_vec::ThinVec; @@ -251,25 +252,15 @@ impl<'a> Parser<'a> { /// PATH `=` UNSUFFIXED_LIT /// The delimiters or `=` are still put into the resulting token stream. pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> { - let item = match &self.token.kind { - token::Interpolated(nt) => match &nt.0 { - Nonterminal::NtMeta(item) => Some(item.clone().into_inner()), - _ => None, - }, - _ => None, + maybe_whole!(self, NtMeta, |attr| attr.into_inner()); + + let do_parse = |this: &mut Self| { + let path = this.parse_path(PathStyle::Mod)?; + let args = this.parse_attr_args()?; + Ok(ast::AttrItem { path, args, tokens: None }) }; - Ok(if let Some(item) = item { - self.bump(); - item - } else { - let do_parse = |this: &mut Self| { - let path = this.parse_path(PathStyle::Mod)?; - let args = this.parse_attr_args()?; - Ok(ast::AttrItem { path, args, tokens: None }) - }; - // Attr items don't have attributes - if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) }? - }) + // Attr items don't have attributes + if capture_tokens { self.collect_tokens_no_attrs(do_parse) } else { do_parse(self) } } /// Parses attributes that appear after the opening of an item. These should @@ -371,22 +362,18 @@ impl<'a> Parser<'a> { /// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ; /// ``` pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> { - let nt_meta = match &self.token.kind { - token::Interpolated(nt) => match &nt.0 { - token::NtMeta(e) => Some(e.clone()), - _ => None, - }, - _ => None, - }; - - if let Some(item) = nt_meta { - match item.meta(item.path.span) { + // We can't use `maybe_whole` here because it would bump in the `None` + // case, which we don't want. + if let token::Interpolated(nt) = &self.token.kind + && let token::NtMeta(attr_item) = &nt.0 + { + match attr_item.meta(attr_item.path.span) { Some(meta) => { self.bump(); return Ok(meta); } None => self.unexpected()?, - }; + } } let lo = self.token.span; diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 136145dd182..fe17eba0d7b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -11,7 +11,7 @@ use crate::errors; use crate::maybe_recover_from_interpolated_ty_qpath; use ast::mut_visit::{noop_visit_expr, MutVisitor}; use ast::token::IdentIsRaw; -use ast::{CoroutineKind, ForLoopKind, GenBlockKind, Pat, Path, PathSegment}; +use ast::{CoroutineKind, ForLoopKind, GenBlockKind, MatchKind, Pat, Path, PathSegment}; use core::mem; use core::ops::ControlFlow; use rustc_ast::ptr::P; @@ -1379,6 +1379,13 @@ impl<'a> Parser<'a> { return Ok(self.mk_await_expr(self_arg, lo)); } + // Post-fix match + if self.eat_keyword(kw::Match) { + let match_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::postfix_match, match_span); + return self.parse_match_block(lo, match_span, self_arg, MatchKind::Postfix); + } + let fn_span_lo = self.token.span; let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]); @@ -1939,11 +1946,11 @@ impl<'a> Parser<'a> { /// Parse `builtin # ident(args,*)`. fn parse_expr_builtin(&mut self) -> PResult<'a, P<Expr>> { self.parse_builtin(|this, lo, ident| { - if ident.name == sym::offset_of { - return Ok(Some(this.parse_expr_offset_of(lo)?)); - } - - Ok(None) + Ok(match ident.name { + sym::offset_of => Some(this.parse_expr_offset_of(lo)?), + sym::type_ascribe => Some(this.parse_expr_type_ascribe(lo)?), + _ => None, + }) }) } @@ -1978,6 +1985,7 @@ impl<'a> Parser<'a> { ret } + /// Built-in macro for `offset_of!` expressions. pub(crate) fn parse_expr_offset_of(&mut self, lo: Span) -> PResult<'a, P<Expr>> { let container = self.parse_ty()?; self.expect(&TokenKind::Comma)?; @@ -2007,6 +2015,15 @@ impl<'a> Parser<'a> { Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields))) } + /// Built-in macro for type ascription expressions. + pub(crate) fn parse_expr_type_ascribe(&mut self, lo: Span) -> PResult<'a, P<Expr>> { + let expr = self.parse_expr()?; + self.expect(&token::Comma)?; + let ty = self.parse_ty()?; + let span = lo.to(self.token.span); + Ok(self.mk_expr(span, ExprKind::Type(expr, ty))) + } + /// Returns a string literal if the next token is a string literal. /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, /// and returns `None` if the next token is not literal at all. @@ -2043,16 +2060,6 @@ impl<'a> Parser<'a> { &mut self, mk_lit_char: impl FnOnce(Symbol, Span) -> L, ) -> PResult<'a, L> { - if let token::Interpolated(nt) = &self.token.kind - && let token::NtExpr(e) | token::NtLiteral(e) = &nt.0 - && matches!(e.kind, ExprKind::Err(_)) - { - let mut err = self - .dcx() - .create_err(errors::InvalidInterpolatedExpression { span: self.token.span }); - err.downgrade_to_delayed_bug(); - return Err(err); - } let token = self.token.clone(); let err = |self_: &Self| { let msg = format!("unexpected token: {}", super::token_descr(&token)); @@ -2894,8 +2901,20 @@ impl<'a> Parser<'a> { /// Parses a `match ... { ... }` expression (`match` token already eaten). fn parse_expr_match(&mut self) -> PResult<'a, P<Expr>> { let match_span = self.prev_token.span; - let lo = self.prev_token.span; let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + + self.parse_match_block(match_span, match_span, scrutinee, MatchKind::Prefix) + } + + /// Parses the block of a `match expr { ... }` or a `expr.match { ... }` + /// expression. This is after the match token and scrutinee are eaten + fn parse_match_block( + &mut self, + lo: Span, + match_span: Span, + scrutinee: P<Expr>, + match_kind: MatchKind, + ) -> PResult<'a, P<Expr>> { if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) { if self.token == token::Semi { e.span_suggestion_short( @@ -2938,7 +2957,7 @@ impl<'a> Parser<'a> { }); return Ok(self.mk_expr_with_attrs( span, - ExprKind::Match(scrutinee, arms), + ExprKind::Match(scrutinee, arms, match_kind), attrs, )); } @@ -2946,7 +2965,7 @@ impl<'a> Parser<'a> { } let hi = self.token.span; self.bump(); - Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs)) + Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms, match_kind), attrs)) } /// Attempt to recover from match arm body with statements and no surrounding braces. @@ -3955,7 +3974,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::While(_, _, _) | ExprKind::ForLoop { .. } | ExprKind::Loop(_, _, _) - | ExprKind::Match(_, _) + | ExprKind::Match(_, _, _) | ExprKind::Closure(_) | ExprKind::Block(_, _) | ExprKind::Gen(_, _, _) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 18d210eacfa..d54eb8dc4c9 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -6,6 +6,7 @@ use super::{ }; use crate::errors::{self, MacroExpandsToAdtField}; use crate::fluent_generated as fluent; +use crate::maybe_whole; use ast::token::IdentIsRaw; use rustc_ast::ast::*; use rustc_ast::ptr::P; @@ -115,17 +116,10 @@ impl<'a> Parser<'a> { fn_parse_mode: FnParseMode, force_collect: ForceCollect, ) -> PResult<'a, Option<Item>> { - // Don't use `maybe_whole` so that we have precise control - // over when we bump the parser - if let token::Interpolated(nt) = &self.token.kind - && let token::NtItem(item) = &nt.0 - { - let mut item = item.clone(); - self.bump(); - + maybe_whole!(self, NtItem, |item| { attrs.prepend_to_nt_inner(&mut item.attrs); - return Ok(Some(item.into_inner())); - }; + Some(item.into_inner()) + }); let item = self.collect_tokens_trailing_token(attrs, force_collect, |this: &mut Self, attrs| { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 125e77d8ee7..de83528b52c 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -20,7 +20,7 @@ pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; pub use path::PathStyle; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind}; +use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; @@ -93,12 +93,13 @@ pub enum TrailingToken { #[macro_export] macro_rules! maybe_whole { ($p:expr, $constructor:ident, |$x:ident| $e:expr) => { - if let token::Interpolated(nt) = &$p.token.kind { - if let token::$constructor(x) = &nt.0 { - let $x = x.clone(); - $p.bump(); - return Ok($e); - } + if let token::Interpolated(nt) = &$p.token.kind + && let token::$constructor(x) = &nt.0 + { + #[allow(unused_mut)] + let mut $x = x.clone(); + $p.bump(); + return Ok($e); } }; } @@ -449,6 +450,7 @@ impl<'a> Parser<'a> { parser } + #[inline] pub fn recovery(mut self, recovery: Recovery) -> Self { self.recovery = recovery; self @@ -461,6 +463,7 @@ impl<'a> Parser<'a> { /// /// Technically, this only needs to restrict eager recovery by doing lookahead at more tokens. /// But making the distinction is very subtle, and simply forbidding all recovery is a lot simpler to uphold. + #[inline] fn may_recover(&self) -> bool { matches!(self.recovery, Recovery::Allowed) } @@ -548,6 +551,7 @@ impl<'a> Parser<'a> { /// /// This method will automatically add `tok` to `expected_tokens` if `tok` is not /// encountered. + #[inline] fn check(&mut self, tok: &TokenKind) -> bool { let is_present = self.token == *tok; if !is_present { @@ -556,6 +560,7 @@ impl<'a> Parser<'a> { is_present } + #[inline] fn check_noexpect(&self, tok: &TokenKind) -> bool { self.token == *tok } @@ -564,6 +569,7 @@ impl<'a> Parser<'a> { /// /// the main purpose of this function is to reduce the cluttering of the suggestions list /// which using the normal eat method could introduce in some cases. + #[inline] pub fn eat_noexpect(&mut self, tok: &TokenKind) -> bool { let is_present = self.check_noexpect(tok); if is_present { @@ -573,6 +579,7 @@ impl<'a> Parser<'a> { } /// Consumes a token 'tok' if it exists. Returns whether the given token was present. + #[inline] pub fn eat(&mut self, tok: &TokenKind) -> bool { let is_present = self.check(tok); if is_present { @@ -583,11 +590,13 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, returns `true` without eating it. /// An expectation is also added for diagnostics purposes. + #[inline] fn check_keyword(&mut self, kw: Symbol) -> bool { self.expected_tokens.push(TokenType::Keyword(kw)); self.token.is_keyword(kw) } + #[inline] fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.check_keyword(kw) { return true; @@ -606,6 +615,7 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, eats it and returns `true`. /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes. // Public for rustfmt usage. + #[inline] pub fn eat_keyword(&mut self, kw: Symbol) -> bool { if self.check_keyword(kw) { self.bump(); @@ -618,6 +628,7 @@ impl<'a> Parser<'a> { /// Eats a keyword, optionally ignoring the case. /// If the case differs (and is ignored) an error is issued. /// This is useful for recovery. + #[inline] fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool { if self.eat_keyword(kw) { return true; @@ -635,6 +646,7 @@ impl<'a> Parser<'a> { false } + #[inline] fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { if self.token.is_keyword(kw) { self.bump(); @@ -656,6 +668,7 @@ impl<'a> Parser<'a> { self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()) } + #[inline] fn check_or_expected(&mut self, ok: bool, typ: TokenType) -> bool { if ok { true @@ -703,6 +716,7 @@ impl<'a> Parser<'a> { /// Checks to see if the next token is either `+` or `+=`. /// Otherwise returns `false`. + #[inline] fn check_plus(&mut self) -> bool { self.check_or_expected( self.token.is_like_plus(), @@ -1394,7 +1408,7 @@ impl<'a> Parser<'a> { /// so emit a proper diagnostic. // Public for rustfmt usage. pub fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> { - maybe_whole!(self, NtVis, |x| x.into_inner()); + maybe_whole!(self, NtVis, |vis| vis.into_inner()); if !self.eat_keyword(kw::Pub) { // We need a span for our `Spanned<VisibilityKind>`, but there's inherently no @@ -1571,8 +1585,21 @@ pub enum FlatToken { Empty, } -#[derive(Debug)] -pub enum ParseNtResult { - Nt(Nonterminal), +// Metavar captures of various kinds. +#[derive(Clone, Debug)] +pub enum ParseNtResult<NtType> { Tt(TokenTree), + Nt(NtType), +} + +impl<T> ParseNtResult<T> { + pub fn map_nt<F, U>(self, mut f: F) -> ParseNtResult<U> + where + F: FnMut(T) -> U, + { + match self { + ParseNtResult::Tt(tt) => ParseNtResult::Tt(tt), + ParseNtResult::Nt(nt) => ParseNtResult::Nt(f(nt)), + } + } } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index f1572a18a8b..36a00df7b44 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,5 +1,5 @@ use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, Nonterminal::*, NonterminalKind, Token}; +use rustc_ast::token::{self, Delimiter, Nonterminal, Nonterminal::*, NonterminalKind, Token}; use rustc_ast::HasTokens; use rustc_ast_pretty::pprust; use rustc_errors::PResult; @@ -66,15 +66,14 @@ impl<'a> Parser<'a> { token::Interpolated(nt) => may_be_ident(&nt.0), _ => false, }, - NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => { - match &token.kind { + NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => match &token.kind { token::Ident(..) | // box, ref, mut, and other identifiers (can stricten) token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern token::OpenDelim(Delimiter::Bracket) | // slice pattern token::BinOp(token::And) | // reference token::BinOp(token::Minus) | // negative literal token::AndAnd | // double reference - token::Literal(_) | // literal + token::Literal(_) | // literal token::DotDot | // range pattern (future compat) token::DotDotDot | // range pattern (future compat) token::ModSep | // path @@ -84,8 +83,7 @@ impl<'a> Parser<'a> { token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr), token::Interpolated(nt) => may_be_ident(&nt.0), _ => false, - } - } + }, NonterminalKind::Lifetime => match &token.kind { token::Lifetime(_) => true, token::Interpolated(nt) => { @@ -102,7 +100,10 @@ impl<'a> Parser<'a> { /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). Inlined because there is only one call /// site. #[inline] - pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseNtResult> { + pub fn parse_nonterminal( + &mut self, + kind: NonterminalKind, + ) -> PResult<'a, ParseNtResult<Nonterminal>> { // A `macro_rules!` invocation may pass a captured item/expr to a proc-macro, // which requires having captured tokens available. Since we cannot determine // in advance whether or not a proc-macro will be (transitively) invoked, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 88ae1b5420f..fbc28859535 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -435,7 +435,7 @@ impl<'a> Parser<'a> { syntax_loc: Option<PatternLocation>, ) -> PResult<'a, P<Pat>> { maybe_recover_from_interpolated_ty_qpath!(self, true); - maybe_whole!(self, NtPat, |x| x); + maybe_whole!(self, NtPat, |pat| pat); let mut lo = self.token.span; @@ -498,11 +498,14 @@ impl<'a> Parser<'a> { } else { PatKind::Lit(const_expr) } + } else if self.is_builtin() { + self.parse_pat_builtin()? + } // Don't eagerly error on semantically invalid tokens when matching // declarative macros, as the input to those doesn't have to be // semantically valid. For attribute/derive proc macros this is not the // case, so doing the recovery for them is fine. - } else if self.can_be_ident_pat() + else if self.can_be_ident_pat() || (self.is_lit_bad_ident().is_some() && self.may_recover()) { // Parse `ident @ pat` @@ -1119,6 +1122,21 @@ impl<'a> Parser<'a> { .contains(&self.token.kind) } + fn parse_pat_builtin(&mut self) -> PResult<'a, PatKind> { + self.parse_builtin(|self_, _lo, ident| { + Ok(match ident.name { + // builtin#deref(PAT) + sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt( + None, + RecoverComma::Yes, + RecoverColon::Yes, + CommaRecoveryMode::LikelyTuple, + )?)), + _ => None, + }) + }) + } + /// Parses `box pat` fn parse_pat_box(&mut self) -> PResult<'a, PatKind> { let box_span = self.prev_token.span; diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index fc907760531..6601011665b 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -40,8 +40,8 @@ impl<'a> Parser<'a> { })) } - /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of whether - /// or not we have attributes + /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of + /// whether or not we have attributes. // Public for `cfg_eval` macro expansion. pub fn parse_stmt_without_recovery( &mut self, @@ -51,18 +51,12 @@ impl<'a> Parser<'a> { let attrs = self.parse_outer_attributes()?; let lo = self.token.span; - // Don't use `maybe_whole` so that we have precise control - // over when we bump the parser - if let token::Interpolated(nt) = &self.token.kind - && let token::NtStmt(stmt) = &nt.0 - { - let mut stmt = stmt.clone(); - self.bump(); + maybe_whole!(self, NtStmt, |stmt| { stmt.visit_attrs(|stmt_attrs| { attrs.prepend_to_nt_inner(stmt_attrs); }); - return Ok(Some(stmt.into_inner())); - } + Some(stmt.into_inner()) + }); if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) { self.bump(); @@ -539,7 +533,7 @@ impl<'a> Parser<'a> { blk_mode: BlockCheckMode, can_be_struct_literal: bool, ) -> PResult<'a, (AttrVec, P<Block>)> { - maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x)); + maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block)); let maybe_ident = self.prev_token.clone(); self.maybe_recover_unexpected_block_label(); @@ -643,7 +637,7 @@ impl<'a> Parser<'a> { recover: AttemptLocalParseRecovery, ) -> PResult<'a, Option<Stmt>> { // Skip looking for a trailing semicolon when we have an interpolated statement. - maybe_whole!(self, NtStmt, |x| Some(x.into_inner())); + maybe_whole!(self, NtStmt, |stmt| Some(stmt.into_inner())); let Some(mut stmt) = self.parse_stmt_without_recovery(true, ForceCollect::No)? else { return Ok(None); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 2385c18c089..1cea32cb90f 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -250,7 +250,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, P<Ty>> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); - maybe_whole!(self, NtTy, |x| x); + maybe_whole!(self, NtTy, |ty| ty); let lo = self.token.span; let mut impl_dyn_multi = false; diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 8080216a252..4a1a2049083 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -48,7 +48,7 @@ impl NonConstExpr { Self::Match(TryDesugar(_)) => &[sym::const_try], // All other expressions are allowed. - Self::Loop(Loop | While) | Self::Match(Normal | FormatArgs) => &[], + Self::Loop(Loop | While) | Self::Match(Normal | Postfix | FormatArgs) => &[], }; Some(gates) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 350f7e166bf..80f2078fff2 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -525,15 +525,16 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { let tcx = self.tcx; let unconditionally_treat_fields_as_live = self.repr_unconditionally_treats_fields_as_live; let has_repr_simd = self.repr_has_repr_simd; + let effective_visibilities = &tcx.effective_visibilities(()); let live_fields = def.fields().iter().filter_map(|f| { let def_id = f.def_id; if unconditionally_treat_fields_as_live || (f.is_positional() && has_repr_simd) { return Some(def_id); } - if !tcx.visibility(f.hir_id.owner.def_id).is_public() { + if !effective_visibilities.is_reachable(f.hir_id.owner.def_id) { return None; } - if tcx.visibility(def_id).is_public() { Some(def_id) } else { None } + if effective_visibilities.is_reachable(def_id) { Some(def_id) } else { None } }); self.live_symbols.extend(live_fields); @@ -831,7 +832,7 @@ fn create_and_seed_worklist( .collect::<Vec<_>>(); let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { + for id in crate_items.free_items() { check_item(tcx, &mut worklist, &mut struct_constructors, &mut unsolved_impl_item, id); } @@ -1084,7 +1085,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { let module_items = tcx.hir_module_items(module); - for item in module_items.items() { + for item in module_items.free_items() { let def_kind = tcx.def_kind(item.owner_id); let mut dead_codes = Vec::new(); diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index d742ffc69e4..e6e52648d6f 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -264,7 +264,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_foreign_item(self, i) } - fn visit_local(&mut self, l: &'v hir::Local<'v>) { + fn visit_local(&mut self, l: &'v hir::LetStmt<'v>) { self.record("Local", Id::Node(l.hir_id), l); hir_visit::walk_local(self, l) } @@ -300,6 +300,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { Path, Tuple, Box, + Deref, Ref, Lit, Range, @@ -566,6 +567,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Path, Tuple, Box, + Deref, Ref, Lit, Range, diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index f0c3f7a385d..125084f4750 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -342,7 +342,7 @@ impl<'tcx> IrMaps<'tcx> { } impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { self.add_from_pat(local.pat); if local.els.is_some() { self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id)); @@ -1350,7 +1350,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // Checking for error conditions impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { self.check_unused_vars_in_pat(local.pat, None, None, |spans, hir_id, ln, var| { if local.init.is_some() { self.warn_about_dead_assign(spans, hir_id, ln, var); diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index b7135de08ba..c2e604b02b3 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -437,7 +437,7 @@ fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet { // trait is a lang item. let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { + for id in crate_items.free_items() { check_item(tcx, id, &mut reachable_context.worklist, effective_visibilities); } diff --git a/compiler/rustc_pattern_analysis/Cargo.toml b/compiler/rustc_pattern_analysis/Cargo.toml index b9bdcb41929..6357d18b9da 100644 --- a/compiler/rustc_pattern_analysis/Cargo.toml +++ b/compiler/rustc_pattern_analysis/Cargo.toml @@ -22,6 +22,10 @@ smallvec = { version = "1.8.1", features = ["union"] } tracing = "0.1" # tidy-alphabetical-end +[dev-dependencies] +tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "ansi"] } +tracing-tree = "0.2.0" + [features] default = ["rustc"] rustc = [ diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 5b58d7f43b2..95c5556410d 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -819,6 +819,81 @@ impl<Cx: PatCx> Constructor<Cx> { } }) } + + pub(crate) fn fmt_fields( + &self, + f: &mut fmt::Formatter<'_>, + ty: &Cx::Ty, + mut fields: impl Iterator<Item = impl fmt::Debug>, + ) -> fmt::Result { + let mut first = true; + let mut start_or_continue = |s| { + if first { + first = false; + "" + } else { + s + } + }; + let mut start_or_comma = || start_or_continue(", "); + + match self { + Struct | Variant(_) | UnionField => { + Cx::write_variant_name(f, self, ty)?; + // Without `cx`, we can't know which field corresponds to which, so we can't + // get the names of the fields. Instead we just display everything as a tuple + // struct, which should be good enough. + write!(f, "(")?; + for p in fields { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + write!(f, ")")?; + } + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to detect strings here. However a string literal pattern will never + // be reported as a non-exhaustiveness witness, so we can ignore this issue. + Ref => { + write!(f, "&{:?}", &fields.next().unwrap())?; + } + Slice(slice) => { + write!(f, "[")?; + match slice.kind { + SliceKind::FixedLen(_) => { + for p in fields { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + } + SliceKind::VarLen(prefix_len, _) => { + for p in fields.by_ref().take(prefix_len) { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + write!(f, "{}..", start_or_comma())?; + for p in fields { + write!(f, "{}{:?}", start_or_comma(), p)?; + } + } + } + write!(f, "]")?; + } + Bool(b) => write!(f, "{b}")?, + // Best-effort, will render signed ranges incorrectly + IntRange(range) => write!(f, "{range:?}")?, + F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?, + F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?, + Str(value) => write!(f, "{value:?}")?, + Opaque(..) => write!(f, "<constant pattern>")?, + Or => { + for pat in fields { + write!(f, "{}{:?}", start_or_continue(" | "), pat)?; + } + } + Never => write!(f, "!")?, + Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => { + write!(f, "_ : {:?}", ty)? + } + } + Ok(()) + } } #[derive(Debug, Clone, Copy)] diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 5c57c990323..1a1da5c55f6 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -49,6 +49,12 @@ pub mod index { } } + impl<V> FromIterator<V> for IdxContainer<usize, V> { + fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self { + Self(iter.into_iter().enumerate().collect()) + } + } + #[derive(Debug)] pub struct IdxSet<T>(pub rustc_hash::FxHashSet<T>); impl<T: Idx> IdxSet<T> { @@ -120,7 +126,8 @@ pub trait PatCx: Sized + fmt::Debug { /// `DeconstructedPat`. Only invoqued when `pat.ctor()` is `Struct | Variant(_) | UnionField`. fn write_variant_name( f: &mut fmt::Formatter<'_>, - pat: &crate::pat::DeconstructedPat<Self>, + ctor: &crate::constructor::Constructor<Self>, + ty: &Self::Ty, ) -> fmt::Result; /// Raise a bug. diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index e7eed673c94..5f388ee9f89 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -138,81 +138,11 @@ impl<Cx: PatCx> DeconstructedPat<Cx> { /// This is best effort and not good enough for a `Display` impl. impl<Cx: PatCx> fmt::Debug for DeconstructedPat<Cx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let pat = self; - let mut first = true; - let mut start_or_continue = |s| { - if first { - first = false; - "" - } else { - s - } - }; - let mut start_or_comma = || start_or_continue(", "); - let mut fields: Vec<_> = (0..self.arity).map(|_| PatOrWild::Wild).collect(); for ipat in self.iter_fields() { fields[ipat.idx] = PatOrWild::Pat(&ipat.pat); } - - match pat.ctor() { - Struct | Variant(_) | UnionField => { - Cx::write_variant_name(f, pat)?; - // Without `cx`, we can't know which field corresponds to which, so we can't - // get the names of the fields. Instead we just display everything as a tuple - // struct, which should be good enough. - write!(f, "(")?; - for p in fields { - write!(f, "{}", start_or_comma())?; - write!(f, "{p:?}")?; - } - write!(f, ")") - } - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to detect strings here. However a string literal pattern will never - // be reported as a non-exhaustiveness witness, so we can ignore this issue. - Ref => { - write!(f, "&{:?}", &fields[0]) - } - Slice(slice) => { - write!(f, "[")?; - match slice.kind { - SliceKind::FixedLen(_) => { - for p in fields { - write!(f, "{}{:?}", start_or_comma(), p)?; - } - } - SliceKind::VarLen(prefix_len, _) => { - for p in &fields[..prefix_len] { - write!(f, "{}{:?}", start_or_comma(), p)?; - } - write!(f, "{}", start_or_comma())?; - write!(f, "..")?; - for p in &fields[prefix_len..] { - write!(f, "{}{:?}", start_or_comma(), p)?; - } - } - } - write!(f, "]") - } - Bool(b) => write!(f, "{b}"), - // Best-effort, will render signed ranges incorrectly - IntRange(range) => write!(f, "{range:?}"), - F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), - F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"), - Str(value) => write!(f, "{value:?}"), - Opaque(..) => write!(f, "<constant pattern>"), - Or => { - for pat in fields { - write!(f, "{}{:?}", start_or_continue(" | "), pat)?; - } - Ok(()) - } - Never => write!(f, "!"), - Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => { - write!(f, "_ : {:?}", pat.ty()) - } - } + self.ctor().fmt_fields(f, self.ty(), fields.into_iter()) } } @@ -295,7 +225,6 @@ impl<'p, Cx: PatCx> fmt::Debug for PatOrWild<'p, Cx> { /// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics /// purposes. As such they don't use interning and can be cloned. -#[derive(Debug)] pub struct WitnessPat<Cx: PatCx> { ctor: Constructor<Cx>, pub(crate) fields: Vec<WitnessPat<Cx>>, @@ -353,3 +282,10 @@ impl<Cx: PatCx> WitnessPat<Cx> { self.fields.iter() } } + +/// This is best effort and not good enough for a `Display` impl. +impl<Cx: PatCx> fmt::Debug for WitnessPat<Cx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.ctor().fmt_fields(f, self.ty(), self.fields.iter()) + } +} diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index eedc00a5613..b0f506c3651 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -398,7 +398,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ty::Float(_) | ty::Str | ty::Foreign(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) | ty::Dynamic(_, _, _) @@ -462,6 +462,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty), }; } + PatKind::DerefPattern { .. } => { + // FIXME(deref_patterns): At least detect that `box _` is irrefutable. + fields = vec![]; + arity = 0; + ctor = Opaque(OpaqueId::new()); + } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { match ty.kind() { ty::Tuple(fs) => { @@ -874,13 +880,14 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { fn write_variant_name( f: &mut fmt::Formatter<'_>, - pat: &crate::pat::DeconstructedPat<Self>, + ctor: &crate::constructor::Constructor<Self>, + ty: &Self::Ty, ) -> fmt::Result { - if let ty::Adt(adt, _) = pat.ty().kind() { + if let ty::Adt(adt, _) = ty.kind() { if adt.is_box() { write!(f, "Box")? } else { - let variant = adt.variant(Self::variant_index_for_adt(pat.ctor(), *adt)); + let variant = adt.variant(Self::variant_index_for_adt(ctor, *adt)); write!(f, "{}", variant.name)?; } } diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 3760db8b688..cdc03eaeb37 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -1042,7 +1042,7 @@ struct MatrixRow<'p, Cx: PatCx> { is_under_guard: bool, /// When we specialize, we remember which row of the original matrix produced a given row of the /// specialized matrix. When we unspecialize, we use this to propagate usefulness back up the - /// callstack. + /// callstack. On creation, this stores the index of the original match arm. parent_row: usize, /// False when the matrix is just built. This is set to `true` by /// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful. @@ -1163,10 +1163,10 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> { place_info: smallvec![place_info], wildcard_row_is_relevant: true, }; - for (row_id, arm) in arms.iter().enumerate() { + for (arm_id, arm) in arms.iter().enumerate() { let v = MatrixRow { pats: PatStack::from_pattern(arm.pat), - parent_row: row_id, // dummy, we don't read it + parent_row: arm_id, is_under_guard: arm.has_guard, useful: false, intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`. @@ -1738,6 +1738,9 @@ pub struct UsefulnessReport<'p, Cx: PatCx> { /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// exhaustiveness. pub non_exhaustiveness_witnesses: Vec<WitnessPat<Cx>>, + /// For each arm, a set of indices of arms above it that have non-empty intersection, i.e. there + /// is a value matched by both arms. This may miss real intersections. + pub arm_intersections: Vec<BitSet<usize>>, } /// Computes whether a match is exhaustive and which of its arms are useful. @@ -1769,5 +1772,19 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>( }) .collect(); - Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }) + let mut arm_intersections: Vec<_> = + arms.iter().enumerate().map(|(i, _)| BitSet::new_empty(i)).collect(); + for row in matrix.rows() { + let arm_id = row.parent_row; + for intersection in row.intersects.iter() { + // Convert the matrix row ids into arm ids (they can differ because we expand or-patterns). + let arm_intersection = matrix.rows[intersection].parent_row; + // Note: self-intersection can happen with or-patterns. + if arm_intersection != arm_id { + arm_intersections[arm_id].insert(arm_intersection); + } + } + } + + Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses, arm_intersections }) } diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs new file mode 100644 index 00000000000..e72fddb9e9a --- /dev/null +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -0,0 +1,315 @@ +use rustc_pattern_analysis::{ + constructor::{ + Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility, + }, + usefulness::{PlaceValidity, UsefulnessReport}, + Captures, MatchArm, PatCx, PrivateUninhabitedField, +}; + +/// Sets up `tracing` for easier debugging. Tries to look like the `rustc` setup. +pub fn init_tracing() { + use tracing_subscriber::layer::SubscriberExt; + use tracing_subscriber::util::SubscriberInitExt; + use tracing_subscriber::Layer; + let _ = tracing_tree::HierarchicalLayer::default() + .with_writer(std::io::stderr) + .with_indent_lines(true) + .with_ansi(true) + .with_targets(true) + .with_indent_amount(2) + .with_subscriber( + tracing_subscriber::Registry::default() + .with(tracing_subscriber::EnvFilter::from_default_env()), + ) + .try_init(); +} + +/// A simple set of types. +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum Ty { + /// Booleans + Bool, + /// 8-bit unsigned integers + U8, + /// Tuples. + Tuple(&'static [Ty]), + /// A struct with `arity` fields of type `ty`. + BigStruct { arity: usize, ty: &'static Ty }, + /// A enum with `arity` variants of type `ty`. + BigEnum { arity: usize, ty: &'static Ty }, +} + +/// The important logic. +impl Ty { + pub fn sub_tys(&self, ctor: &Constructor<Cx>) -> Vec<Self> { + use Constructor::*; + match (ctor, *self) { + (Struct, Ty::Tuple(tys)) => tys.iter().copied().collect(), + (Struct, Ty::BigStruct { arity, ty }) => (0..arity).map(|_| *ty).collect(), + (Variant(_), Ty::BigEnum { ty, .. }) => vec![*ty], + (Bool(..) | IntRange(..) | NonExhaustive | Missing | Wildcard, _) => vec![], + _ => panic!("Unexpected ctor {ctor:?} for type {self:?}"), + } + } + + pub fn ctor_set(&self) -> ConstructorSet<Cx> { + match *self { + Ty::Bool => ConstructorSet::Bool, + Ty::U8 => ConstructorSet::Integers { + range_1: IntRange::from_range( + MaybeInfiniteInt::new_finite_uint(0), + MaybeInfiniteInt::new_finite_uint(255), + RangeEnd::Included, + ), + range_2: None, + }, + Ty::Tuple(..) | Ty::BigStruct { .. } => ConstructorSet::Struct { empty: false }, + Ty::BigEnum { arity, .. } => ConstructorSet::Variants { + variants: (0..arity).map(|_| VariantVisibility::Visible).collect(), + non_exhaustive: false, + }, + } + } + + pub fn write_variant_name( + &self, + f: &mut std::fmt::Formatter<'_>, + ctor: &Constructor<Cx>, + ) -> std::fmt::Result { + match (*self, ctor) { + (Ty::Tuple(..), _) => Ok(()), + (Ty::BigStruct { .. }, _) => write!(f, "BigStruct"), + (Ty::BigEnum { .. }, Constructor::Variant(i)) => write!(f, "BigEnum::Variant{i}"), + _ => write!(f, "{:?}::{:?}", self, ctor), + } + } +} + +/// Compute usefulness in our simple context (and set up tracing for easier debugging). +pub fn compute_match_usefulness<'p>( + arms: &[MatchArm<'p, Cx>], + ty: Ty, + scrut_validity: PlaceValidity, + complexity_limit: Option<usize>, +) -> Result<UsefulnessReport<'p, Cx>, ()> { + init_tracing(); + rustc_pattern_analysis::usefulness::compute_match_usefulness( + &Cx, + arms, + ty, + scrut_validity, + complexity_limit, + ) +} + +#[derive(Debug)] +pub struct Cx; + +/// The context for pattern analysis. Forwards anything interesting to `Ty` methods. +impl PatCx for Cx { + type Ty = Ty; + type Error = (); + type VariantIdx = usize; + type StrLit = (); + type ArmData = (); + type PatData = (); + + fn is_exhaustive_patterns_feature_on(&self) -> bool { + false + } + + fn is_min_exhaustive_patterns_feature_on(&self) -> bool { + false + } + + fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize { + ty.sub_tys(ctor).len() + } + + fn ctor_sub_tys<'a>( + &'a self, + ctor: &'a Constructor<Self>, + ty: &'a Self::Ty, + ) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator + Captures<'a> + { + ty.sub_tys(ctor).into_iter().map(|ty| (ty, PrivateUninhabitedField(false))) + } + + fn ctors_for_ty(&self, ty: &Self::Ty) -> Result<ConstructorSet<Self>, Self::Error> { + Ok(ty.ctor_set()) + } + + fn write_variant_name( + f: &mut std::fmt::Formatter<'_>, + ctor: &Constructor<Self>, + ty: &Self::Ty, + ) -> std::fmt::Result { + ty.write_variant_name(f, ctor) + } + + fn bug(&self, fmt: std::fmt::Arguments<'_>) -> Self::Error { + panic!("{}", fmt) + } + + /// Abort when reaching the complexity limit. This is what we'll check in tests. + fn complexity_exceeded(&self) -> Result<(), Self::Error> { + Err(()) + } +} + +/// Construct a single pattern; see `pats!()`. +#[allow(unused_macros)] +macro_rules! pat { + ($($rest:tt)*) => {{ + let mut vec = pats!($($rest)*); + vec.pop().unwrap() + }}; +} + +/// A macro to construct patterns. Called like `pats!(type_expr; pattern, pattern, ..)` and returns +/// a `Vec<DeconstructedPat>`. A pattern can be nested and looks like `Constructor(pat, pat)` or +/// `Constructor { .i: pat, .j: pat }`, where `Constructor` is `Struct`, `Variant.i` (with index +/// `i`), as well as booleans and integer ranges. +/// +/// The general structure of the macro is a tt-muncher with several stages identified with +/// `@something(args)`. The args are a key-value list (the keys ensure we don't mix the arguments +/// around) which is passed down and modified as needed. We then parse token-trees from +/// left-to-right. Non-trivial recursion happens when we parse the arguments to a pattern: we +/// recurse to parse the tokens inside `{..}`/`(..)`, and then we continue parsing anything that +/// follows. +macro_rules! pats { + // Entrypoint + // Parse `type; ..` + ($ty:expr; $($rest:tt)*) => {{ + #[allow(unused_imports)] + use rustc_pattern_analysis::{ + constructor::{Constructor, IntRange, MaybeInfiniteInt, RangeEnd}, + pat::DeconstructedPat, + }; + let ty = $ty; + // The heart of the macro is designed to push `IndexedPat`s into a `Vec`, so we work around + // that. + let sub_tys = ::std::iter::repeat(&ty); + let mut vec = Vec::new(); + pats!(@ctor(vec:vec, sub_tys:sub_tys, idx:0) $($rest)*); + vec.into_iter().map(|ipat| ipat.pat).collect::<Vec<_>>() + }}; + + // Parse `constructor ..` + + (@ctor($($args:tt)*) true $($rest:tt)*) => {{ + let ctor = Constructor::Bool(true); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) false $($rest:tt)*) => {{ + let ctor = Constructor::Bool(false); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) Struct $($rest:tt)*) => {{ + let ctor = Constructor::Struct; + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) ( $($fields:tt)* ) $($rest:tt)*) => {{ + let ctor = Constructor::Struct; // tuples + pats!(@pat($($args)*, ctor:ctor) ( $($fields)* ) $($rest)*) + }}; + (@ctor($($args:tt)*) Variant.$variant:ident $($rest:tt)*) => {{ + let ctor = Constructor::Variant($variant); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) Variant.$variant:literal $($rest:tt)*) => {{ + let ctor = Constructor::Variant($variant); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) _ $($rest:tt)*) => {{ + let ctor = Constructor::Wildcard; + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + + // Integers and int ranges + (@ctor($($args:tt)*) $($start:literal)?..$end:literal $($rest:tt)*) => {{ + let ctor = Constructor::IntRange(IntRange::from_range( + pats!(@rangeboundary- $($start)?), + pats!(@rangeboundary+ $end), + RangeEnd::Excluded, + )); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) $($start:literal)?.. $($rest:tt)*) => {{ + let ctor = Constructor::IntRange(IntRange::from_range( + pats!(@rangeboundary- $($start)?), + pats!(@rangeboundary+), + RangeEnd::Excluded, + )); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) $($start:literal)?..=$end:literal $($rest:tt)*) => {{ + let ctor = Constructor::IntRange(IntRange::from_range( + pats!(@rangeboundary- $($start)?), + pats!(@rangeboundary+ $end), + RangeEnd::Included, + )); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + (@ctor($($args:tt)*) $int:literal $($rest:tt)*) => {{ + let ctor = Constructor::IntRange(IntRange::from_range( + pats!(@rangeboundary- $int), + pats!(@rangeboundary+ $int), + RangeEnd::Included, + )); + pats!(@pat($($args)*, ctor:ctor) $($rest)*) + }}; + // Utility to manage range boundaries. + (@rangeboundary $sign:tt $int:literal) => { MaybeInfiniteInt::new_finite_uint($int) }; + (@rangeboundary -) => { MaybeInfiniteInt::NegInfinity }; + (@rangeboundary +) => { MaybeInfiniteInt::PosInfinity }; + + // Parse subfields: `(..)` or `{..}` + + // Constructor with no fields, e.g. `bool` or `Variant.1`. + (@pat($($args:tt)*) $(,)?) => { + pats!(@pat($($args)*) {}) + }; + (@pat($($args:tt)*) , $($rest:tt)*) => { + pats!(@pat($($args)*) {}, $($rest)*) + }; + // `(..)` and `{..}` are treated the same. + (@pat($($args:tt)*) ( $($subpat:tt)* ) $($rest:tt)*) => {{ + pats!(@pat($($args)*) { $($subpat)* } $($rest)*) + }}; + (@pat(vec:$vec:expr, sub_tys:$sub_tys:expr, idx:$idx:expr, ctor:$ctor:expr) { $($fields:tt)* } $($rest:tt)*) => {{ + let sub_tys = $sub_tys; + let index = $idx; + // Silly dance to work with both a vec and `iter::repeat()`. + let ty = *(&sub_tys).clone().into_iter().nth(index).unwrap(); + let ctor = $ctor; + let ctor_sub_tys = &ty.sub_tys(&ctor); + #[allow(unused_mut)] + let mut fields = Vec::new(); + // Parse subpatterns (note the leading comma). + pats!(@fields(idx:0, vec:fields, sub_tys:ctor_sub_tys) ,$($fields)*); + let arity = ctor_sub_tys.len(); + let pat = DeconstructedPat::new(ctor, fields, arity, ty, ()).at_index(index); + $vec.push(pat); + + // Continue parsing further patterns. + pats!(@fields(idx:index+1, vec:$vec, sub_tys:sub_tys) $($rest)*); + }}; + + // Parse fields one by one. + + // No fields left. + (@fields($($args:tt)*) $(,)?) => {}; + // `.i: pat` sets the current index to `i`. + (@fields(idx:$_idx:expr, $($args:tt)*) , .$idx:literal : $($rest:tt)*) => {{ + pats!(@ctor($($args)*, idx:$idx) $($rest)*); + }}; + (@fields(idx:$_idx:expr, $($args:tt)*) , .$idx:ident : $($rest:tt)*) => {{ + pats!(@ctor($($args)*, idx:$idx) $($rest)*); + }}; + // Field without an explicit index; we use the current index which gets incremented above. + (@fields(idx:$idx:expr, $($args:tt)*) , $($rest:tt)*) => {{ + pats!(@ctor($($args)*, idx:$idx) $($rest)*); + }}; +} diff --git a/compiler/rustc_pattern_analysis/tests/complexity.rs b/compiler/rustc_pattern_analysis/tests/complexity.rs new file mode 100644 index 00000000000..93f455c6257 --- /dev/null +++ b/compiler/rustc_pattern_analysis/tests/complexity.rs @@ -0,0 +1,109 @@ +//! Test the pattern complexity limit. +use common::*; +use rustc_pattern_analysis::{pat::DeconstructedPat, usefulness::PlaceValidity, MatchArm}; + +#[macro_use] +mod common; + +/// Analyze a match made of these patterns. Ignore the report; we only care whether we exceeded the +/// limit or not. +fn check(patterns: &[DeconstructedPat<Cx>], complexity_limit: usize) -> Result<(), ()> { + let ty = *patterns[0].ty(); + let arms: Vec<_> = + patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); + compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, Some(complexity_limit)) + .map(|_report| ()) +} + +/// Asserts that analyzing this match takes exactly `complexity` steps. +#[track_caller] +fn assert_complexity(patterns: Vec<DeconstructedPat<Cx>>, complexity: usize) { + assert!(check(&patterns, complexity).is_ok()); + assert!(check(&patterns, complexity - 1).is_err()); +} + +/// Construct a match like: +/// ```ignore(illustrative) +/// match ... { +/// BigStruct { field01: true, .. } => {} +/// BigStruct { field02: true, .. } => {} +/// BigStruct { field03: true, .. } => {} +/// BigStruct { field04: true, .. } => {} +/// ... +/// _ => {} +/// } +/// ``` +fn diagonal_match(arity: usize) -> Vec<DeconstructedPat<Cx>> { + let struct_ty = Ty::BigStruct { arity, ty: &Ty::Bool }; + let mut patterns = vec![]; + for i in 0..arity { + patterns.push(pat!(struct_ty; Struct { .i: true })); + } + patterns.push(pat!(struct_ty; _)); + patterns +} + +/// Construct a match like: +/// ```ignore(illustrative) +/// match ... { +/// BigStruct { field01: true, .. } => {} +/// BigStruct { field02: true, .. } => {} +/// BigStruct { field03: true, .. } => {} +/// BigStruct { field04: true, .. } => {} +/// ... +/// BigStruct { field01: false, .. } => {} +/// BigStruct { field02: false, .. } => {} +/// BigStruct { field03: false, .. } => {} +/// BigStruct { field04: false, .. } => {} +/// ... +/// _ => {} +/// } +/// ``` +fn diagonal_exponential_match(arity: usize) -> Vec<DeconstructedPat<Cx>> { + let struct_ty = Ty::BigStruct { arity, ty: &Ty::Bool }; + let mut patterns = vec![]; + for i in 0..arity { + patterns.push(pat!(struct_ty; Struct { .i: true })); + } + for i in 0..arity { + patterns.push(pat!(struct_ty; Struct { .i: false })); + } + patterns.push(pat!(struct_ty; _)); + patterns +} + +#[test] +fn test_diagonal_struct_match() { + // These cases are nicely linear: we check `arity` patterns with exactly one `true`, matching + // in 2 branches each, and a final pattern with all `false`, matching only the `_` branch. + assert_complexity(diagonal_match(20), 41); + assert_complexity(diagonal_match(30), 61); + // This case goes exponential. + assert!(check(&diagonal_exponential_match(10), 10000).is_err()); +} + +/// Construct a match like: +/// ```ignore(illustrative) +/// match ... { +/// BigEnum::Variant1(_) => {} +/// BigEnum::Variant2(_) => {} +/// BigEnum::Variant3(_) => {} +/// ... +/// _ => {} +/// } +/// ``` +fn big_enum(arity: usize) -> Vec<DeconstructedPat<Cx>> { + let enum_ty = Ty::BigEnum { arity, ty: &Ty::Bool }; + let mut patterns = vec![]; + for i in 0..arity { + patterns.push(pat!(enum_ty; Variant.i)); + } + patterns.push(pat!(enum_ty; _)); + patterns +} + +#[test] +fn test_big_enum() { + // We try 2 branches per variant. + assert_complexity(big_enum(20), 40); +} diff --git a/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs new file mode 100644 index 00000000000..4c6c72fa8ec --- /dev/null +++ b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs @@ -0,0 +1,77 @@ +//! Test exhaustiveness checking. +use common::*; +use rustc_pattern_analysis::{ + pat::{DeconstructedPat, WitnessPat}, + usefulness::PlaceValidity, + MatchArm, +}; + +#[macro_use] +mod common; + +/// Analyze a match made of these patterns. +fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> { + let ty = *patterns[0].ty(); + let arms: Vec<_> = + patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); + let report = + compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, None).unwrap(); + report.non_exhaustiveness_witnesses +} + +#[track_caller] +fn assert_exhaustive(patterns: Vec<DeconstructedPat<Cx>>) { + let witnesses = check(patterns); + if !witnesses.is_empty() { + panic!("non-exaustive match: missing {witnesses:?}"); + } +} + +#[track_caller] +fn assert_non_exhaustive(patterns: Vec<DeconstructedPat<Cx>>) { + let witnesses = check(patterns); + assert!(!witnesses.is_empty()) +} + +#[test] +fn test_int_ranges() { + let ty = Ty::U8; + assert_exhaustive(pats!(ty; + 0..=255, + )); + assert_exhaustive(pats!(ty; + 0.., + )); + assert_non_exhaustive(pats!(ty; + 0..255, + )); + assert_exhaustive(pats!(ty; + 0..255, + 255, + )); + assert_exhaustive(pats!(ty; + ..10, + 10.. + )); +} + +#[test] +fn test_nested() { + let ty = Ty::BigStruct { arity: 2, ty: &Ty::BigEnum { arity: 2, ty: &Ty::Bool } }; + assert_non_exhaustive(pats!(ty; + Struct(Variant.0, _), + )); + assert_exhaustive(pats!(ty; + Struct(Variant.0, _), + Struct(Variant.1, _), + )); + assert_non_exhaustive(pats!(ty; + Struct(Variant.0, _), + Struct(_, Variant.0), + )); + assert_exhaustive(pats!(ty; + Struct(Variant.0, _), + Struct(_, Variant.0), + Struct(Variant.1, Variant.1), + )); +} diff --git a/compiler/rustc_pattern_analysis/tests/intersection.rs b/compiler/rustc_pattern_analysis/tests/intersection.rs new file mode 100644 index 00000000000..4d8a21506d7 --- /dev/null +++ b/compiler/rustc_pattern_analysis/tests/intersection.rs @@ -0,0 +1,69 @@ +//! Test the computation of arm intersections. +use common::*; +use rustc_pattern_analysis::{pat::DeconstructedPat, usefulness::PlaceValidity, MatchArm}; + +#[macro_use] +mod common; + +/// Analyze a match made of these patterns and returns the computed arm intersections. +fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<Vec<usize>> { + let ty = *patterns[0].ty(); + let arms: Vec<_> = + patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); + let report = + compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, None).unwrap(); + report.arm_intersections.into_iter().map(|bitset| bitset.iter().collect()).collect() +} + +#[track_caller] +fn assert_intersects(patterns: Vec<DeconstructedPat<Cx>>, intersects: &[&[usize]]) { + let computed_intersects = check(patterns); + assert_eq!(computed_intersects, intersects); +} + +#[test] +fn test_int_ranges() { + let ty = Ty::U8; + assert_intersects( + pats!(ty; + 0..=100, + 100.., + ), + &[&[], &[0]], + ); + assert_intersects( + pats!(ty; + 0..=101, + 100.., + ), + &[&[], &[0]], + ); + assert_intersects( + pats!(ty; + 0..100, + 100.., + ), + &[&[], &[]], + ); +} + +#[test] +fn test_nested() { + let ty = Ty::Tuple(&[Ty::Bool; 2]); + assert_intersects( + pats!(ty; + (true, true), + (true, _), + (_, true), + ), + &[&[], &[0], &[0, 1]], + ); + // Here we shortcut because `(true, true)` is irrelevant, so we fail to detect the intersection. + assert_intersects( + pats!(ty; + (true, _), + (_, true), + ), + &[&[], &[]], + ); +} diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 073982ca5c3..41d63407418 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1209,7 +1209,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { intravisit::walk_pat(self, pattern); } - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { if let Some(init) = local.init { if self.check_expr_pat_type(init.hir_id, init.span) { // Do not report duplicate errors for `let x = y`. @@ -1696,7 +1696,7 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { } } - for id in module.items() { + for id in module.free_items() { if let ItemKind::Impl(i) = tcx.hir().item(id).kind { if let Some(item) = i.of_trait { let trait_ref = tcx.impl_trait_ref(id.owner_id.def_id).unwrap(); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c661be3587e..b2b339d2521 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -22,7 +22,7 @@ use rustc_errors::{ use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_hir::{PrimTy, TraitCandidate}; +use rustc_hir::{MissingLifetimeKind, PrimTy, TraitCandidate}; use rustc_middle::middle::resolve_bound_vars::Set1; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; @@ -44,9 +44,7 @@ type Res = def::Res<NodeId>; type IdentMap<T> = FxHashMap<Ident, T>; -use diagnostics::{ - ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime, MissingLifetimeKind, -}; +use diagnostics::{ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime}; #[derive(Copy, Clone, Debug)] struct BindingInfo { @@ -1637,22 +1635,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) { debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime); - let missing_lifetime = MissingLifetime { - id: lifetime.id, - span: lifetime.ident.span, - kind: if elided { - MissingLifetimeKind::Ampersand - } else { - MissingLifetimeKind::Underscore - }, - count: 1, - }; + let kind = + if elided { MissingLifetimeKind::Ampersand } else { MissingLifetimeKind::Underscore }; + let missing_lifetime = + MissingLifetime { id: lifetime.id, span: lifetime.ident.span, kind, count: 1 }; let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for (i, rib) in self.lifetime_ribs.iter().enumerate().rev() { debug!(?rib.kind); match rib.kind { LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { - let res = self.create_fresh_lifetime(lifetime.ident, binder); + let res = self.create_fresh_lifetime(lifetime.ident, binder, kind); self.record_lifetime_res(lifetime.id, res, elision_candidate); return; } @@ -1744,13 +1736,18 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn create_fresh_lifetime(&mut self, ident: Ident, binder: NodeId) -> LifetimeRes { + fn create_fresh_lifetime( + &mut self, + ident: Ident, + binder: NodeId, + kind: MissingLifetimeKind, + ) -> LifetimeRes { debug_assert_eq!(ident.name, kw::UnderscoreLifetime); debug!(?ident.span); // Leave the responsibility to create the `LocalDefId` to lowering. let param = self.r.next_node_id(); - let res = LifetimeRes::Fresh { param, binder }; + let res = LifetimeRes::Fresh { param, binder, kind }; self.record_lifetime_param(param, res); // Record the created lifetime parameter so lowering can pick it up and add it to HIR. @@ -1844,14 +1841,15 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }; let ident = Ident::new(kw::UnderscoreLifetime, elided_lifetime_span); + let kind = if segment.has_generic_args { + MissingLifetimeKind::Comma + } else { + MissingLifetimeKind::Brackets + }; let missing_lifetime = MissingLifetime { id: node_ids.start, span: elided_lifetime_span, - kind: if segment.has_generic_args { - MissingLifetimeKind::Comma - } else { - MissingLifetimeKind::Brackets - }, + kind, count: expected_lifetimes, }; let mut should_lint = true; @@ -1897,7 +1895,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { // Group all suggestions into the first record. let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime); for id in node_ids { - let res = self.create_fresh_lifetime(ident, binder); + let res = self.create_fresh_lifetime(ident, binder, kind); self.record_lifetime_res( id, res, @@ -4672,7 +4670,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { return; } - if path.iter().any(|seg| seg.ident.span.from_expansion()) { + if finalize.path_span.from_expansion() + || path.iter().any(|seg| seg.ident.span.from_expansion()) + { return; } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 47f8cc56139..bb4294fbcfb 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -24,7 +24,7 @@ use rustc_errors::{ use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; -use rustc_hir::PrimTy; +use rustc_hir::{MissingLifetimeKind, PrimTy}; use rustc_session::lint; use rustc_session::Session; use rustc_span::edit_distance::find_best_match_for_name; @@ -109,18 +109,6 @@ pub(super) struct MissingLifetime { pub count: usize, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub(super) enum MissingLifetimeKind { - /// An explicit `'_`. - Underscore, - /// An elided lifetime `&' ty`. - Ampersand, - /// An elided lifetime in brackets with written brackets. - Comma, - /// An elided lifetime with elided brackets. - Brackets, -} - /// Description of the lifetimes appearing in a function parameter. /// This is used to provide a literal explanation to the elision failure. #[derive(Clone, Debug)] @@ -1604,18 +1592,23 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { match (res, source) { ( - Res::Def(DefKind::Macro(MacroKind::Bang), _), + Res::Def(DefKind::Macro(MacroKind::Bang), def_id), PathSource::Expr(Some(Expr { kind: ExprKind::Index(..) | ExprKind::Call(..), .. })) | PathSource::Struct, ) => { + // Don't suggest macro if it's unstable. + let suggestable = def_id.is_local() + || self.r.tcx.lookup_stability(def_id).map_or(true, |s| s.is_stable()); + err.span_label(span, fallback_label.to_string()); // Don't suggest `!` for a macro invocation if there are generic args if path .last() .is_some_and(|segment| !segment.has_generic_args && !segment.has_lifetime_args) + && suggestable { err.span_suggestion_verbose( span.shrink_to_hi(), diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index 2553df33cc7..93839fa1186 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -44,6 +44,10 @@ pub struct FieldInfo { pub offset: u64, pub size: u64, pub align: u64, + /// Name of the type of this field. + /// Present only if the creator thought that this would be important for identifying the field, + /// typically because the field name is uninformative. + pub type_name: Option<Symbol>, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -192,7 +196,7 @@ impl CodeStats { fields.sort_by_key(|f| (f.offset, f.size)); for field in fields { - let FieldInfo { kind, ref name, offset, size, align } = field; + let FieldInfo { kind, ref name, offset, size, align, type_name } = field; if offset > min_offset { let pad = offset - min_offset; @@ -201,21 +205,27 @@ impl CodeStats { if offset < min_offset { // If this happens it's probably a union. - println!( + print!( "print-type-size {indent}{kind} `.{name}`: {size} bytes, \ offset: {offset} bytes, \ alignment: {align} bytes" ); } else if info.packed || offset == min_offset { - println!("print-type-size {indent}{kind} `.{name}`: {size} bytes"); + print!("print-type-size {indent}{kind} `.{name}`: {size} bytes"); } else { // Include field alignment in output only if it caused padding injection - println!( + print!( "print-type-size {indent}{kind} `.{name}`: {size} bytes, \ alignment: {align} bytes" ); } + if let Some(type_name) = type_name { + println!(", type: {type_name}"); + } else { + println!(); + } + min_offset = offset + size; } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e6eb1a3e83c..a88ae268e27 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -22,7 +22,7 @@ use rustc_span::{FileName, FileNameDisplayPreference, RealFileName, SourceFileHa use rustc_target::abi::Align; use rustc_target::spec::LinkSelfContainedComponents; use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, SplitDebuginfo}; -use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS}; +use rustc_target::spec::{Target, TargetTriple, TARGETS}; use std::collections::btree_map::{ Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, }; @@ -1549,34 +1549,25 @@ pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg { user_cfg } -pub fn build_target_config( - early_dcx: &EarlyDiagCtxt, - opts: &Options, - target_override: Option<Target>, - sysroot: &Path, -) -> Target { - let target_result = target_override.map_or_else( - || Target::search(&opts.target_triple, sysroot), - |t| Ok((t, TargetWarnings::empty())), - ); - let (target, target_warnings) = target_result.unwrap_or_else(|e| { - early_dcx.early_fatal(format!( +pub fn build_target_config(early_dcx: &EarlyDiagCtxt, opts: &Options, sysroot: &Path) -> Target { + match Target::search(&opts.target_triple, sysroot) { + Ok((target, warnings)) => { + for warning in warnings.warning_messages() { + early_dcx.early_warn(warning) + } + if !matches!(target.pointer_width, 16 | 32 | 64) { + early_dcx.early_fatal(format!( + "target specification was invalid: unrecognized target-pointer-width {}", + target.pointer_width + )) + } + target + } + Err(e) => early_dcx.early_fatal(format!( "Error loading target specification: {e}. \ - Run `rustc --print target-list` for a list of built-in targets" - )) - }); - for warning in target_warnings.warning_messages() { - early_dcx.early_warn(warning) - } - - if !matches!(target.pointer_width, 16 | 32 | 64) { - early_dcx.early_fatal(format!( - "target specification was invalid: unrecognized target-pointer-width {}", - target.pointer_width - )) + Run `rustc --print target-list` for a list of built-in targets" + )), } - - target } #[derive(Copy, Clone, PartialEq, Eq, Debug)] diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9c94fd7027f..e6d82d6fab3 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1008,7 +1008,7 @@ pub fn build_session( fluent_resources: Vec<&'static str>, driver_lint_caps: FxHashMap<lint::LintId, lint::Level>, file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>, - target_cfg: Target, + target: Target, sysroot: PathBuf, cfg_version: &'static str, ice_file: Option<PathBuf>, @@ -1036,7 +1036,7 @@ pub fn build_session( let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); let hash_kind = sopts.unstable_opts.src_hash_algorithm.unwrap_or_else(|| { - if target_cfg.is_like_msvc { + if target.is_like_msvc { SourceFileHashAlgorithm::Sha256 } else { SourceFileHashAlgorithm::Md5 @@ -1117,11 +1117,10 @@ pub fn build_session( _ => CtfeBacktrace::Disabled, }); - let asm_arch = - if target_cfg.allow_asm { InlineAsmArch::from_str(&target_cfg.arch).ok() } else { None }; + let asm_arch = if target.allow_asm { InlineAsmArch::from_str(&target.arch).ok() } else { None }; let sess = Session { - target: target_cfg, + target, host, opts: sopts, host_tlib_path, diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs index 2cc9bc31873..e8cc41cc886 100644 --- a/compiler/rustc_smir/src/rustc_internal/internal.rs +++ b/compiler/rustc_smir/src/rustc_internal/internal.rs @@ -10,7 +10,7 @@ use rustc_span::Symbol; use stable_mir::abi::Layout; use stable_mir::mir::alloc::AllocId; use stable_mir::mir::mono::{Instance, MonoItem, StaticDef}; -use stable_mir::mir::{Mutability, Safety}; +use stable_mir::mir::{Mutability, Place, ProjectionElem, Safety}; use stable_mir::ty::{ Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const, DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, @@ -95,10 +95,9 @@ impl RustcInternal for RigidTy { } RigidTy::Str => rustc_ty::TyKind::Str, RigidTy::Slice(ty) => rustc_ty::TyKind::Slice(ty.internal(tables, tcx)), - RigidTy::RawPtr(ty, mutability) => rustc_ty::TyKind::RawPtr(rustc_ty::TypeAndMut { - ty: ty.internal(tables, tcx), - mutbl: mutability.internal(tables, tcx), - }), + RigidTy::RawPtr(ty, mutability) => { + rustc_ty::TyKind::RawPtr(ty.internal(tables, tcx), mutability.internal(tables, tcx)) + } RigidTy::Ref(region, ty, mutability) => rustc_ty::TyKind::Ref( region.internal(tables, tcx), ty.internal(tables, tcx), @@ -492,6 +491,50 @@ impl RustcInternal for Layout { } } +impl RustcInternal for Place { + type T<'tcx> = rustc_middle::mir::Place<'tcx>; + + fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + rustc_middle::mir::Place { + local: rustc_middle::mir::Local::from_usize(self.local), + projection: tcx.mk_place_elems(&self.projection.internal(tables, tcx)), + } + } +} + +impl RustcInternal for ProjectionElem { + type T<'tcx> = rustc_middle::mir::PlaceElem<'tcx>; + + fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> { + match self { + ProjectionElem::Deref => rustc_middle::mir::PlaceElem::Deref, + ProjectionElem::Field(idx, ty) => { + rustc_middle::mir::PlaceElem::Field((*idx).into(), ty.internal(tables, tcx)) + } + ProjectionElem::Index(idx) => rustc_middle::mir::PlaceElem::Index((*idx).into()), + ProjectionElem::ConstantIndex { offset, min_length, from_end } => { + rustc_middle::mir::PlaceElem::ConstantIndex { + offset: *offset, + min_length: *min_length, + from_end: *from_end, + } + } + ProjectionElem::Subslice { from, to, from_end } => { + rustc_middle::mir::PlaceElem::Subslice { from: *from, to: *to, from_end: *from_end } + } + ProjectionElem::Downcast(idx) => { + rustc_middle::mir::PlaceElem::Downcast(None, idx.internal(tables, tcx)) + } + ProjectionElem::OpaqueCast(ty) => { + rustc_middle::mir::PlaceElem::OpaqueCast(ty.internal(tables, tcx)) + } + ProjectionElem::Subtype(ty) => { + rustc_middle::mir::PlaceElem::Subtype(ty.internal(tables, tcx)) + } + } + } +} + impl<T> RustcInternal for &T where T: RustcInternal, diff --git a/compiler/rustc_smir/src/rustc_internal/pretty.rs b/compiler/rustc_smir/src/rustc_internal/pretty.rs index 3ef2d28ea47..c0dce08b0d3 100644 --- a/compiler/rustc_smir/src/rustc_internal/pretty.rs +++ b/compiler/rustc_smir/src/rustc_internal/pretty.rs @@ -14,7 +14,7 @@ pub fn write_smir_pretty<'tcx, W: io::Write>(tcx: TyCtxt<'tcx>, w: &mut W) -> io )?; let _ = run(tcx, || { let items = stable_mir::all_local_items(); - let _ = items.iter().map(|item| -> io::Result<()> { item.dump(w) }).collect::<Vec<_>>(); + let _ = items.iter().map(|item| -> io::Result<()> { item.emit_mir(w) }).collect::<Vec<_>>(); }); Ok(()) } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index d427e5fb88d..d39a3788d4c 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -19,7 +19,7 @@ use stable_mir::abi::{FnAbi, Layout, LayoutShape}; use stable_mir::compiler_interface::Context; use stable_mir::mir::alloc::GlobalAlloc; use stable_mir::mir::mono::{InstanceDef, StaticDef}; -use stable_mir::mir::Body; +use stable_mir::mir::{Body, Place}; use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, @@ -423,7 +423,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> { def_ty.instantiate(tables.tcx, args).stable(&mut *tables) } - fn const_literal(&self, cnst: &stable_mir::ty::Const) -> String { + fn const_pretty(&self, cnst: &stable_mir::ty::Const) -> String { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; cnst.internal(&mut *tables, tcx).to_string() @@ -434,6 +434,11 @@ impl<'tcx> Context for TablesWrapper<'tcx> { tables.tcx.def_span(tables[def_id]).stable(&mut *tables) } + fn ty_pretty(&self, ty: stable_mir::ty::Ty) -> String { + let tables = self.0.borrow_mut(); + tables.types[ty].to_string() + } + fn ty_kind(&self, ty: stable_mir::ty::Ty) -> TyKind { let mut tables = self.0.borrow_mut(); tables.types[ty].kind().stable(&mut *tables) @@ -654,6 +659,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let tcx = tables.tcx; id.internal(&mut *tables, tcx).0.stable(&mut *tables) } + + fn place_pretty(&self, place: &Place) -> String { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + format!("{:?}", place.internal(&mut *tables, tcx)) + } } pub struct TablesWrapper<'tcx>(pub RefCell<Tables<'tcx>>); diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index c0876adf905..b6a722da602 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -251,19 +251,13 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { type T = stable_mir::mir::NullOp; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { use rustc_middle::mir::NullOp::*; - use rustc_middle::mir::UbKind; match self { SizeOf => stable_mir::mir::NullOp::SizeOf, AlignOf => stable_mir::mir::NullOp::AlignOf, OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf( indices.iter().map(|idx| idx.stable(tables)).collect(), ), - UbCheck(UbKind::LanguageUb) => { - stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LanguageUb) - } - UbCheck(UbKind::LibraryUb) => { - stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LibraryUb) - } + UbChecks => stable_mir::mir::NullOp::UbChecks, } } } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index 84c6cbf178b..2ad8f350f10 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -331,7 +331,7 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { TyKind::RigidTy(RigidTy::Array(ty.stable(tables), constant.stable(tables))) } ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(ty.stable(tables))), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { + ty::RawPtr(ty, mutbl) => { TyKind::RigidTy(RigidTy::RawPtr(ty.stable(tables), mutbl.stable(tables))) } ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref( @@ -719,6 +719,18 @@ impl<'tcx> Stable<'tcx> for ty::ImplPolarity { } } +impl<'tcx> Stable<'tcx> for ty::PredicatePolarity { + type T = stable_mir::ty::PredicatePolarity; + + fn stable(&self, _: &mut Tables<'_>) -> Self::T { + use rustc_middle::ty::PredicatePolarity::*; + match self { + Positive => stable_mir::ty::PredicatePolarity::Positive, + Negative => stable_mir::ty::PredicatePolarity::Negative, + } + } +} + impl<'tcx> Stable<'tcx> for ty::Region<'tcx> { type T = stable_mir::ty::Region; diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 527938daae4..37fea6c122c 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1062,7 +1062,7 @@ pub enum ExpnKind { Macro(MacroKind, Symbol), /// Transform done by the compiler on the AST. AstPass(AstPass), - /// Desugaring done by the compiler during HIR lowering. + /// Desugaring done by the compiler during AST lowering. Desugaring(DesugaringKind), } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8b911a41a11..73fcd2a76df 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -207,6 +207,7 @@ symbols! { FromResidual, FsOpenOptions, FsPermissions, + FusedIterator, Future, FutureOutput, GlobalAlloc, @@ -517,8 +518,6 @@ symbols! { cfi, cfi_encoding, char, - check_language_ub, - check_library_ub, client, clippy, clobber_abi, @@ -674,6 +673,7 @@ symbols! { deref_method, deref_mut, deref_mut_method, + deref_patterns, deref_target, derive, derive_const, @@ -689,6 +689,7 @@ symbols! { dispatch_from_dyn, div, div_assign, + diverging_block_default, do_not_recommend, doc, doc_alias, @@ -814,9 +815,7 @@ symbols! { fadd_algebraic, fadd_fast, fake_variadic, - fallback_to_never, - fallback_to_niko, - fallback_to_unit, + fallback, fdiv_algebraic, fdiv_fast, feature, @@ -885,6 +884,7 @@ symbols! { fsub_algebraic, fsub_fast, fundamental, + fused_iterator, future, future_trait, gdb_script_file, @@ -1227,6 +1227,7 @@ symbols! { new_v1, new_v1_formatted, next, + niko, nll, no, no_builtins, @@ -1235,7 +1236,6 @@ symbols! { no_crate_inject, no_debug, no_default_passes, - no_fallback, no_implicit_prelude, no_inline, no_link, @@ -1333,6 +1333,7 @@ symbols! { poll, poll_next, post_dash_lto: "post-lto", + postfix_match, powerpc_target_feature, powf128, powf16, @@ -1553,7 +1554,7 @@ symbols! { rustc_mir, rustc_must_implement_one_of, rustc_never_returns_null_ptr, - rustc_never_type_mode, + rustc_never_type_options, rustc_no_mir_inline, rustc_nonnull_optimization_guaranteed, rustc_nounwind, @@ -1833,6 +1834,7 @@ symbols! { type_macros, type_name, type_privacy_lints, + typed_swap, u128, u128_legacy_const_max, u128_legacy_const_min, @@ -1863,6 +1865,7 @@ symbols! { u8_legacy_fn_max_value, u8_legacy_fn_min_value, u8_legacy_mod, + ub_checks, unaligned_volatile_load, unaligned_volatile_store, unboxed_closures, diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 6766080a54f..f0fb87fe83c 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -25,7 +25,7 @@ pub fn report_symbol_names(tcx: TyCtxt<'_>) { let mut symbol_names = SymbolNamesTest { tcx }; let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { + for id in crate_items.free_items() { symbol_names.process_attrs(id.owner_id.def_id); } diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 51e2c96120c..07a382d161d 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::{ use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; use rustc_span::def_id::DefId; use rustc_span::sym; -use rustc_target::abi::call::{Conv, FnAbi}; +use rustc_target::abi::call::{Conv, FnAbi, PassMode}; use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; use std::fmt::Write as _; @@ -525,8 +525,8 @@ fn encode_ty<'tcx>( "{}", &len.try_to_scalar() .unwrap() - .to_u64() - .unwrap_or_else(|_| panic!("failed to convert length to u64")) + .to_target_usize(&tcx.data_layout) + .expect("Array lens are defined in usize") ); s.push_str(&encode_ty(tcx, *ty0, dict, options)); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); @@ -683,13 +683,14 @@ fn encode_ty<'tcx>( typeid.push_str(&s); } - ty::RawPtr(tm) => { + ty::RawPtr(ptr_ty, _mutbl) => { + // FIXME: This can definitely not be so spaghettified. // P[K]<element-type> let mut s = String::new(); - s.push_str(&encode_ty(tcx, tm.ty, dict, options)); + s.push_str(&encode_ty(tcx, *ptr_ty, dict, options)); if !ty.is_mutable_ptr() { s = format!("{}{}", "K", &s); - compress(dict, DictKey::Ty(tm.ty, TyQ::Const), &mut s); + compress(dict, DictKey::Ty(*ptr_ty, TyQ::Const), &mut s); }; s = format!("{}{}", "P", &s); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); @@ -930,7 +931,7 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } } - ty::RawPtr(tm) => { + ty::RawPtr(ptr_ty, _) => { if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { if ty.is_mutable_ptr() { ty = Ty::new_mut_ptr(tcx, Ty::new_unit(tcx)); @@ -939,9 +940,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } } else { if ty.is_mutable_ptr() { - ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, tm.ty, options)); + ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, *ptr_ty, options)); } else { - ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, tm.ty, options)); + ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, *ptr_ty, options)); } } } @@ -1040,19 +1041,27 @@ pub fn typeid_for_fnabi<'tcx>( typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); // Encode the parameter types + + // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how + // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR + // interpretation today will allow skipped arguments to simply not be passed at a call-site. if !fn_abi.c_variadic { - if !fn_abi.args.is_empty() { - for arg in fn_abi.args.iter() { - let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); - } - } else { + let mut pushed_arg = false; + for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { + pushed_arg = true; + let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + if !pushed_arg { // Empty parameter lists, whether declared as () or conventionally as (void), are // encoded with a void parameter specifier "v". typeid.push('v'); } } else { for n in 0..fn_abi.fixed_count as usize { + if fn_abi.args[n].mode == PassMode::Ignore { + continue; + } let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 6bc375512ac..4369f020d27 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -361,12 +361,12 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty.print(self)?; } - ty::RawPtr(mt) => { - self.push(match mt.mutbl { + ty::RawPtr(ty, mutbl) => { + self.push(match mutbl { hir::Mutability::Not => "P", hir::Mutability::Mut => "O", }); - mt.ty.print(self)?; + ty.print(self)?; } ty::Array(ty, len) => { diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 97132311a5c..70528c1222c 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -27,32 +27,28 @@ impl AArch64InlineAsmRegClass { None } - pub fn suggest_modifier( - self, - _arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, _arch: InlineAsmArch, ty: InlineAsmType) -> Option<ModifierInfo> { match self { Self::reg => match ty.size().bits() { 64 => None, - _ => Some(('w', "w0")), + _ => Some(('w', "w0", 32).into()), }, Self::vreg | Self::vreg_low16 => match ty.size().bits() { - 8 => Some(('b', "b0")), - 16 => Some(('h', "h0")), - 32 => Some(('s', "s0")), - 64 => Some(('d', "d0")), - 128 => Some(('q', "q0")), + 8 => Some(('b', "b0", 8).into()), + 16 => Some(('h', "h0", 16).into()), + 32 => Some(('s', "s0", 32).into()), + 64 => Some(('d', "d0", 64).into()), + 128 => Some(('q', "q0", 128).into()), _ => None, }, Self::preg => None, } } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { match self { - Self::reg => Some(('x', "x0")), - Self::vreg | Self::vreg_low16 => Some(('v', "v0")), + Self::reg => Some(('x', "x0", 64).into()), + Self::vreg | Self::vreg_low16 => Some(('v', "v0", 128).into()), Self::preg => None, } } diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 514e30ae020..f56dbac708b 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -35,11 +35,11 @@ impl ArmInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs index 9a96a61f5b2..eab38a4a4f4 100644 --- a/compiler/rustc_target/src/asm/avr.rs +++ b/compiler/rustc_target/src/asm/avr.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -29,11 +29,11 @@ impl AvrInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/bpf.rs b/compiler/rustc_target/src/asm/bpf.rs index 3b03766a089..9bc94274675 100644 --- a/compiler/rustc_target/src/asm/bpf.rs +++ b/compiler/rustc_target/src/asm/bpf.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl BpfInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/csky.rs b/compiler/rustc_target/src/asm/csky.rs index db3d8106040..64607ee4b81 100644 --- a/compiler/rustc_target/src/asm/csky.rs +++ b/compiler/rustc_target/src/asm/csky.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl CSKYInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/hexagon.rs b/compiler/rustc_target/src/asm/hexagon.rs index d20270ac9e9..19da7b80848 100644 --- a/compiler/rustc_target/src/asm/hexagon.rs +++ b/compiler/rustc_target/src/asm/hexagon.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -22,11 +22,11 @@ impl HexagonInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs index 9d1a4f3eeea..15d0f54ce3b 100644 --- a/compiler/rustc_target/src/asm/loongarch.rs +++ b/compiler/rustc_target/src/asm/loongarch.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl LoongArchInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/m68k.rs b/compiler/rustc_target/src/asm/m68k.rs index 8c857550cf2..ac94dcc03dc 100644 --- a/compiler/rustc_target/src/asm/m68k.rs +++ b/compiler/rustc_target/src/asm/m68k.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -24,11 +24,11 @@ impl M68kInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/mips.rs b/compiler/rustc_target/src/asm/mips.rs index 4e7c2eb1bf8..0ac1a43ae18 100644 --- a/compiler/rustc_target/src/asm/mips.rs +++ b/compiler/rustc_target/src/asm/mips.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl MipsInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index a11884bea26..2e04dca98c5 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -6,6 +6,18 @@ use rustc_span::Symbol; use std::fmt; use std::str::FromStr; +pub struct ModifierInfo { + pub modifier: char, + pub result: &'static str, + pub size: u64, +} + +impl From<(char, &'static str, u64)> for ModifierInfo { + fn from((modifier, result, size): (char, &'static str, u64)) -> Self { + Self { modifier, result, size } + } +} + macro_rules! def_reg_class { ($arch:ident $arch_regclass:ident { $( @@ -512,11 +524,7 @@ impl InlineAsmRegClass { /// Such suggestions are useful if a type smaller than the full register /// size is used and a modifier can be used to point to the subregister of /// the correct size. - pub fn suggest_modifier( - self, - arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<ModifierInfo> { match self { Self::X86(r) => r.suggest_modifier(arch, ty), Self::Arm(r) => r.suggest_modifier(arch, ty), @@ -545,7 +553,7 @@ impl InlineAsmRegClass { /// This is only needed when the register class can suggest a modifier, so /// that the user can be shown how to get the default behavior without a /// warning. - pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<ModifierInfo> { match self { Self::X86(r) => r.default_modifier(arch), Self::Arm(r) => r.default_modifier(arch), diff --git a/compiler/rustc_target/src/asm/msp430.rs b/compiler/rustc_target/src/asm/msp430.rs index a27d6390a72..439f3ba0b57 100644 --- a/compiler/rustc_target/src/asm/msp430.rs +++ b/compiler/rustc_target/src/asm/msp430.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -22,11 +22,11 @@ impl Msp430InlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/nvptx.rs b/compiler/rustc_target/src/asm/nvptx.rs index 8e1e91e7c5f..57aa50fceb8 100644 --- a/compiler/rustc_target/src/asm/nvptx.rs +++ b/compiler/rustc_target/src/asm/nvptx.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -23,11 +23,11 @@ impl NvptxInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs index d3ccb30350a..4e8cbe34de9 100644 --- a/compiler/rustc_target/src/asm/powerpc.rs +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -26,11 +26,11 @@ impl PowerPCInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index dea6d50fe2b..2505d36f5b8 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -26,11 +26,11 @@ impl RiscVInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/s390x.rs b/compiler/rustc_target/src/asm/s390x.rs index b8afeb824d8..6bc668454b1 100644 --- a/compiler/rustc_target/src/asm/s390x.rs +++ b/compiler/rustc_target/src/asm/s390x.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -24,11 +24,11 @@ impl S390xInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/spirv.rs b/compiler/rustc_target/src/asm/spirv.rs index 31073da10b2..d13a6131f9a 100644 --- a/compiler/rustc_target/src/asm/spirv.rs +++ b/compiler/rustc_target/src/asm/spirv.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -21,11 +21,11 @@ impl SpirVInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/wasm.rs b/compiler/rustc_target/src/asm/wasm.rs index f095b7c6e11..eb0b23ef43d 100644 --- a/compiler/rustc_target/src/asm/wasm.rs +++ b/compiler/rustc_target/src/asm/wasm.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -21,11 +21,11 @@ impl WasmInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 3902dac7ff6..3b5da8806cc 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -53,32 +53,28 @@ impl X86InlineAsmRegClass { } } - pub fn suggest_modifier( - self, - arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<ModifierInfo> { match self { Self::reg => match ty.size().bits() { - 16 => Some(('x', "ax")), - 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + 16 => Some(('x', "ax", 16).into()), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax", 32).into()), _ => None, }, Self::reg_abcd => match ty.size().bits() { - 16 => Some(('x', "ax")), - 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + 16 => Some(('x', "ax", 16).into()), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax", 32).into()), _ => None, }, Self::reg_byte => None, Self::xmm_reg => None, Self::ymm_reg => match ty.size().bits() { 256 => None, - _ => Some(('x', "xmm0")), + _ => Some(('x', "xmm0", 128).into()), }, Self::zmm_reg => match ty.size().bits() { 512 => None, - 256 => Some(('y', "ymm0")), - _ => Some(('x', "xmm0")), + 256 => Some(('y', "ymm0", 256).into()), + _ => Some(('x', "xmm0", 128).into()), }, Self::kreg | Self::kreg0 => None, Self::mmx_reg | Self::x87_reg => None, @@ -86,19 +82,19 @@ impl X86InlineAsmRegClass { } } - pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<ModifierInfo> { match self { Self::reg | Self::reg_abcd => { if arch == InlineAsmArch::X86_64 { - Some(('r', "rax")) + Some(('r', "rax", 64).into()) } else { - Some(('e', "eax")) + Some(('e', "eax", 32).into()) } } Self::reg_byte => None, - Self::xmm_reg => Some(('x', "xmm0")), - Self::ymm_reg => Some(('y', "ymm0")), - Self::zmm_reg => Some(('z', "zmm0")), + Self::xmm_reg => Some(('x', "xmm0", 128).into()), + Self::ymm_reg => Some(('y', "ymm0", 256).into()), + Self::zmm_reg => Some(('z', "zmm0", 512).into()), Self::kreg | Self::kreg0 => None, Self::mmx_reg | Self::x87_reg => None, Self::tmm_reg => None, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 941d767b850..966da2c5eda 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1621,6 +1621,7 @@ supported_targets! { ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), ("riscv32im-risc0-zkvm-elf", riscv32im_risc0_zkvm_elf), ("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf), + ("riscv32ima-unknown-none-elf", riscv32ima_unknown_none_elf), ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), ("riscv32imc-esp-espidf", riscv32imc_esp_espidf), ("riscv32imac-esp-espidf", riscv32imac_esp_espidf), @@ -2091,6 +2092,9 @@ pub struct TargetOptions { /// compiling `rustc` will be used instead (or llvm if it is not set). /// /// N.B. when *using* the compiler, backend can always be overridden with `-Zcodegen-backend`. + /// + /// This was added by WaffleLapkin in #116793. The motivation is a rustc fork that requires a + /// custom codegen backend for a particular target. pub default_codegen_backend: Option<StaticCow<str>>, /// Whether to generate trap instructions in places where optimization would diff --git a/compiler/rustc_target/src/spec/targets/riscv32ima_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32ima_unknown_none_elf.rs new file mode 100644 index 00000000000..9acf6a3b5a0 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32ima_unknown_none_elf.rs @@ -0,0 +1,29 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv32".into(), + max_atomic_width: Some(32), + features: "+m,+a".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 0dcba0e05f7..f96bd985237 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -19,6 +19,9 @@ trait_selection_closure_kind_mismatch = expected a closure that implements the ` trait_selection_closure_kind_requirement = the requirement to implement `{$trait_prefix}{$expected}` derives from here +trait_selection_disallowed_positional_argument = positional format arguments are not allowed here + .help = only named format arguments with the name of one of the generic types are allowed in this context + trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries} trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]` @@ -30,6 +33,9 @@ trait_selection_ignored_diagnostic_option = `{$option_name}` is ignored due to p trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}` +trait_selection_invalid_format_specifier = invalid format specifier + .help = no format specifier are supported in this position + trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]` .label = invalid on-clause here @@ -60,3 +66,6 @@ trait_selection_unable_to_construct_constant_value = unable to construct a const trait_selection_unknown_format_parameter_for_on_unimplemented_attr = there is no parameter `{$argument_name}` on trait `{$trait_name}` .help = expect either a generic argument name or {"`{Self}`"} as format argument + +trait_selection_wrapped_parser_error = {$description} + .label = {$label} diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 9f33dce2a6d..5e580df01cb 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -215,6 +215,13 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + /// A coroutine (that comes from a `gen` desugaring) is known to implement + /// `FusedIterator` + fn consider_builtin_fused_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -350,7 +357,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -497,6 +504,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_future_candidate(self, goal) } else if lang_items.iterator_trait() == Some(trait_def_id) { G::consider_builtin_iterator_candidate(self, goal) + } else if lang_items.fused_iterator_trait() == Some(trait_def_id) { + G::consider_builtin_fused_iterator_candidate(self, goal) } else if lang_items.async_iterator_trait() == Some(trait_def_id) { G::consider_builtin_async_iterator_candidate(self, goal) } else if lang_items.coroutine_trait() == Some(trait_def_id) { @@ -581,7 +590,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -669,7 +678,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) 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 faf8c55c475..00cd4b48797 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -6,7 +6,7 @@ use rustc_hir::{def_id::DefId, Movability, Mutability}; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::Goal; use rustc_middle::ty::{ - self, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + self, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, }; use rustc_span::sym; @@ -46,7 +46,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( bug!("unexpected type `{ty}`") } - ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { + ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => { Ok(vec![ty::Binder::dummy(element_ty)]) } @@ -73,8 +73,8 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( ty::CoroutineWitness(def_id, args) => Ok(ecx .tcx() - .coroutine_hidden_types(def_id) - .map(|bty| replace_erased_lifetimes_with_bound_vars(tcx, bty.instantiate(tcx, args))) + .bound_coroutine_hidden_types(def_id) + .map(|bty| bty.instantiate(tcx, args)) .collect()), // For `PhantomData<T>`, we pass `T`. @@ -93,27 +93,6 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( } } -pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> ty::Binder<'tcx, Ty<'tcx>> { - debug_assert!(!ty.has_bound_regions()); - let mut counter = 0; - let ty = tcx.fold_regions(ty, |r, current_depth| match r.kind() { - ty::ReErased => { - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon }; - counter += 1; - ty::Region::new_bound(tcx, current_depth, br) - } - // All free regions should be erased here. - r => bug!("unexpected region: {r:?}"), - }); - let bound_vars = tcx.mk_bound_variable_kinds_from_iter( - (0..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon)), - ); - ty::Binder::bind_with_vars(ty, bound_vars) -} - #[instrument(level = "debug", skip(ecx), ret)] pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( ecx: &EvalCtxt<'_, 'tcx>, @@ -241,13 +220,8 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( // impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types ty::CoroutineWitness(def_id, args) => Ok(ecx .tcx() - .coroutine_hidden_types(def_id) - .map(|bty| { - replace_erased_lifetimes_with_bound_vars( - ecx.tcx(), - bty.instantiate(ecx.tcx(), args), - ) - }) + .bound_coroutine_hidden_types(def_id) + .map(|bty| bty.instantiate(ecx.tcx(), args)) .collect()), } } @@ -366,7 +340,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Dynamic(_, _, _) | ty::Coroutine(_, _) @@ -553,7 +527,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Dynamic(_, _, _) | ty::Coroutine(_, _) diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs index d60490bce44..439f9eec831 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs @@ -1,5 +1,5 @@ //! Computes a normalizes-to (projection) goal for inherent associated types, -//! `#![feature(inherent_associated_type)]`. Since astconv already determines +//! `#![feature(inherent_associated_type)]`. Since HIR ty lowering already determines //! which impl the IAT is being projected from, we just: //! 1. instantiate generic parameters, //! 2. equate the self type, and diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index 85bb6338daf..66688893235 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -647,6 +647,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { ) } + fn consider_builtin_fused_iterator_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`FusedIterator` does not have an associated type: {:?}", goal); + } + fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index c252ad76dfe..eb3ad0aa782 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -55,17 +55,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // An upper bound of the certainty of this goal, used to lower the certainty // of reservation impl to ambiguous during coherence. let impl_polarity = impl_trait_header.polarity; - let maximal_certainty = match impl_polarity { - ty::ImplPolarity::Positive | ty::ImplPolarity::Negative => { - match impl_polarity == goal.predicate.polarity { - true => Certainty::Yes, - false => return Err(NoSolution), - } - } - ty::ImplPolarity::Reservation => match ecx.solver_mode() { - SolverMode::Normal => return Err(NoSolution), + let maximal_certainty = match (impl_polarity, goal.predicate.polarity) { + // In intercrate mode, this is ambiguous. But outside of intercrate, + // it's not a real impl. + (ty::ImplPolarity::Reservation, _) => match ecx.solver_mode() { SolverMode::Coherence => Certainty::AMBIGUOUS, + SolverMode::Normal => return Err(NoSolution), }, + + // Impl matches polarity + (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive) + | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => Certainty::Yes, + + // Impl doesn't match polarity + (ty::ImplPolarity::Positive, ty::PredicatePolarity::Negative) + | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Positive) => { + return Err(NoSolution); + } }; ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { @@ -123,7 +129,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -168,7 +174,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -191,7 +197,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -205,7 +211,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -219,7 +225,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -251,7 +257,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let self_ty = goal.predicate.self_ty(); match goal.predicate.polarity { // impl FnPtr for FnPtr {} - ty::ImplPolarity::Positive => { + ty::PredicatePolarity::Positive => { if self_ty.is_fn_ptr() { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { @@ -259,7 +265,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } } // impl !FnPtr for T where T != FnPtr && T is rigid {} - ty::ImplPolarity::Negative => { + ty::PredicatePolarity::Negative => { // If a type is rigid and not a fn ptr, then we know for certain // that it does *not* implement `FnPtr`. if !self_ty.is_fn_ptr() && self_ty.is_known_rigid() { @@ -268,10 +274,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { Err(NoSolution) } } - // FIXME: Goal polarity should be split from impl polarity - ty::ImplPolarity::Reservation => { - bug!("we never expect a `Reservation` polarity in a trait goal") - } } } @@ -280,7 +282,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -316,7 +318,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal: Goal<'tcx, Self>, goal_kind: ty::ClosureKind, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -386,7 +388,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -401,7 +403,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -412,7 +414,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -436,7 +438,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -456,11 +458,33 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + fn consider_builtin_fused_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + + let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else { + return Err(NoSolution); + }; + + // Coroutines are not iterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_gen(def_id) { + return Err(NoSolution); + } + + // Gen coroutines unconditionally implement `FusedIterator` + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + fn consider_builtin_async_iterator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -484,7 +508,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -515,7 +539,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -527,7 +551,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -542,7 +566,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return Err(NoSolution); } @@ -579,7 +603,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { return vec![]; } @@ -1028,7 +1052,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 0796ffcbc45..c909a0b49e2 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -8,7 +8,7 @@ use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::traits::project::ProjectAndUnifyResult; use rustc_infer::infer::DefineOpaqueTypes; use rustc_middle::mir::interpret::ErrorHandled; -use rustc_middle::ty::{ImplPolarity, Region, RegionVid}; +use rustc_middle::ty::{Region, RegionVid}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; @@ -96,9 +96,9 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty::TraitPredicate { trait_ref, polarity: if polarity { - ImplPolarity::Positive + ty::PredicatePolarity::Positive } else { - ImplPolarity::Negative + ty::PredicatePolarity::Negative }, }, )); @@ -258,7 +258,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { trait_ref: ty::TraitRef::new(infcx.tcx, trait_did, [ty]), // Auto traits are positive - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, })); let computed_preds = param_env.caller_bounds().iter().map(|c| c.as_predicate()); @@ -295,7 +295,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { }) = impl_source { // Blame 'tidy' for the weird bracket placement. - if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative { + if infcx.tcx.impl_polarity(*impl_def_id) != ty::ImplPolarity::Positive { debug!( "evaluate_nested_obligations: found explicit negative impl\ {:?}, bailing out", diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs index cbe2ec0f0eb..6c6c8ca1d9f 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs @@ -206,7 +206,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, param_env: ty::ParamEnv<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>, - polarity: ty::ImplPolarity, + polarity: ty::PredicatePolarity, ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { self.commit_if_ok(|_| { for trait_def_id in [ diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 745ddfc08af..5fccc769785 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -367,6 +367,23 @@ pub struct UnknownFormatParameterForOnUnimplementedAttr { trait_name: Symbol, } +#[derive(LintDiagnostic)] +#[diag(trait_selection_disallowed_positional_argument)] +#[help] +pub struct DisallowedPositionalArgument; + +#[derive(LintDiagnostic)] +#[diag(trait_selection_invalid_format_specifier)] +#[help] +pub struct InvalidFormatSpecifier; + +#[derive(LintDiagnostic)] +#[diag(trait_selection_wrapped_parser_error)] +pub struct WrappedParserError { + description: String, + label: String, +} + impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, @@ -758,64 +775,108 @@ impl<'tcx> OnUnimplementedFormatString { let trait_name = tcx.item_name(trait_def_id); let generics = tcx.generics_of(item_def_id); let s = self.symbol.as_str(); - let parser = Parser::new(s, None, None, false, ParseMode::Format); + let mut parser = Parser::new(s, None, None, false, ParseMode::Format); let mut result = Ok(()); - for token in parser { + for token in &mut parser { match token { Piece::String(_) => (), // Normal string, no need to check it - Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => { - match Symbol::intern(s) { - // `{ThisTraitsName}` is allowed - s if s == trait_name && !self.is_diagnostic_namespace_variant => (), - s if ALLOWED_FORMAT_SYMBOLS.contains(&s) - && !self.is_diagnostic_namespace_variant => - { - () - } - // So is `{A}` if A is a type parameter - s if generics.params.iter().any(|param| param.name == s) => (), - s => { - if self.is_diagnostic_namespace_variant { - tcx.emit_node_span_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id.expect_local()), - self.span, - UnknownFormatParameterForOnUnimplementedAttr { - argument_name: s, - trait_name, - }, - ); - } else { - result = Err(struct_span_code_err!( - tcx.dcx(), - self.span, - E0230, - "there is no parameter `{}` on {}", - s, - if trait_def_id == item_def_id { - format!("trait `{trait_name}`") - } else { - "impl".to_string() - } - ) - .emit()); + Piece::NextArgument(a) => { + let format_spec = a.format; + if self.is_diagnostic_namespace_variant + && (format_spec.ty_span.is_some() + || format_spec.width_span.is_some() + || format_spec.precision_span.is_some() + || format_spec.fill_span.is_some()) + { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + InvalidFormatSpecifier, + ); + } + match a.position { + Position::ArgumentNamed(s) => { + match Symbol::intern(s) { + // `{ThisTraitsName}` is allowed + s if s == trait_name && !self.is_diagnostic_namespace_variant => (), + s if ALLOWED_FORMAT_SYMBOLS.contains(&s) + && !self.is_diagnostic_namespace_variant => + { + () + } + // So is `{A}` if A is a type parameter + s if generics.params.iter().any(|param| param.name == s) => (), + s => { + if self.is_diagnostic_namespace_variant { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + UnknownFormatParameterForOnUnimplementedAttr { + argument_name: s, + trait_name, + }, + ); + } else { + result = Err(struct_span_code_err!( + tcx.dcx(), + self.span, + E0230, + "there is no parameter `{}` on {}", + s, + if trait_def_id == item_def_id { + format!("trait `{trait_name}`") + } else { + "impl".to_string() + } + ) + .emit()); + } } } } + // `{:1}` and `{}` are not to be used + Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { + if self.is_diagnostic_namespace_variant { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + DisallowedPositionalArgument, + ); + } else { + let reported = struct_span_code_err!( + tcx.dcx(), + self.span, + E0231, + "only named generic parameters are allowed" + ) + .emit(); + result = Err(reported); + } + } } - // `{:1}` and `{}` are not to be used - Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { - let reported = struct_span_code_err!( - tcx.dcx(), - self.span, - E0231, - "only named generic parameters are allowed" - ) - .emit(); - result = Err(reported); - } - }, + } + } + } + // we cannot return errors from processing the format string as hard error here + // as the diagnostic namespace gurantees that malformed input cannot cause an error + // + // if we encounter any error while processing we nevertheless want to show it as warning + // so that users are aware that something is not correct + for e in parser.errors { + if self.is_diagnostic_namespace_variant { + tcx.emit_node_span_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + WrappedParserError { description: e.description, label: e.label }, + ); + } else { + let reported = + struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,).emit(); + result = Err(reported); } } @@ -853,9 +914,9 @@ impl<'tcx> OnUnimplementedFormatString { let empty_string = String::new(); let s = self.symbol.as_str(); - let parser = Parser::new(s, None, None, false, ParseMode::Format); + let mut parser = Parser::new(s, None, None, false, ParseMode::Format); let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); - parser + let constructed_message = (&mut parser) .map(|p| match p { Piece::String(s) => s.to_owned(), Piece::NextArgument(a) => match a.position { @@ -895,9 +956,29 @@ impl<'tcx> OnUnimplementedFormatString { } } } + Position::ArgumentImplicitlyIs(_) if self.is_diagnostic_namespace_variant => { + String::from("{}") + } + Position::ArgumentIs(idx) if self.is_diagnostic_namespace_variant => { + format!("{{{idx}}}") + } _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol), }, }) - .collect() + .collect(); + // we cannot return errors from processing the format string as hard error here + // as the diagnostic namespace gurantees that malformed input cannot cause an error + // + // if we encounter any error while processing the format string + // we don't want to show the potentially half assembled formated string, + // therefore we fall back to just showing the input string in this case + // + // The actual parser errors are emitted earlier + // as lint warnings in OnUnimplementedFormatString::verify + if self.is_diagnostic_namespace_variant && !parser.errors.is_empty() { + String::from(s) + } else { + constructed_message + } } } 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 d19c2bd1f60..28f4f81e7d8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -31,8 +31,8 @@ use rustc_middle::traits::IsConstable; use rustc_middle::ty::error::TypeError::{self, Sorts}; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs, - InferTy, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitableExt, TypeckResults, + InferTy, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, TypeckResults, }; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -245,7 +245,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { associated_ty: Option<(&'static str, Ty<'tcx>)>, mut body_id: LocalDefId, ) { - if trait_pred.skip_binder().polarity == ty::ImplPolarity::Negative { + if trait_pred.skip_binder().polarity != ty::PredicatePolarity::Positive { return; } @@ -482,7 +482,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let Some(steps) = autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { // Re-add the `&` - let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl }); + let ty = Ty::new_ref(self.tcx, region, ty, mutbl); // Remapping bound vars here let real_trait_pred_and_ty = real_trait_pred @@ -768,7 +768,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } // Different to previous arm because one is `&hir::Local` and the other // is `P<hir::Local>`. - hir::Node::Local(local) => get_name(err, &local.pat.kind), + hir::Node::LetStmt(local) => get_name(err, &local.pat.kind), _ => None, } } @@ -930,7 +930,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else { return; }; - let hir::Node::Local(hir::Local { ty: None, init: Some(init), .. }) = + let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) = self.tcx.parent_hir_node(pat.hir_id) else { return; @@ -1098,8 +1098,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { )) } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { - self.tcx.item_bounds(def_id).instantiate(self.tcx, args).iter().find_map( - |pred| { + self.tcx + .item_super_predicates(def_id) + .instantiate(self.tcx, args) + .iter() + .find_map(|pred| { if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() // args tuple will always be args[1] @@ -1113,8 +1116,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } else { None } - }, - ) + }) } ty::Dynamic(data, _, ty::Dyn) => { data.iter().find_map(|pred| { @@ -1560,7 +1562,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let Res::Local(hir_id) = path.res && let hir::Node::Pat(binding) = self.tcx.hir_node(hir_id) - && let hir::Node::Local(local) = self.tcx.parent_hir_node(binding.hir_id) + && let hir::Node::LetStmt(local) = self.tcx.parent_hir_node(binding.hir_id) && let None = local.ty && let Some(binding_expr) = local.init { @@ -2964,7 +2966,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err.downgrade_to_delayed_bug(); } match tcx.parent_hir_node(hir_id) { - Node::Local(hir::Local { ty: Some(ty), .. }) => { + Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) => { err.span_suggestion_verbose( ty.span.shrink_to_lo(), "consider borrowing here", @@ -2973,7 +2975,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err.note("all local variables must have a statically known size"); } - Node::Local(hir::Local { + Node::LetStmt(hir::LetStmt { init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }), .. }) => { @@ -3865,7 +3867,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: Res::Local(hir_id), .. } = path && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id) - && let hir::Node::Local(local) = self.tcx.parent_hir_node(binding.hir_id) + && let hir::Node::LetStmt(local) = self.tcx.parent_hir_node(binding.hir_id) && let Some(binding_expr) = local.init { // If the expression we're calling on is a binding, we want to point at the @@ -4055,7 +4057,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span, [*ty], ), - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }); let Some(generics) = node.generics() else { continue; @@ -4126,7 +4128,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { let parent = self.tcx.parent_hir_node(binding.hir_id); // We've reached the root of the method call chain... - if let hir::Node::Local(local) = parent + if let hir::Node::LetStmt(local) = parent && let Some(binding_expr) = local.init { // ...and it is a binding. Get the binding creation and continue the chain. @@ -4350,7 +4352,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Go through all the candidate impls to see if any of them is for // slices of `element_ty` with `mutability`. let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() { - ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => { + ty::RawPtr(t, m) | ty::Ref(_, t, m) => { if matches!(*t.kind(), ty::Slice(e) if e == element_ty) && m == mutability.unwrap_or(m) { @@ -4800,7 +4802,7 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>( Some(desc) => format!(" {desc}"), None => String::new(), }; - if let ty::ImplPolarity::Positive = trait_predicate.polarity() { + if let ty::PredicatePolarity::Positive = trait_predicate.polarity() { format!( "{pre_message}the trait `{}` is not implemented for{desc} `{}`{post}", trait_predicate.print_modifiers_and_trait_path(), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 4bc3ff92a67..9444cf8248e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1272,7 +1272,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { let parent = self.tcx.parent_hir_node(binding.hir_id); // We've reached the root of the method call chain... - if let hir::Node::Local(local) = parent + if let hir::Node::LetStmt(local) = parent && let Some(binding_expr) = local.init { // ...and it is a binding. Get the binding creation and continue the chain. @@ -1357,7 +1357,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "using function pointers as const generic parameters is forbidden", ) } - ty::RawPtr(_) => { + ty::RawPtr(_, _) => { struct_span_code_err!( self.dcx(), span, @@ -1809,9 +1809,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let strip_references = |mut t: Ty<'tcx>| -> Ty<'tcx> { loop { match t.kind() { - ty::Ref(_, inner, _) | ty::RawPtr(ty::TypeAndMut { ty: inner, .. }) => { - t = *inner - } + ty::Ref(_, inner, _) | ty::RawPtr(inner, _) => t = *inner, _ => break t, } } @@ -1907,7 +1905,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .all_impls(trait_pred.def_id()) .filter_map(|def_id| { let imp = self.tcx.impl_trait_header(def_id).unwrap(); - if imp.polarity == ty::ImplPolarity::Negative + if imp.polarity != ty::ImplPolarity::Positive || !self.tcx.is_user_visible_dep(def_id.krate) { return None; diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index cc8b8f72cf3..c875d3da47e 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -46,7 +46,7 @@ pub use self::coherence::{IsFirstInputType, OrphanCheckErr, OverlapResult}; pub use self::engine::{ObligationCtxt, TraitEngineExt}; pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; pub use self::normalize::NormalizeExt; -pub use self::object_safety::astconv_object_safety_violations; +pub use self::object_safety::hir_ty_lowering_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::object_safety_violations_for_assoc_item; pub use self::object_safety::ObjectSafetyViolation; diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 1816b98a636..5e1343b50ce 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -32,11 +32,13 @@ use std::ops::ControlFlow; pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation}; -/// Returns the object safety violations that affect -/// astconv -- currently, `Self` in supertraits. This is needed +/// Returns the object safety violations that affect HIR ty lowering. +/// +/// Currently that is `Self` in supertraits. This is needed /// because `object_safety_violations` can't be used during /// type collection. -pub fn astconv_object_safety_violations( +#[instrument(level = "debug", skip(tcx))] +pub fn hir_ty_lowering_object_safety_violations( tcx: TyCtxt<'_>, trait_def_id: DefId, ) -> Vec<ObjectSafetyViolation> { @@ -46,9 +48,7 @@ pub fn astconv_object_safety_violations( .filter(|spans| !spans.is_empty()) .map(ObjectSafetyViolation::SupertraitSelf) .collect(); - - debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", trait_def_id, violations); - + debug!(?violations); violations } @@ -584,7 +584,7 @@ fn virtual_call_violations_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, - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }) = pred.kind().skip_binder() && pred_trait_ref.self_ty() == tcx.types.self_param && tcx.trait_is_auto(pred_trait_ref.def_id) 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 6c8834f11f1..aaa38d14d6e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -36,7 +36,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | ty::FnPtr(_) | ty::Char | ty::CoroutineWitness(..) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::Str | ty::Foreign(..) 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 49091e53be7..c6ea596b819 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -56,7 +56,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; // Negative trait predicates have different rules than positive trait predicates. - if obligation.polarity() == ty::ImplPolarity::Negative { + if obligation.polarity() == ty::PredicatePolarity::Negative { self.assemble_candidates_for_trait_alias(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; @@ -118,6 +118,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_future_candidates(obligation, &mut candidates); } else if lang_items.iterator_trait() == Some(def_id) { self.assemble_iterator_candidates(obligation, &mut candidates); + } else if lang_items.fused_iterator_trait() == Some(def_id) { + self.assemble_fused_iterator_candidates(obligation, &mut candidates); } else if lang_items.async_iterator_trait() == Some(def_id) { self.assemble_async_iterator_candidates(obligation, &mut candidates); } else if lang_items.async_fn_kind_helper() == Some(def_id) { @@ -302,14 +304,31 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates: &mut SelectionCandidateSet<'tcx>, ) { let self_ty = obligation.self_ty().skip_binder(); - if let ty::Coroutine(did, ..) = self_ty.kind() { - // gen constructs get lowered to a special kind of coroutine that - // should directly `impl Iterator`. - if self.tcx().coroutine_is_gen(*did) { - debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + // gen constructs get lowered to a special kind of coroutine that + // should directly `impl Iterator`. + if let ty::Coroutine(did, ..) = self_ty.kind() + && self.tcx().coroutine_is_gen(*did) + { + debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); - candidates.vec.push(IteratorCandidate); - } + candidates.vec.push(IteratorCandidate); + } + } + + fn assemble_fused_iterator_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = obligation.self_ty().skip_binder(); + // gen constructs get lowered to a special kind of coroutine that + // should directly `impl FusedIterator`. + if let ty::Coroutine(did, ..) = self_ty.kind() + && self.tcx().coroutine_is_gen(*did) + { + debug!(?self_ty, ?obligation, "assemble_fused_iterator_candidates",); + + candidates.vec.push(BuiltinCandidate { has_nested: false }); } } @@ -652,7 +671,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::Closure(..) | ty::CoroutineClosure(..) @@ -786,7 +805,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Array(_, _) | ty::Slice(_) | ty::Adt(..) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) @@ -1167,7 +1186,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::Str - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) @@ -1248,7 +1267,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(_, _) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(_) @@ -1311,7 +1330,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Str | ty::Array(..) | ty::Slice(_) - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::Placeholder(..) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 51fc223a5d1..6f512a1173f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -14,7 +14,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; use rustc_middle::ty::{ self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, - TraitPredicate, Ty, TyCtxt, TypeVisitableExt, + TraitPredicate, Ty, TyCtxt, }; use rustc_span::def_id::DefId; @@ -267,6 +267,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.copy_clone_conditions(obligation) } else if Some(trait_def) == lang_items.clone_trait() { self.copy_clone_conditions(obligation) + } else if Some(trait_def) == lang_items.fused_iterator_trait() { + self.fused_iterator_conditions(obligation) } else { bug!("unexpected builtin trait {:?}", trait_def) }; @@ -1262,7 +1264,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // 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). + // HIR ty lowering (which means it's un-normalized). let source_tail = normalize_with_depth_to( self, obligation.param_env, @@ -1411,7 +1413,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::Str - | ty::RawPtr(_) + | ty::RawPtr(_, _) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(_) @@ -1438,10 +1440,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::CoroutineWitness(def_id, args) => { let tcx = self.tcx(); - stack.extend(tcx.coroutine_hidden_types(def_id).map(|bty| { - let ty = bty.instantiate(tcx, args); - debug_assert!(!ty.has_bound_regions()); - ty + stack.extend(tcx.bound_coroutine_hidden_types(def_id).map(|bty| { + self.infcx.enter_forall_and_leak_universe(bty.instantiate(tcx, args)) })) } @@ -1460,7 +1460,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause.span, [nested_ty.into(), host_effect_param], ), - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }), &mut nested, ); @@ -1485,7 +1485,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause.span, [nested_ty.into(), host_effect_param], ), - polarity: ty::ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }); nested.push(Obligation::with_depth( diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 6460613d7d5..1894fbba302 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1418,10 +1418,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for candidate in candidates { if let ImplCandidate(def_id) = candidate { - if ty::ImplPolarity::Reservation == tcx.impl_polarity(def_id) - || obligation.polarity() == tcx.impl_polarity(def_id) - { - result.push(candidate); + match (tcx.impl_polarity(def_id), obligation.polarity()) { + (ty::ImplPolarity::Reservation, _) + | (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive) + | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => { + result.push(candidate); + } + _ => {} } } else { result.push(candidate); @@ -1617,21 +1620,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => return ControlFlow::Continue(()), }; - for bound in - self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args) - { - // HACK: On subsequent recursions, we only care about bounds that don't - // share the same type as `self_ty`. This is because for truly rigid - // projections, we will never be able to equate, e.g. `<T as Tr>::A` - // with `<<T as Tr>::A as Tr>::A`. - if in_parent_alias_type { - match bound.kind().skip_binder() { - ty::ClauseKind::Trait(tr) if tr.self_ty() == self_ty => continue, - ty::ClauseKind::Projection(p) if p.self_ty() == self_ty => continue, - _ => {} - } - } + // HACK: On subsequent recursions, we only care about bounds that don't + // share the same type as `self_ty`. This is because for truly rigid + // projections, we will never be able to equate, e.g. `<T as Tr>::A` + // with `<<T as Tr>::A as Tr>::A`. + let relevant_bounds = if in_parent_alias_type { + self.tcx().item_non_self_assumptions(alias_ty.def_id) + } else { + self.tcx().item_super_predicates(alias_ty.def_id) + }; + for bound in relevant_bounds.instantiate(self.tcx(), alias_ty.args) { for_each(self, bound, idx)?; idx += 1; } @@ -2263,6 +2262,20 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } + fn fused_iterator_conditions( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + if let ty::Coroutine(did, ..) = *self_ty.kind() + && self.tcx().coroutine_is_gen(did) + { + BuiltinImplConditions::Where(ty::Binder::dummy(Vec::new())) + } else { + BuiltinImplConditions::None + } + } + /// For default impls, we need to break apart a type into its /// "constituent types" -- meaning, the types that it contains. /// @@ -2304,9 +2317,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { bug!("asked to assemble constituent types of unexpected type: {:?}", t); } - ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { - t.rebind(vec![element_ty]) - } + ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => t.rebind(vec![element_ty]), ty::Array(element_ty, _) | ty::Slice(element_ty) => t.rebind(vec![element_ty]), diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 95db9e2092f..1aa65b87f0b 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -396,7 +396,7 @@ pub(crate) fn assoc_def( // associated type. Normally this situation // could only arise through a compiler bug -- // if the user wrote a bad item name, it - // should have failed in astconv. + // should have failed during HIR ty lowering. bug!( "No associated type `{}` for {}", tcx.item_name(assoc_def_id), diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 64f02bfd321..7941a8fe95c 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -363,7 +363,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // Negative trait predicates don't require supertraits to hold, just // that their args are WF. - if trait_pred.polarity == ty::ImplPolarity::Negative { + if trait_pred.polarity == ty::PredicatePolarity::Negative { self.compute_negative_trait_pred(trait_ref); return; } @@ -687,7 +687,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { } } - ty::RawPtr(_) => { + ty::RawPtr(_, _) => { // Simple cases that are WF if their type args are WF. } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 9a43d67d435..f6bc224c7e7 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -174,10 +174,10 @@ pub(crate) mod rustc { use crate::layout::rustc::{Def, Ref}; use rustc_middle::ty::layout::LayoutError; - use rustc_middle::ty::util::Discr; use rustc_middle::ty::AdtDef; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::ParamEnv; + use rustc_middle::ty::ScalarInt; use rustc_middle::ty::VariantDef; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::ErrorGuaranteed; @@ -331,14 +331,15 @@ pub(crate) mod rustc { trace!(?adt_def, "treeifying enum"); let mut tree = Tree::uninhabited(); - for (idx, discr) in adt_def.discriminants(tcx) { + for (idx, variant) in adt_def.variants().iter_enumerated() { + let tag = tcx.tag_for_variant((ty, idx)); tree = tree.or(Self::from_repr_c_variant( ty, *adt_def, args_ref, &layout_summary, - Some(discr), - adt_def.variant(idx), + tag, + variant, tcx, )?); } @@ -393,7 +394,7 @@ pub(crate) mod rustc { adt_def: AdtDef<'tcx>, args_ref: GenericArgsRef<'tcx>, layout_summary: &LayoutSummary, - discr: Option<Discr<'tcx>>, + tag: Option<ScalarInt>, variant_def: &'tcx VariantDef, tcx: TyCtxt<'tcx>, ) -> Result<Self, Err> { @@ -403,9 +404,6 @@ pub(crate) mod rustc { let min_align = repr.align.unwrap_or(Align::ONE); let max_align = repr.pack.unwrap_or(Align::MAX); - let clamp = - |align: Align| align.clamp(min_align, max_align).bytes().try_into().unwrap(); - let variant_span = trace_span!( "treeifying variant", min_align = ?min_align, @@ -419,17 +417,12 @@ pub(crate) mod rustc { ) .unwrap(); - // The layout of the variant is prefixed by the discriminant, if any. - if let Some(discr) = discr { - trace!(?discr, "treeifying discriminant"); - let discr_layout = alloc::Layout::from_size_align( - layout_summary.discriminant_size, - clamp(layout_summary.discriminant_align), - ) - .unwrap(); - trace!(?discr_layout, "computed discriminant layout"); - variant_layout = variant_layout.extend(discr_layout).unwrap().0; - tree = tree.then(Self::from_discr(discr, tcx, layout_summary.discriminant_size)); + // The layout of the variant is prefixed by the tag, if any. + if let Some(tag) = tag { + let tag_layout = + alloc::Layout::from_size_align(tag.size().bytes_usize(), 1).unwrap(); + tree = tree.then(Self::from_tag(tag, tcx)); + variant_layout = variant_layout.extend(tag_layout).unwrap().0; } // Next come fields. @@ -469,18 +462,19 @@ pub(crate) mod rustc { Ok(tree) } - pub fn from_discr(discr: Discr<'tcx>, tcx: TyCtxt<'tcx>, size: usize) -> Self { + pub fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self { use rustc_target::abi::Endian; - + let size = tag.size(); + let bits = tag.to_bits(size).unwrap(); let bytes: [u8; 16]; let bytes = match tcx.data_layout.endian { Endian::Little => { - bytes = discr.val.to_le_bytes(); - &bytes[..size] + bytes = bits.to_le_bytes(); + &bytes[..size.bytes_usize()] } Endian::Big => { - bytes = discr.val.to_be_bytes(); - &bytes[bytes.len() - size..] + bytes = bits.to_be_bytes(); + &bytes[bytes.len() - size.bytes_usize()..] } }; Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect()) diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index baf4de768c5..af1dfb6f7e9 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -623,7 +623,7 @@ fn fn_abi_new_uncached<'tcx>( let is_return = arg_idx.is_none(); let is_drop_target = is_drop_in_place && arg_idx == Some(0); let drop_target_pointee = is_drop_target.then(|| match ty.kind() { - ty::RawPtr(ty::TypeAndMut { ty, .. }) => *ty, + ty::RawPtr(ty, _) => *ty, _ => bug!("argument to drop_in_place is not a raw ptr: {:?}", ty), }); diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index e32179d56d1..9c3d39307b2 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::layout::{ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; +use rustc_span::sym; use rustc_span::symbol::Symbol; use rustc_target::abi::*; @@ -156,7 +157,7 @@ fn layout_of_uncached<'tcx>( ty::Never => tcx.mk_layout(cx.layout_of_never_type()), // Potentially-wide pointers. - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + ty::Ref(_, pointee, _) | ty::RawPtr(pointee, _) => { let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA)); if !ty.is_unsafe_ptr() { data_ptr.valid_range_mut().start = 1; @@ -1007,6 +1008,7 @@ fn variant_info_for_adt<'tcx>( offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), + type_name: None, } }) .collect(); @@ -1090,6 +1092,7 @@ fn variant_info_for_coroutine<'tcx>( offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), + type_name: None, } }) .collect(); @@ -1104,19 +1107,24 @@ fn variant_info_for_coroutine<'tcx>( .iter() .enumerate() .map(|(field_idx, local)| { + let field_name = coroutine.field_names[*local]; let field_layout = variant_layout.field(cx, field_idx); let offset = variant_layout.fields.offset(field_idx); // The struct is as large as the last field's end variant_size = variant_size.max(offset + field_layout.size); FieldInfo { kind: FieldKind::CoroutineLocal, - name: coroutine.field_names[*local].unwrap_or(Symbol::intern(&format!( + name: field_name.unwrap_or(Symbol::intern(&format!( ".coroutine_field{}", local.as_usize() ))), offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), + // Include the type name if there is no field name, or if the name is the + // __awaitee placeholder symbol which means a child future being `.await`ed. + type_name: (field_name.is_none() || field_name == Some(sym::__awaitee)) + .then(|| Symbol::intern(&field_layout.ty.to_string())), } }) .chain(upvar_fields.iter().copied()) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index b38ef2ad84d..cd199222d90 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -85,7 +85,7 @@ bitflags! { | TypeFlags::HAS_TY_INHERENT.bits() | TypeFlags::HAS_CT_PROJECTION.bits(); - /// Is an error type/const reachable? + /// Is an error type/lifetime/const reachable? const HAS_ERROR = 1 << 15; /// Does this have any region that "appears free" in the type? diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index de8f56618d0..5ed73cd94f4 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -60,7 +60,7 @@ impl AliasKind { /// Defines the kinds of types used by the type system. /// /// Types written by the user start out as `hir::TyKind` and get -/// converted to this representation using `AstConv::ast_ty_to_ty`. +/// converted to this representation using `<dyn HirTyLowerer>::lower_ty`. #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "IrTyKind")] #[derive(derivative::Derivative)] #[derivative( @@ -112,7 +112,7 @@ pub enum TyKind<I: Interner> { Slice(I::Ty), /// A raw pointer. Written as `*mut T` or `*const T` - RawPtr(TypeAndMut<I>), + RawPtr(I::Ty, Mutability), /// A reference; a pointer with an associated lifetime. Written as /// `&'a mut T` or `&'a T`. @@ -270,7 +270,7 @@ const fn tykind_discriminant<I: Interner>(value: &TyKind<I>) -> usize { Str => 7, Array(_, _) => 8, Slice(_) => 9, - RawPtr(_) => 10, + RawPtr(_, _) => 10, Ref(_, _, _) => 11, FnDef(_, _) => 12, FnPtr(_) => 13, @@ -308,7 +308,7 @@ impl<I: Interner> PartialEq for TyKind<I> { (Foreign(a_d), Foreign(b_d)) => a_d == b_d, (Array(a_t, a_c), Array(b_t, b_c)) => a_t == b_t && a_c == b_c, (Slice(a_t), Slice(b_t)) => a_t == b_t, - (RawPtr(a_t), RawPtr(b_t)) => a_t == b_t, + (RawPtr(a_t, a_m), RawPtr(b_t, b_m)) => a_t == b_t && a_m == b_m, (Ref(a_r, a_t, a_m), Ref(b_r, b_t, b_m)) => a_r == b_r && a_t == b_t && a_m == b_m, (FnDef(a_d, a_s), FnDef(b_d, b_s)) => a_d == b_d && a_s == b_s, (FnPtr(a_s), FnPtr(b_s)) => a_s == b_s, @@ -371,7 +371,7 @@ impl<I: Interner> DebugWithInfcx<I> for TyKind<I> { Str => write!(f, "str"), Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)), Slice(t) => write!(f, "[{:?}]", &this.wrap(t)), - RawPtr(TypeAndMut { ty, mutbl }) => { + RawPtr(ty, mutbl) => { match mutbl { Mutability::Mut => write!(f, "*mut "), Mutability::Not => write!(f, "*const "), diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index f53dbcfbd96..8ed34fab54d 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -8,7 +8,7 @@ use std::cell::Cell; use crate::abi::{FnAbi, Layout, LayoutShape}; use crate::mir::alloc::{AllocId, GlobalAlloc}; use crate::mir::mono::{Instance, InstanceDef, StaticDef}; -use crate::mir::Body; +use crate::mir::{Body, Place}; use crate::target::MachineInfo; use crate::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, @@ -126,12 +126,15 @@ pub trait Context { fn def_ty_with_args(&self, item: DefId, args: &GenericArgs) -> Ty; /// Returns literal value of a const as a string. - fn const_literal(&self, cnst: &Const) -> String; + fn const_pretty(&self, cnst: &Const) -> String; /// `Span` of an item fn span_of_an_item(&self, def_id: DefId) -> Span; /// Obtain the representation of a type. + fn ty_pretty(&self, ty: Ty) -> String; + + /// Obtain the representation of a type. fn ty_kind(&self, ty: Ty) -> TyKind; // Get the discriminant Ty for this Ty if there's one. @@ -205,6 +208,9 @@ pub trait Context { /// Get the layout shape. fn layout_shape(&self, id: Layout) -> LayoutShape; + + /// Get a debug string representation of a place. + fn place_pretty(&self, place: &Place) -> String; } // A thread local variable that stores a pointer to the tables mapping between TyCtxt diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs index d849c834ae0..d1a2948ea77 100644 --- a/compiler/stable_mir/src/lib.rs +++ b/compiler/stable_mir/src/lib.rs @@ -27,7 +27,6 @@ use crate::compiler_interface::with; pub use crate::crate_def::CrateDef; pub use crate::crate_def::DefId; pub use crate::error::*; -use crate::mir::pretty::function_name; use crate::mir::Body; use crate::mir::Mutability; use crate::ty::{ForeignModuleDef, ImplDef, IndexedVal, Span, TraitDef, Ty}; @@ -148,9 +147,8 @@ impl CrateItem { with(|cx| cx.is_foreign_item(self.0)) } - pub fn dump<W: io::Write>(&self, w: &mut W) -> io::Result<()> { - writeln!(w, "{}", function_name(*self))?; - self.body().dump(w) + pub fn emit_mir<W: io::Write>(&self, w: &mut W) -> io::Result<()> { + self.body().dump(w, &self.name()) } } diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/stable_mir/src/mir/alloc.rs index 66457933438..408e0bafa58 100644 --- a/compiler/stable_mir/src/mir/alloc.rs +++ b/compiler/stable_mir/src/mir/alloc.rs @@ -55,7 +55,7 @@ impl IndexedVal for AllocId { /// Utility function used to read an allocation data into a unassigned integer. pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result<u128, Error> { let mut buf = [0u8; std::mem::size_of::<u128>()]; - match MachineInfo::target_endianess() { + match MachineInfo::target_endianness() { Endian::Little => { bytes.read_exact(&mut buf[..bytes.len()])?; Ok(u128::from_le_bytes(buf)) @@ -70,7 +70,7 @@ pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result<u128, Error> { /// Utility function used to read an allocation data into an assigned integer. pub(crate) fn read_target_int(mut bytes: &[u8]) -> Result<i128, Error> { let mut buf = [0u8; std::mem::size_of::<i128>()]; - match MachineInfo::target_endianess() { + match MachineInfo::target_endianness() { Endian::Little => { bytes.read_exact(&mut buf[..bytes.len()])?; Ok(i128::from_le_bytes(buf)) diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index ae8e71bb950..7c536a3e914 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -1,10 +1,11 @@ -use crate::mir::pretty::{function_body, pretty_statement, pretty_terminator}; +use crate::mir::pretty::function_body; use crate::ty::{ AdtDef, ClosureDef, Const, CoroutineDef, GenericArgs, Movability, Region, RigidTy, Ty, TyKind, VariantIdx, }; use crate::{Error, Opaque, Span, Symbol}; use std::io; + /// The SMIR representation of a single function. #[derive(Clone, Debug)] pub struct Body { @@ -90,28 +91,9 @@ impl Body { self.locals.iter().enumerate() } - pub fn dump<W: io::Write>(&self, w: &mut W) -> io::Result<()> { - writeln!(w, "{}", function_body(self))?; - self.blocks - .iter() - .enumerate() - .map(|(index, block)| -> io::Result<()> { - writeln!(w, " bb{}: {{", index)?; - let _ = block - .statements - .iter() - .map(|statement| -> io::Result<()> { - writeln!(w, "{}", pretty_statement(&statement.kind))?; - Ok(()) - }) - .collect::<Vec<_>>(); - pretty_terminator(&block.terminator.kind, w)?; - writeln!(w, "").unwrap(); - writeln!(w, " }}").unwrap(); - Ok(()) - }) - .collect::<Result<Vec<_>, _>>()?; - Ok(()) + /// Emit the body using the provided name for the signature. + pub fn dump<W: io::Write>(&self, w: &mut W, fn_name: &str) -> io::Result<()> { + function_body(w, self, fn_name) } pub fn spread_arg(&self) -> Option<Local> { @@ -639,7 +621,7 @@ impl Rvalue { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { Ok(Ty::usize_ty()) } - Rvalue::NullaryOp(NullOp::UbCheck(_), _) => Ok(Ty::bool_ty()), + Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), Rvalue::Aggregate(ak, ops) => match *ak { AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), AggregateKind::Tuple => Ok(Ty::new_tuple( @@ -674,7 +656,7 @@ pub enum Operand { Constant(Constant), } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq)] pub struct Place { pub local: Local, /// projection out of a place (access a field, deref a pointer, etc) @@ -1007,13 +989,7 @@ pub enum NullOp { /// Returns the offset of a field. OffsetOf(Vec<(VariantIdx, FieldIdx)>), /// cfg!(debug_assertions), but at codegen time - UbCheck(UbKind), -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum UbKind { - LanguageUb, - LibraryUb, + UbChecks, } impl Operand { diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index 38e5776c48c..aafa89c03e0 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -4,6 +4,7 @@ use crate::mir::Body; use crate::ty::{Allocation, ClosureDef, ClosureKind, FnDef, GenericArgs, IndexedVal, Ty}; use crate::{with, CrateItem, DefId, Error, ItemKind, Opaque, Symbol}; use std::fmt::{Debug, Formatter}; +use std::io; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum MonoItem { @@ -157,6 +158,11 @@ impl Instance { pub fn try_const_eval(&self, const_ty: Ty) -> Result<Allocation, Error> { with(|cx| cx.eval_instance(self.def, const_ty)) } + + /// Emit the body of this instance if it has one. + pub fn emit_mir<W: io::Write>(&self, w: &mut W) -> io::Result<()> { + if let Some(body) = self.body() { body.dump(w, &self.name()) } else { Ok(()) } + } } impl Debug for Instance { diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs index 8b7b488d312..4ac4833add7 100644 --- a/compiler/stable_mir/src/mir/pretty.rs +++ b/compiler/stable_mir/src/mir/pretty.rs @@ -1,185 +1,193 @@ -use crate::crate_def::CrateDef; -use crate::mir::{Operand, Rvalue, StatementKind, UnwindAction}; -use crate::ty::{DynKind, FloatTy, IntTy, RigidTy, TyKind, UintTy}; -use crate::{with, Body, CrateItem, Mutability}; +use crate::mir::{Operand, Place, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents}; +use crate::ty::{Const, IndexedVal, Ty}; +use crate::{with, Body, Mutability}; +use fmt::{Display, Formatter}; +use std::fmt::Debug; use std::io::Write; -use std::{io, iter}; +use std::{fmt, io, iter}; use super::{AssertMessage, BinOp, TerminatorKind}; -pub fn function_name(item: CrateItem) -> String { - let mut pretty_name = String::new(); - let body = item.body(); - pretty_name.push_str("fn "); - pretty_name.push_str(item.name().as_str()); - if body.arg_locals().is_empty() { - pretty_name.push_str("()"); - } else { - pretty_name.push_str("("); - } - body.arg_locals().iter().enumerate().for_each(|(index, local)| { - pretty_name.push_str(format!("_{}: ", index).as_str()); - pretty_name.push_str(&pretty_ty(local.ty.kind())); - }); - if !body.arg_locals().is_empty() { - pretty_name.push_str(")"); +use super::BorrowKind; + +impl Display for Ty { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + with(|ctx| write!(f, "{}", ctx.ty_pretty(*self))) } - let return_local = body.ret_local(); - pretty_name.push_str(" -> "); - pretty_name.push_str(&pretty_ty(return_local.ty.kind())); - pretty_name.push_str(" {"); - pretty_name } -pub fn function_body(body: &Body) -> String { - let mut pretty_body = String::new(); - body.inner_locals().iter().enumerate().for_each(|(index, local)| { - pretty_body.push_str(" "); - pretty_body.push_str(format!("let {}", ret_mutability(&local.mutability)).as_str()); - pretty_body.push_str(format!("_{}: ", index).as_str()); - pretty_body.push_str(format!("{}", pretty_ty(local.ty.kind())).as_str()); - pretty_body.push_str(";\n"); - }); - pretty_body.push_str("}"); - pretty_body +impl Debug for Place { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + with(|ctx| write!(f, "{}", ctx.place_pretty(self))) + } } -pub fn ret_mutability(mutability: &Mutability) -> String { - match mutability { - Mutability::Not => "".to_string(), - Mutability::Mut => "mut ".to_string(), - } +pub(crate) fn function_body<W: Write>(writer: &mut W, body: &Body, name: &str) -> io::Result<()> { + write!(writer, "fn {}(", name)?; + body.arg_locals() + .iter() + .enumerate() + .try_for_each(|(index, local)| write!(writer, "_{}: {}", index + 1, local.ty))?; + write!(writer, ")")?; + + let return_local = body.ret_local(); + writeln!(writer, " -> {} {{", return_local.ty)?; + + body.locals().iter().enumerate().try_for_each(|(index, local)| -> io::Result<()> { + if index == 0 || index > body.arg_count { + writeln!(writer, " let {}_{}: {};", pretty_mut(local.mutability), index, local.ty) + } else { + Ok(()) + } + })?; + + body.var_debug_info.iter().try_for_each(|info| { + let content = match &info.value { + VarDebugInfoContents::Place(place) => { + format!("{place:?}") + } + VarDebugInfoContents::Const(constant) => pretty_const(&constant.const_), + }; + writeln!(writer, " debug {} => {};", info.name, content) + })?; + + body.blocks + .iter() + .enumerate() + .map(|(index, block)| -> io::Result<()> { + writeln!(writer, " bb{}: {{", index)?; + let _ = block + .statements + .iter() + .map(|statement| -> io::Result<()> { + pretty_statement(writer, &statement.kind)?; + Ok(()) + }) + .collect::<Vec<_>>(); + pretty_terminator(writer, &block.terminator.kind)?; + writeln!(writer, " }}").unwrap(); + Ok(()) + }) + .collect::<Result<Vec<_>, _>>()?; + writeln!(writer, "}}")?; + Ok(()) } -pub fn pretty_statement(statement: &StatementKind) -> String { - let mut pretty = String::new(); +fn pretty_statement<W: Write>(writer: &mut W, statement: &StatementKind) -> io::Result<()> { match statement { StatementKind::Assign(place, rval) => { - pretty.push_str(format!(" _{} = ", place.local).as_str()); - pretty.push_str(format!("{}", &pretty_rvalue(rval)).as_str()); + write!(writer, " {:?} = ", place)?; + pretty_rvalue(writer, rval)?; + writeln!(writer, ";") } // FIXME: Add rest of the statements - StatementKind::FakeRead(_, _) => { - return String::from("StatementKind::FakeRead:Unimplemented"); + StatementKind::FakeRead(cause, place) => { + writeln!(writer, "FakeRead({cause:?}, {place:?});") } - StatementKind::SetDiscriminant { .. } => { - return String::from("StatementKind::SetDiscriminant:Unimplemented"); + StatementKind::SetDiscriminant { place, variant_index } => { + writeln!(writer, "discriminant({place:?} = {};", variant_index.to_index()) } - StatementKind::Deinit(_) => return String::from("StatementKind::Deinit:Unimplemented"), - StatementKind::StorageLive(_) => { - return String::from("StatementKind::StorageLive:Unimplemented"); + StatementKind::Deinit(place) => writeln!(writer, "Deinit({place:?};"), + StatementKind::StorageLive(local) => { + writeln!(writer, "StorageLive(_{local});") } - StatementKind::StorageDead(_) => { - return String::from("StatementKind::StorageDead:Unimplemented"); + StatementKind::StorageDead(local) => { + writeln!(writer, "StorageDead(_{local});") } - StatementKind::Retag(_, _) => return String::from("StatementKind::Retag:Unimplemented"), - StatementKind::PlaceMention(_) => { - return String::from("StatementKind::PlaceMention:Unimplemented"); - } - StatementKind::AscribeUserType { .. } => { - return String::from("StatementKind::AscribeUserType:Unimplemented"); - } - StatementKind::Coverage(_) => return String::from("StatementKind::Coverage:Unimplemented"), - StatementKind::Intrinsic(_) => { - return String::from("StatementKind::Intrinsic:Unimplemented"); + StatementKind::Retag(kind, place) => writeln!(writer, "Retag({kind:?}, {place:?});"), + StatementKind::PlaceMention(place) => { + writeln!(writer, "PlaceMention({place:?};") } StatementKind::ConstEvalCounter => { - return String::from("StatementKind::ConstEvalCounter:Unimplemented"); + writeln!(writer, "ConstEvalCounter;") + } + StatementKind::Nop => writeln!(writer, "nop;"), + StatementKind::AscribeUserType { .. } + | StatementKind::Coverage(_) + | StatementKind::Intrinsic(_) => { + // FIX-ME: Make them pretty. + writeln!(writer, "{statement:?};") } - StatementKind::Nop => return String::from("StatementKind::Nop:Unimplemented"), } - pretty } -pub fn pretty_terminator<W: io::Write>(terminator: &TerminatorKind, w: &mut W) -> io::Result<()> { - write!(w, "{}", pretty_terminator_head(terminator))?; +fn pretty_terminator<W: Write>(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { + pretty_terminator_head(writer, terminator)?; let successors = terminator.successors(); let successor_count = successors.len(); let labels = pretty_successor_labels(terminator); let show_unwind = !matches!(terminator.unwind(), None | Some(UnwindAction::Cleanup(_))); - let fmt_unwind = |fmt: &mut dyn Write| -> io::Result<()> { - write!(fmt, "unwind ")?; + let fmt_unwind = |w: &mut W| -> io::Result<()> { + write!(w, "unwind ")?; match terminator.unwind() { None | Some(UnwindAction::Cleanup(_)) => unreachable!(), - Some(UnwindAction::Continue) => write!(fmt, "continue"), - Some(UnwindAction::Unreachable) => write!(fmt, "unreachable"), - Some(UnwindAction::Terminate) => write!(fmt, "terminate"), + Some(UnwindAction::Continue) => write!(w, "continue"), + Some(UnwindAction::Unreachable) => write!(w, "unreachable"), + Some(UnwindAction::Terminate) => write!(w, "terminate"), } }; match (successor_count, show_unwind) { - (0, false) => Ok(()), + (0, false) => {} (0, true) => { - write!(w, " -> ")?; - fmt_unwind(w)?; - Ok(()) - } - (1, false) => { - write!(w, " -> {:?}", successors[0])?; - Ok(()) + write!(writer, " -> ")?; + fmt_unwind(writer)?; } + (1, false) => write!(writer, " -> bb{:?}", successors[0])?, _ => { - write!(w, " -> [")?; + write!(writer, " -> [")?; for (i, target) in successors.iter().enumerate() { if i > 0 { - write!(w, ", ")?; + write!(writer, ", ")?; } - write!(w, "{}: bb{:?}", labels[i], target)?; + write!(writer, "{}: bb{:?}", labels[i], target)?; } if show_unwind { - write!(w, ", ")?; - fmt_unwind(w)?; + write!(writer, ", ")?; + fmt_unwind(writer)?; } - write!(w, "]") + write!(writer, "]")?; } - }?; + }; - Ok(()) + writeln!(writer, ";") } -pub fn pretty_terminator_head(terminator: &TerminatorKind) -> String { +fn pretty_terminator_head<W: Write>(writer: &mut W, terminator: &TerminatorKind) -> io::Result<()> { use self::TerminatorKind::*; - let mut pretty = String::new(); + const INDENT: &'static str = " "; match terminator { - Goto { .. } => format!(" goto"), + Goto { .. } => write!(writer, "{INDENT}goto"), SwitchInt { discr, .. } => { - format!(" switchInt(_{})", pretty_operand(discr)) + write!(writer, "{INDENT}switchInt({})", pretty_operand(discr)) } - Resume => format!(" resume"), - Abort => format!(" abort"), - Return => format!(" return"), - Unreachable => format!(" unreachable"), - Drop { place, .. } => format!(" drop(_{:?})", place.local), + Resume => write!(writer, "{INDENT}resume"), + Abort => write!(writer, "{INDENT}abort"), + Return => write!(writer, "{INDENT}return"), + Unreachable => write!(writer, "{INDENT}unreachable"), + Drop { place, .. } => write!(writer, "{INDENT}drop({:?})", place), Call { func, args, destination, .. } => { - pretty.push_str(" "); - pretty.push_str(format!("_{} = ", destination.local).as_str()); - pretty.push_str(&pretty_operand(func)); - pretty.push_str("("); - args.iter().enumerate().for_each(|(i, arg)| { - if i > 0 { - pretty.push_str(", "); - } - pretty.push_str(&pretty_operand(arg)); - }); - pretty.push_str(")"); - pretty + write!(writer, "{INDENT}{:?} = {}(", destination, pretty_operand(func))?; + let mut args_iter = args.iter(); + args_iter.next().map_or(Ok(()), |arg| write!(writer, "{}", pretty_operand(arg)))?; + args_iter.try_for_each(|arg| write!(writer, ", {}", pretty_operand(arg)))?; + write!(writer, ")") } Assert { cond, expected, msg, target: _, unwind: _ } => { - pretty.push_str(" assert("); + write!(writer, "{INDENT}assert(")?; if !expected { - pretty.push_str("!"); + write!(writer, "!")?; } - pretty.push_str(format!("{} bool),", &pretty_operand(cond)).as_str()); - pretty.push_str(&pretty_assert_message(msg)); - pretty.push_str(")"); - pretty + write!(writer, "{}, ", &pretty_operand(cond))?; + pretty_assert_message(writer, msg)?; + write!(writer, ")") } - InlineAsm { .. } => todo!(), + InlineAsm { .. } => write!(writer, "{INDENT}InlineAsm"), } } -pub fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec<String> { +fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec<String> { use self::TerminatorKind::*; match terminator { Resume | Abort | Return | Unreachable => vec![], @@ -201,283 +209,174 @@ pub fn pretty_successor_labels(terminator: &TerminatorKind) -> Vec<String> { vec!["success".into(), "unwind".into()] } Assert { unwind: _, .. } => vec!["success".into()], - InlineAsm { .. } => todo!(), + InlineAsm { destination: Some(_), .. } => vec!["goto".into(), "unwind".into()], + InlineAsm { destination: None, .. } => vec!["unwind".into()], } } -pub fn pretty_assert_message(msg: &AssertMessage) -> String { - let mut pretty = String::new(); +fn pretty_assert_message<W: Write>(writer: &mut W, msg: &AssertMessage) -> io::Result<()> { match msg { AssertMessage::BoundsCheck { len, index } => { let pretty_len = pretty_operand(len); let pretty_index = pretty_operand(index); - pretty.push_str(format!("\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}").as_str()); - pretty + write!( + writer, + "\"index out of bounds: the length is {{}} but the index is {{}}\", {pretty_len}, {pretty_index}" + ) } AssertMessage::Overflow(BinOp::Add, l, r) => { let pretty_l = pretty_operand(l); let pretty_r = pretty_operand(r); - pretty.push_str(format!("\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str()); - pretty + write!( + writer, + "\"attempt to compute `{{}} + {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) } AssertMessage::Overflow(BinOp::Sub, l, r) => { let pretty_l = pretty_operand(l); let pretty_r = pretty_operand(r); - pretty.push_str(format!("\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str()); - pretty + write!( + writer, + "\"attempt to compute `{{}} - {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) } AssertMessage::Overflow(BinOp::Mul, l, r) => { let pretty_l = pretty_operand(l); let pretty_r = pretty_operand(r); - pretty.push_str(format!("\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str()); - pretty + write!( + writer, + "\"attempt to compute `{{}} * {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) } AssertMessage::Overflow(BinOp::Div, l, r) => { let pretty_l = pretty_operand(l); let pretty_r = pretty_operand(r); - pretty.push_str(format!("\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str()); - pretty + write!( + writer, + "\"attempt to compute `{{}} / {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) } AssertMessage::Overflow(BinOp::Rem, l, r) => { let pretty_l = pretty_operand(l); let pretty_r = pretty_operand(r); - pretty.push_str(format!("\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}").as_str()); - pretty + write!( + writer, + "\"attempt to compute `{{}} % {{}}`, which would overflow\", {pretty_l}, {pretty_r}" + ) } AssertMessage::Overflow(BinOp::Shr, _, r) => { let pretty_r = pretty_operand(r); - pretty.push_str( - format!("\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}") - .as_str(), - ); - pretty + write!(writer, "\"attempt to shift right by `{{}}`, which would overflow\", {pretty_r}") } AssertMessage::Overflow(BinOp::Shl, _, r) => { let pretty_r = pretty_operand(r); - pretty.push_str( - format!("\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}") - .as_str(), - ); - pretty + write!(writer, "\"attempt to shift left by `{{}}`, which would overflow\", {pretty_r}") } AssertMessage::Overflow(op, _, _) => unreachable!("`{:?}` cannot overflow", op), AssertMessage::OverflowNeg(op) => { let pretty_op = pretty_operand(op); - pretty.push_str( - format!("\"attempt to negate `{{}}`, which would overflow\", {pretty_op}").as_str(), - ); - pretty + write!(writer, "\"attempt to negate `{{}}`, which would overflow\", {pretty_op}") } AssertMessage::DivisionByZero(op) => { let pretty_op = pretty_operand(op); - pretty.push_str(format!("\"attempt to divide `{{}}` by zero\", {pretty_op}").as_str()); - pretty + write!(writer, "\"attempt to divide `{{}}` by zero\", {pretty_op}") } AssertMessage::RemainderByZero(op) => { let pretty_op = pretty_operand(op); - pretty.push_str( - format!("\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}").as_str(), - ); - pretty + write!( + writer, + "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {pretty_op}" + ) } AssertMessage::MisalignedPointerDereference { required, found } => { let pretty_required = pretty_operand(required); let pretty_found = pretty_operand(found); - pretty.push_str(format!("\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}").as_str()); - pretty + write!( + writer, + "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\",{pretty_required}, {pretty_found}" + ) } AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { - msg.description().unwrap().to_string() + write!(writer, "{}", msg.description().unwrap()) } } } -pub fn pretty_operand(operand: &Operand) -> String { - let mut pretty = String::new(); +fn pretty_operand(operand: &Operand) -> String { match operand { Operand::Copy(copy) => { - pretty.push_str(""); - pretty.push_str(format!("{}", copy.local).as_str()); + format!("{:?}", copy) } Operand::Move(mv) => { - pretty.push_str("move "); - pretty.push_str(format!("_{}", mv.local).as_str()); - } - Operand::Constant(cnst) => { - pretty.push_str("const "); - pretty.push_str(with(|cx| cx.const_literal(&cnst.literal)).as_str()); + format!("move {:?}", mv) } + Operand::Constant(cnst) => pretty_const(&cnst.literal), } - pretty } -pub fn pretty_rvalue(rval: &Rvalue) -> String { - let mut pretty = String::new(); +fn pretty_const(literal: &Const) -> String { + with(|cx| cx.const_pretty(&literal)) +} + +fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> { match rval { - Rvalue::AddressOf(muta, addr) => { - pretty.push_str("&raw "); - pretty.push_str(&ret_mutability(muta)); - pretty.push_str(format!("(*_{})", addr.local).as_str()); - } - Rvalue::Aggregate(aggregatekind, operands) => { - pretty.push_str(format!("{:#?}", aggregatekind).as_str()); - pretty.push_str("("); - operands.iter().enumerate().for_each(|(i, op)| { - pretty.push_str(&pretty_operand(op)); - if i != operands.len() - 1 { - pretty.push_str(", "); - } - }); - pretty.push_str(")"); + Rvalue::AddressOf(mutability, place) => { + write!(writer, "&raw {}(*{:?})", &pretty_mut(*mutability), place) } - Rvalue::BinaryOp(bin, op, op2) => { - pretty.push_str(&pretty_operand(op)); - pretty.push_str(" "); - pretty.push_str(format!("{:#?}", bin).as_str()); - pretty.push_str(" "); - pretty.push_str(&pretty_operand(op2)); + Rvalue::Aggregate(aggregate_kind, operands) => { + // FIXME: Add pretty_aggregate function that returns a pretty string + write!(writer, "{aggregate_kind:?} (")?; + let mut op_iter = operands.iter(); + op_iter.next().map_or(Ok(()), |op| write!(writer, "{}", pretty_operand(op)))?; + op_iter.try_for_each(|op| write!(writer, ", {}", pretty_operand(op)))?; + write!(writer, ")") + } + Rvalue::BinaryOp(bin, op1, op2) => { + write!(writer, "{:?}({}, {})", bin, &pretty_operand(op1), pretty_operand(op2)) } Rvalue::Cast(_, op, ty) => { - pretty.push_str(&pretty_operand(op)); - pretty.push_str(" as "); - pretty.push_str(&pretty_ty(ty.kind())); + write!(writer, "{} as {}", pretty_operand(op), ty) } Rvalue::CheckedBinaryOp(bin, op1, op2) => { - pretty.push_str(&pretty_operand(op1)); - pretty.push_str(" "); - pretty.push_str(format!("{:#?}", bin).as_str()); - pretty.push_str(" "); - pretty.push_str(&pretty_operand(op2)); + write!(writer, "Checked{:?}({}, {})", bin, &pretty_operand(op1), pretty_operand(op2)) } Rvalue::CopyForDeref(deref) => { - pretty.push_str("CopyForDeref"); - pretty.push_str(format!("{}", deref.local).as_str()); + write!(writer, "CopyForDeref({:?})", deref) } Rvalue::Discriminant(place) => { - pretty.push_str("discriminant"); - pretty.push_str(format!("{}", place.local).as_str()); + write!(writer, "discriminant({:?})", place) } Rvalue::Len(len) => { - pretty.push_str("len"); - pretty.push_str(format!("{}", len.local).as_str()); + write!(writer, "len({:?})", len) } Rvalue::Ref(_, borrowkind, place) => { - pretty.push_str("ref"); - pretty.push_str(format!("{:#?}", borrowkind).as_str()); - pretty.push_str(format!("{}", place.local).as_str()); + let kind = match borrowkind { + BorrowKind::Shared => "&", + BorrowKind::Fake => "&fake ", + BorrowKind::Mut { .. } => "&mut ", + }; + write!(writer, "{kind}{:?}", place) } Rvalue::Repeat(op, cnst) => { - pretty.push_str(&pretty_operand(op)); - pretty.push_str(" "); - pretty.push_str(&pretty_ty(cnst.ty().kind())); + write!(writer, "{} \" \" {}", &pretty_operand(op), cnst.ty()) } - Rvalue::ShallowInitBox(_, _) => (), + Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::ThreadLocalRef(item) => { - pretty.push_str("thread_local_ref"); - pretty.push_str(format!("{:#?}", item).as_str()); + write!(writer, "thread_local_ref{:?}", item) } Rvalue::NullaryOp(nul, ty) => { - pretty.push_str(format!("{:#?}", nul).as_str()); - pretty.push_str(&pretty_ty(ty.kind())); - pretty.push_str(" "); + write!(writer, "{:?} {} \" \"", nul, ty) } Rvalue::UnaryOp(un, op) => { - pretty.push_str(&pretty_operand(op)); - pretty.push_str(" "); - pretty.push_str(format!("{:#?}", un).as_str()); + write!(writer, "{} \" \" {:?}", pretty_operand(op), un) } - Rvalue::Use(op) => pretty.push_str(&pretty_operand(op)), + Rvalue::Use(op) => write!(writer, "{}", pretty_operand(op)), } - pretty } -pub fn pretty_ty(ty: TyKind) -> String { - let mut pretty = String::new(); - match ty { - TyKind::RigidTy(rigid_ty) => match rigid_ty { - RigidTy::Bool => "bool".to_string(), - RigidTy::Char => "char".to_string(), - RigidTy::Int(i) => match i { - IntTy::Isize => "isize".to_string(), - IntTy::I8 => "i8".to_string(), - IntTy::I16 => "i16".to_string(), - IntTy::I32 => "i32".to_string(), - IntTy::I64 => "i64".to_string(), - IntTy::I128 => "i128".to_string(), - }, - RigidTy::Uint(u) => match u { - UintTy::Usize => "usize".to_string(), - UintTy::U8 => "u8".to_string(), - UintTy::U16 => "u16".to_string(), - UintTy::U32 => "u32".to_string(), - UintTy::U64 => "u64".to_string(), - UintTy::U128 => "u128".to_string(), - }, - RigidTy::Float(f) => match f { - FloatTy::F32 => "f32".to_string(), - FloatTy::F64 => "f64".to_string(), - }, - RigidTy::Adt(def, _) => { - format!("{:#?}", with(|cx| cx.def_ty(def.0))) - } - RigidTy::Str => "str".to_string(), - RigidTy::Array(ty, len) => { - format!("[{}; {}]", pretty_ty(ty.kind()), with(|cx| cx.const_literal(&len))) - } - RigidTy::Slice(ty) => { - format!("[{}]", pretty_ty(ty.kind())) - } - RigidTy::RawPtr(ty, mutability) => { - pretty.push_str("*"); - match mutability { - Mutability::Not => pretty.push_str("const "), - Mutability::Mut => pretty.push_str("mut "), - } - pretty.push_str(&pretty_ty(ty.kind())); - pretty - } - RigidTy::Ref(_, ty, mutability) => match mutability { - Mutability::Not => format!("&{}", pretty_ty(ty.kind())), - Mutability::Mut => format!("&mut {}", pretty_ty(ty.kind())), - }, - RigidTy::FnDef(_, _) => format!("{:#?}", rigid_ty), - RigidTy::FnPtr(_) => format!("{:#?}", rigid_ty), - RigidTy::Closure(_, _) => format!("{:#?}", rigid_ty), - RigidTy::Coroutine(_, _, _) => format!("{:#?}", rigid_ty), - RigidTy::Dynamic(data, region, repr) => { - // FIXME: Fix binder printing, it looks ugly now - pretty.push_str("("); - match repr { - DynKind::Dyn => pretty.push_str("dyn "), - DynKind::DynStar => pretty.push_str("dyn* "), - } - pretty.push_str(format!("{:#?}", data).as_str()); - pretty.push_str(format!(" + {:#?} )", region).as_str()); - pretty - } - RigidTy::Never => "!".to_string(), - RigidTy::Tuple(tuple) => { - if tuple.is_empty() { - "()".to_string() - } else { - let mut tuple_str = String::new(); - tuple_str.push_str("("); - tuple.iter().enumerate().for_each(|(i, ty)| { - tuple_str.push_str(&pretty_ty(ty.kind())); - if i != tuple.len() - 1 { - tuple_str.push_str(", "); - } - }); - tuple_str.push_str(")"); - tuple_str - } - } - _ => format!("{:#?}", rigid_ty), - }, - TyKind::Alias(_, _) => format!("{:#?}", ty), - TyKind::Param(param_ty) => { - format!("{:#?}", param_ty.name) - } - TyKind::Bound(_, _) => format!("{:#?}", ty), +fn pretty_mut(mutability: Mutability) -> &'static str { + match mutability { + Mutability::Not => " ", + Mutability::Mut => "mut ", } } diff --git a/compiler/stable_mir/src/target.rs b/compiler/stable_mir/src/target.rs index 3a9011a2ffe..e00a418c540 100644 --- a/compiler/stable_mir/src/target.rs +++ b/compiler/stable_mir/src/target.rs @@ -14,7 +14,7 @@ impl MachineInfo { with(|cx| cx.target_info()) } - pub fn target_endianess() -> Endian { + pub fn target_endianness() -> Endian { with(|cx| cx.target_info().endian) } diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index a3376752028..21db222095f 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -1332,7 +1332,7 @@ pub enum AliasRelationDirection { #[derive(Clone, Debug, Eq, PartialEq)] pub struct TraitPredicate { pub trait_ref: TraitRef, - pub polarity: ImplPolarity, + pub polarity: PredicatePolarity, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -1354,6 +1354,12 @@ pub enum ImplPolarity { Reservation, } +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum PredicatePolarity { + Positive, + Negative, +} + pub trait IndexedVal { fn to_val(index: usize) -> Self; diff --git a/config.example.toml b/config.example.toml index f94553dd63f..b8cdc2ec848 100644 --- a/config.example.toml +++ b/config.example.toml @@ -915,6 +915,6 @@ # Available options: fast, balanced, best #compression-profile = "fast" -# Copy the linker, DLLs, and various libraries from MinGW into the rustc toolchain. +# Copy the linker, DLLs, and various libraries from MinGW into the Rust toolchain. # Only applies when the host or target is pc-windows-gnu. #include-mingw-linker = true diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index e99c6220e20..3875f61efaf 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -72,7 +72,7 @@ pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// `BTreeMap` that observed the logic error and not result in undefined behavior. This could /// include panics, incorrect results, aborts, memory leaks, and non-termination. /// -/// Iterators obtained from functions such as [`BTreeMap::iter`], [`BTreeMap::values`], or +/// Iterators obtained from functions such as [`BTreeMap::iter`], [`BTreeMap::into_iter`], [`BTreeMap::values`], or /// [`BTreeMap::keys`] produce their items in order by key, and take worst-case logarithmic and /// amortized constant time per item returned. /// @@ -415,7 +415,7 @@ impl<'a, K: 'a, V: 'a> Default for IterMut<'a, K, V> { } } -/// An owning iterator over the entries of a `BTreeMap`. +/// An owning iterator over the entries of a `BTreeMap`, sorted by key. /// /// This `struct` is created by the [`into_iter`] method on [`BTreeMap`] /// (provided by the [`IntoIterator`] trait). See its documentation for more. @@ -1637,6 +1637,7 @@ impl<K, V, A: Allocator + Clone> IntoIterator for BTreeMap<K, V, A> { type Item = (K, V); type IntoIter = IntoIter<K, V, A>; + /// Gets an owning iterator over the entries of the map, sorted by key. fn into_iter(self) -> IntoIter<K, V, A> { let mut me = ManuallyDrop::new(self); if let Some(root) = me.root.take() { diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index a85a3162451..5e6a26f65c4 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -655,7 +655,7 @@ impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Lea pub enum Position<BorrowType, K, V> { Leaf(NodeRef<BorrowType, K, V, marker::Leaf>), Internal(NodeRef<BorrowType, K, V, marker::Internal>), - InternalKV(Handle<NodeRef<BorrowType, K, V, marker::Internal>, marker::KV>), + InternalKV, } impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> { @@ -677,7 +677,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> visit(Position::Leaf(leaf)); match edge.next_kv() { Ok(kv) => { - visit(Position::InternalKV(kv)); + visit(Position::InternalKV); kv.right_edge() } Err(_) => return, @@ -699,7 +699,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> self.visit_nodes_in_order(|pos| match pos { Position::Leaf(node) => result += node.len(), Position::Internal(node) => result += node.len(), - Position::InternalKV(_) => (), + Position::InternalKV => (), }); result } diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index 64bce0ff8c0..d230749d712 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -32,11 +32,7 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> result += &format!("\n{}{:?}", indent, leaf.keys()); } navigate::Position::Internal(_) => {} - navigate::Position::InternalKV(kv) => { - let depth = self.height() - kv.into_node().height(); - let indent = " ".repeat(depth); - result += &format!("\n{}{:?}", indent, kv.into_kv().0); - } + navigate::Position::InternalKV => {} }); result } diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index ed91ae1a66e..7508ae468ae 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -27,7 +27,7 @@ use crate::alloc::{Allocator, Global}; /// `BTreeSet` that observed the logic error and not result in undefined behavior. This could /// include panics, incorrect results, aborts, memory leaks, and non-termination. /// -/// Iterators returned by [`BTreeSet::iter`] produce their items in order, and take worst-case +/// Iterators returned by [`BTreeSet::iter`] and [`BTreeSet::into_iter`] produce their items in order, and take worst-case /// logarithmic and amortized constant time per item returned. /// /// [`Cell`]: core::cell::Cell @@ -140,7 +140,7 @@ impl<T: fmt::Debug> fmt::Debug for Iter<'_, T> { } } -/// An owning iterator over the items of a `BTreeSet`. +/// An owning iterator over the items of a `BTreeSet` in ascending order. /// /// This `struct` is created by the [`into_iter`] method on [`BTreeSet`] /// (provided by the [`IntoIterator`] trait). See its documentation for more. @@ -1237,7 +1237,7 @@ impl<T, A: Allocator + Clone> IntoIterator for BTreeSet<T, A> { type Item = T; type IntoIter = IntoIter<T, A>; - /// Gets an iterator for moving out the `BTreeSet`'s contents. + /// Gets an iterator for moving out the `BTreeSet`'s contents in ascending order. /// /// # Examples /// diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 80f0f2acc99..7e3e2fb38b1 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -233,7 +233,7 @@ macro_rules! acquire { /// let val = Arc::clone(&val); /// /// thread::spawn(move || { -/// let v = val.fetch_add(1, Ordering::SeqCst); +/// let v = val.fetch_add(1, Ordering::Relaxed); /// println!("{v:?}"); /// }); /// } diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 07eb91c9005..4907a45e881 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -229,96 +229,106 @@ where I: Iterator<Item = T> + InPlaceCollect, <I as SourceIter>::Source: AsVecIntoIter, { - default fn from_iter(mut iterator: I) -> Self { - // See "Layout constraints" section in the module documentation. We rely on const - // optimization here since these conditions currently cannot be expressed as trait bounds - if const { !in_place_collectible::<T, I::Src>(I::MERGE_BY, I::EXPAND_BY) } { - // fallback to more generic implementations - return SpecFromIterNested::from_iter(iterator); - } - - let (src_buf, src_ptr, src_cap, mut dst_buf, dst_end, dst_cap) = unsafe { - let inner = iterator.as_inner().as_into_iter(); - ( - inner.buf.as_ptr(), - inner.ptr, - inner.cap, - inner.buf.as_ptr() as *mut T, - inner.end as *const T, - inner.cap * mem::size_of::<I::Src>() / mem::size_of::<T>(), - ) + default fn from_iter(iterator: I) -> Self { + // Select the implementation in const eval to avoid codegen of the dead branch to improve compile times. + let fun: fn(I) -> Vec<T> = const { + // See "Layout constraints" section in the module documentation. We use const conditions here + // since these conditions currently cannot be expressed as trait bounds + if in_place_collectible::<T, I::Src>(I::MERGE_BY, I::EXPAND_BY) { + from_iter_in_place + } else { + // fallback + SpecFromIterNested::<T, I>::from_iter + } }; - // SAFETY: `dst_buf` and `dst_end` are the start and end of the buffer. - let len = unsafe { SpecInPlaceCollect::collect_in_place(&mut iterator, dst_buf, dst_end) }; + fun(iterator) + } +} - let src = unsafe { iterator.as_inner().as_into_iter() }; - // check if SourceIter contract was upheld - // caveat: if they weren't we might not even make it to this point - debug_assert_eq!(src_buf, src.buf.as_ptr()); - // check InPlaceIterable contract. This is only possible if the iterator advanced the - // source pointer at all. If it uses unchecked access via TrustedRandomAccess - // then the source pointer will stay in its initial position and we can't use it as reference - if src.ptr != src_ptr { - debug_assert!( - unsafe { dst_buf.add(len) as *const _ } <= src.ptr.as_ptr(), - "InPlaceIterable contract violation, write pointer advanced beyond read pointer" - ); - } +fn from_iter_in_place<I, T>(mut iterator: I) -> Vec<T> +where + I: Iterator<Item = T> + InPlaceCollect, + <I as SourceIter>::Source: AsVecIntoIter, +{ + let (src_buf, src_ptr, src_cap, mut dst_buf, dst_end, dst_cap) = unsafe { + let inner = iterator.as_inner().as_into_iter(); + ( + inner.buf.as_ptr(), + inner.ptr, + inner.cap, + inner.buf.as_ptr() as *mut T, + inner.end as *const T, + inner.cap * mem::size_of::<I::Src>() / mem::size_of::<T>(), + ) + }; - // The ownership of the source allocation and the new `T` values is temporarily moved into `dst_guard`. - // This is safe because - // * `forget_allocation_drop_remaining` immediately forgets the allocation - // before any panic can occur in order to avoid any double free, and then proceeds to drop - // any remaining values at the tail of the source. - // * the shrink either panics without invalidating the allocation, aborts or - // succeeds. In the last case we disarm the guard. - // - // Note: This access to the source wouldn't be allowed by the TrustedRandomIteratorNoCoerce - // contract (used by SpecInPlaceCollect below). But see the "O(1) collect" section in the - // module documentation why this is ok anyway. - let dst_guard = - InPlaceDstDataSrcBufDrop { ptr: dst_buf, len, src_cap, src: PhantomData::<I::Src> }; - src.forget_allocation_drop_remaining(); + // SAFETY: `dst_buf` and `dst_end` are the start and end of the buffer. + let len = unsafe { SpecInPlaceCollect::collect_in_place(&mut iterator, dst_buf, dst_end) }; - // Adjust the allocation if the source had a capacity in bytes that wasn't a multiple - // of the destination type size. - // Since the discrepancy should generally be small this should only result in some - // bookkeeping updates and no memmove. - if needs_realloc::<I::Src, T>(src_cap, dst_cap) { - let alloc = Global; - debug_assert_ne!(src_cap, 0); - debug_assert_ne!(dst_cap, 0); - unsafe { - // The old allocation exists, therefore it must have a valid layout. - let src_align = mem::align_of::<I::Src>(); - let src_size = mem::size_of::<I::Src>().unchecked_mul(src_cap); - let old_layout = Layout::from_size_align_unchecked(src_size, src_align); + let src = unsafe { iterator.as_inner().as_into_iter() }; + // check if SourceIter contract was upheld + // caveat: if they weren't we might not even make it to this point + debug_assert_eq!(src_buf, src.buf.as_ptr()); + // check InPlaceIterable contract. This is only possible if the iterator advanced the + // source pointer at all. If it uses unchecked access via TrustedRandomAccess + // then the source pointer will stay in its initial position and we can't use it as reference + if src.ptr != src_ptr { + debug_assert!( + unsafe { dst_buf.add(len) as *const _ } <= src.ptr.as_ptr(), + "InPlaceIterable contract violation, write pointer advanced beyond read pointer" + ); + } - // The allocation must be equal or smaller for in-place iteration to be possible - // therefore the new layout must be ≤ the old one and therefore valid. - let dst_align = mem::align_of::<T>(); - let dst_size = mem::size_of::<T>().unchecked_mul(dst_cap); - let new_layout = Layout::from_size_align_unchecked(dst_size, dst_align); + // The ownership of the source allocation and the new `T` values is temporarily moved into `dst_guard`. + // This is safe because + // * `forget_allocation_drop_remaining` immediately forgets the allocation + // before any panic can occur in order to avoid any double free, and then proceeds to drop + // any remaining values at the tail of the source. + // * the shrink either panics without invalidating the allocation, aborts or + // succeeds. In the last case we disarm the guard. + // + // Note: This access to the source wouldn't be allowed by the TrustedRandomIteratorNoCoerce + // contract (used by SpecInPlaceCollect below). But see the "O(1) collect" section in the + // module documentation why this is ok anyway. + let dst_guard = + InPlaceDstDataSrcBufDrop { ptr: dst_buf, len, src_cap, src: PhantomData::<I::Src> }; + src.forget_allocation_drop_remaining(); - let result = alloc.shrink( - NonNull::new_unchecked(dst_buf as *mut u8), - old_layout, - new_layout, - ); - let Ok(reallocated) = result else { handle_alloc_error(new_layout) }; - dst_buf = reallocated.as_ptr() as *mut T; - } - } else { - debug_assert_eq!(src_cap * mem::size_of::<I::Src>(), dst_cap * mem::size_of::<T>()); + // Adjust the allocation if the source had a capacity in bytes that wasn't a multiple + // of the destination type size. + // Since the discrepancy should generally be small this should only result in some + // bookkeeping updates and no memmove. + if needs_realloc::<I::Src, T>(src_cap, dst_cap) { + let alloc = Global; + debug_assert_ne!(src_cap, 0); + debug_assert_ne!(dst_cap, 0); + unsafe { + // The old allocation exists, therefore it must have a valid layout. + let src_align = mem::align_of::<I::Src>(); + let src_size = mem::size_of::<I::Src>().unchecked_mul(src_cap); + let old_layout = Layout::from_size_align_unchecked(src_size, src_align); + + // The allocation must be equal or smaller for in-place iteration to be possible + // therefore the new layout must be ≤ the old one and therefore valid. + let dst_align = mem::align_of::<T>(); + let dst_size = mem::size_of::<T>().unchecked_mul(dst_cap); + let new_layout = Layout::from_size_align_unchecked(dst_size, dst_align); + + let result = + alloc.shrink(NonNull::new_unchecked(dst_buf as *mut u8), old_layout, new_layout); + let Ok(reallocated) = result else { handle_alloc_error(new_layout) }; + dst_buf = reallocated.as_ptr() as *mut T; } + } else { + debug_assert_eq!(src_cap * mem::size_of::<I::Src>(), dst_cap * mem::size_of::<T>()); + } - mem::forget(dst_guard); + mem::forget(dst_guard); - let vec = unsafe { Vec::from_raw_parts(dst_buf, len, dst_cap) }; + let vec = unsafe { Vec::from_raw_parts(dst_buf, len, dst_cap) }; - vec - } + vec } fn write_in_place_with_drop<T>( diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index db56b8d07c7..94bed825bb2 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1541,6 +1541,9 @@ impl<T, A: Allocator> Vec<T, A> { } let len = self.len(); + if index > len { + assert_failed(index, len); + } // space for the new element if len == self.buf.capacity() { @@ -1556,10 +1559,6 @@ impl<T, A: Allocator> Vec<T, A> { // Shift everything over to make space. (Duplicating the // `index`th element into two consecutive places.) ptr::copy(p, p.add(1), len - index); - } else if index == len { - // No elements need shifting. - } else { - assert_failed(index, len); } // Write it in, overwriting the first copy of the `index`th // element. diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index aa95b4e9770..f1f841fe190 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -2643,3 +2643,44 @@ fn test_vec_from_array_ref() { fn test_vec_from_array_mut_ref() { assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]); } + +/// This assortment of tests, in combination with miri, verifies we handle UB on fishy arguments +/// in the stdlib. Draining and extending the allocation are fairly well-tested earlier, but +/// `vec.insert(usize::MAX, val)` once slipped by! +/// +/// All code that manipulates the collection types should be tested with "trivially wrong" args. +#[test] +fn max_dont_panic() { + let mut v = vec![0]; + let _ = v.get(usize::MAX); + v.shrink_to(usize::MAX); + v.truncate(usize::MAX); +} + +#[test] +#[should_panic] +fn max_insert() { + let mut v = vec![0]; + v.insert(usize::MAX, 1); +} + +#[test] +#[should_panic] +fn max_remove() { + let mut v = vec![0]; + v.remove(usize::MAX); +} + +#[test] +#[should_panic] +fn max_splice() { + let mut v = vec![0]; + v.splice(usize::MAX.., core::iter::once(1)); +} + +#[test] +#[should_panic] +fn max_swap_remove() { + let mut v = vec![0]; + v.swap_remove(usize::MAX); +} diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index a1fff6707bd..8df3ace54ff 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -24,10 +24,7 @@ use crate::ptr; /// use std::alloc::{GlobalAlloc, Layout}; /// use std::cell::UnsafeCell; /// use std::ptr::null_mut; -/// use std::sync::atomic::{ -/// AtomicUsize, -/// Ordering::{Acquire, SeqCst}, -/// }; +/// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; /// /// const ARENA_SIZE: usize = 128 * 1024; /// const MAX_SUPPORTED_ALIGN: usize = 4096; @@ -61,7 +58,7 @@ use crate::ptr; /// let mut allocated = 0; /// if self /// .remaining -/// .fetch_update(SeqCst, SeqCst, |mut remaining| { +/// .fetch_update(Relaxed, Relaxed, |mut remaining| { /// if size > remaining { /// return None; /// } @@ -81,7 +78,7 @@ use crate::ptr; /// /// fn main() { /// let _s = format!("allocating a string!"); -/// let currently = ALLOCATOR.remaining.load(Acquire); +/// let currently = ALLOCATOR.remaining.load(Relaxed); /// println!("allocated so far: {}", ARENA_SIZE - currently); /// } /// ``` diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 70b9e89f9ea..8f612929110 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -4,9 +4,9 @@ use crate::char::TryFromCharError; use crate::convert::TryFrom; use crate::error::Error; use crate::fmt; -use crate::intrinsics::assert_unsafe_precondition; use crate::mem::transmute; use crate::str::FromStr; +use crate::ub_checks::assert_unsafe_precondition; /// Converts a `u32` to a `char`. See [`char::from_u32`]. #[must_use] diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index ffe059bf65c..b27d0db4619 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -4,6 +4,7 @@ //! Hints may be compile time or runtime. use crate::intrinsics; +use crate::ub_checks; /// Informs the compiler that the site which is calling this function is not /// reachable, possibly enabling further optimizations. @@ -98,7 +99,7 @@ use crate::intrinsics; #[rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unreachable_unchecked() -> ! { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "hint::unreachable_unchecked must never be reached", () => false @@ -148,7 +149,7 @@ pub const unsafe fn unreachable_unchecked() -> ! { pub const unsafe fn assert_unchecked(cond: bool) { // SAFETY: The caller promised `cond` is true. unsafe { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "hint::assert_unchecked must never be called when the condition is false", (cond: bool = cond) => cond, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 613d0ab212a..dec31548fc8 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -66,6 +66,8 @@ use crate::marker::DiscriminantKind; use crate::marker::Tuple; use crate::mem::align_of; +use crate::ptr; +use crate::ub_checks; pub mod mir; pub mod simd; @@ -1163,14 +1165,6 @@ extern "rust-intrinsic" { /// may lead to unexpected and unstable compilation results. This makes `transmute` **incredibly /// unsafe**. `transmute` should be the absolute last resort. /// - /// Transmuting pointers *to* integers in a `const` context is [undefined behavior][ub], - /// unless the pointer was originally created *from* an integer. - /// (That includes this function specifically, integer-to-pointer casts, and helpers like [`invalid`][crate::ptr::dangling], - /// but also semantically-equivalent conversions such as punning through `repr(C)` union fields.) - /// Any attempt to use the resulting value for integer operations will abort const-evaluation. - /// (And even outside `const`, such transmutation is touching on many unspecified aspects of the - /// Rust memory model and should be avoided. See below for alternatives.) - /// /// Because `transmute` is a by-value operation, alignment of the *transmuted values /// themselves* is not a concern. As with any other function, the compiler already ensures /// both `Src` and `Dst` are properly aligned. However, when transmuting values that *point @@ -1181,6 +1175,39 @@ extern "rust-intrinsic" { /// /// [ub]: ../../reference/behavior-considered-undefined.html /// + /// # Transmutation between pointers and integers + /// + /// Special care has to be taken when transmuting between pointers and integers, e.g. + /// transmuting between `*const ()` and `usize`. + /// + /// Transmuting *pointers to integers* in a `const` context is [undefined behavior][ub], unless + /// the pointer was originally created *from* an integer. (That includes this function + /// specifically, integer-to-pointer casts, and helpers like [`dangling`][crate::ptr::dangling], + /// but also semantically-equivalent conversions such as punning through `repr(C)` union + /// fields.) Any attempt to use the resulting value for integer operations will abort + /// const-evaluation. (And even outside `const`, such transmutation is touching on many + /// unspecified aspects of the Rust memory model and should be avoided. See below for + /// alternatives.) + /// + /// Transmuting *integers to pointers* is a largely unspecified operation. It is likely *not* + /// equivalent to an `as` cast. Doing non-zero-sized memory accesses with a pointer constructed + /// this way is currently considered undefined behavior. + /// + /// All this also applies when the integer is nested inside an array, tuple, struct, or enum. + /// However, `MaybeUninit<usize>` is not considered an integer type for the purpose of this + /// section. Transmuting `*const ()` to `MaybeUninit<usize>` is fine---but then calling + /// `assume_init()` on that result is considered as completing the pointer-to-integer transmute + /// and thus runs into the issues discussed above. + /// + /// In particular, doing a pointer-to-integer-to-pointer roundtrip via `transmute` is *not* a + /// lossless process. If you want to round-trip a pointer through an integer in a way that you + /// can get back the original pointer, you need to use `as` casts, or replace the integer type + /// by `MaybeUninit<$int>` (and never call `assume_init()`). If you are looking for a way to + /// store data of arbitrary type, also use `MaybeUninit<T>` (that will also handle uninitialized + /// memory due to padding). If you specifically need to store something that is "either an + /// integer or a pointer", use `*mut ()`: integers can be converted to pointers and back without + /// any loss (via `as` casts or via `transmute`). + /// /// # Examples /// /// There are a few things that `transmute` is really useful for. @@ -2638,38 +2665,43 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool { false } -/// Returns whether we should check for library UB. This evaluate to the value of `cfg!(debug_assertions)` -/// during monomorphization. +/// Non-overlapping *typed* swap of a single value. /// -/// This intrinsic is evaluated after monomorphization, and therefore branching on this value can -/// be used to implement debug assertions that are included in the precompiled standard library, -/// but can be optimized out by builds that monomorphize the standard library code with debug -/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`]. +/// The codegen backends will replace this with a better implementation when +/// `T` is a simple type that can be loaded and stored as an immediate. /// -/// We have separate intrinsics for library UB and language UB because checkers like the const-eval -/// interpreter and Miri already implement checks for language UB. Since such checkers do not know -/// about library preconditions, checks guarded by this intrinsic let them find more UB. -#[rustc_const_unstable(feature = "ub_checks", issue = "none")] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[inline(always)] -#[rustc_intrinsic] -pub(crate) const fn check_library_ub() -> bool { - cfg!(debug_assertions) -} - -/// Returns whether we should check for language UB. This evaluate to the value of `cfg!(debug_assertions)` -/// during monomorphization. +/// The stabilized form of this intrinsic is [`crate::mem::swap`]. /// -/// Since checks implemented at the source level must come strictly before the operation that -/// executes UB, if we enabled language UB checks in const-eval/Miri we would miss out on the -/// interpreter's improved diagnostics for the cases that our source-level checks catch. +/// # Safety /// -/// See `check_library_ub` for more information. -#[rustc_const_unstable(feature = "ub_checks", issue = "none")] +/// `x` and `y` are readable and writable as `T`, and non-overlapping. +#[rustc_nounwind] +#[inline] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +// This has fallback `const fn` MIR, so shouldn't need stability, see #122652 +#[rustc_const_unstable(feature = "const_typed_swap", issue = "none")] +pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) { + // SAFETY: The caller provided single non-overlapping items behind + // pointers, so swapping them with `count: 1` is fine. + unsafe { ptr::swap_nonoverlapping(x, y, 1) }; +} + +/// Returns whether we should perform some UB-checking at runtime. This evaluate to the value of +/// `cfg!(debug_assertions)` during monomorphization. +/// +/// This intrinsic is evaluated after monomorphization, which is relevant when mixing crates +/// compiled with and without debug_assertions. The common case here is a user program built with +/// debug_assertions linked against the distributed sysroot which is built without debug_assertions. +/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with +/// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(debug_assertions)` means that +/// assertions are enabled whenever the *user crate* has debug assertions enabled. However if the +/// user has debug assertions disabled, the checks will still get optimized out. This intrinsic is +/// primarily used by [`ub_checks::assert_unsafe_precondition`]. +#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] #[unstable(feature = "core_intrinsics", issue = "none")] #[inline(always)] -#[rustc_intrinsic] -pub(crate) const fn check_language_ub() -> bool { +#[cfg_attr(not(bootstrap), rustc_intrinsic)] // just make it a regular fn in bootstrap +pub(crate) const fn ub_checks() -> bool { cfg!(debug_assertions) } @@ -2733,132 +2765,6 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize { // (`transmute` also falls into this category, but it cannot be wrapped due to the // check that `T` and `U` have the same size.) -/// Check that the preconditions of an unsafe function are followed. The check is enabled at -/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri -/// checks implemented with this macro for language UB are always ignored. -/// -/// This macro should be called as -/// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)` -/// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all -/// those arguments are passed to a function with the body `check_expr`. -/// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB -/// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation -/// of a documented library precondition that does not *immediately* lead to language UB. -/// -/// If `check_library_ub` is used but the check is actually guarding language UB, the check will -/// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice -/// diagnostic, but our ability to detect UB is unchanged. -/// But if `check_language_ub` is used when the check is actually for library UB, the check is -/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the -/// library UB, the backtrace Miri reports may be far removed from original cause. -/// -/// These checks are behind a condition which is evaluated at codegen time, not expansion time like -/// [`debug_assert`]. This means that a standard library built with optimizations and debug -/// assertions disabled will have these checks optimized out of its monomorphizations, but if a -/// caller of the standard library has debug assertions enabled and monomorphizes an expansion of -/// this macro, that monomorphization will contain the check. -/// -/// Since these checks cannot be optimized out in MIR, some care must be taken in both call and -/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to -/// this structure: -/// ```ignore (pseudocode) -/// if ::core::intrinsics::check_language_ub() { -/// precondition_check(args) -/// } -/// ``` -/// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and -/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is -/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in -/// MIR, but *can* be inlined and fully optimized by a codegen backend. -/// -/// Callers should avoid introducing any other `let` bindings or any code outside this macro in -/// order to call it. Since the precompiled standard library is built with full debuginfo and these -/// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough -/// debuginfo to have a measurable compile-time impact on debug builds. -#[allow_internal_unstable(ub_checks)] // permit this to be called in stably-const fn -macro_rules! assert_unsafe_precondition { - ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => { - { - // This check is inlineable, but not by the MIR inliner. - // The reason for this is that the MIR inliner is in an exceptionally bad position - // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`, - // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and - // would be bad for compile times. - // - // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without - // inlining the check. If it's `true`, it can inline it and get significantly better performance. - #[rustc_no_mir_inline] - #[inline] - #[rustc_nounwind] - #[rustc_const_unstable(feature = "ub_checks", issue = "none")] - const fn precondition_check($($name:$ty),*) { - if !$e { - ::core::panicking::panic_nounwind( - concat!("unsafe precondition(s) violated: ", $message) - ); - } - } - - if ::core::intrinsics::$kind() { - precondition_check($($arg,)*); - } - } - }; -} -pub(crate) use assert_unsafe_precondition; - -/// Checks whether `ptr` is properly aligned with respect to -/// `align_of::<T>()`. -/// -/// In `const` this is approximate and can fail spuriously. It is primarily intended -/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the -/// check is anyway not executed in `const`. -#[inline] -pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { - !ptr.is_null() && ptr.is_aligned_to(align) -} - -#[inline] -pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool { - let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size }; - len <= max_len -} - -/// Checks whether the regions of memory starting at `src` and `dst` of size -/// `count * size` do *not* overlap. -/// -/// Note that in const-eval this function just returns `true` and therefore must -/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`. -#[inline] -pub(crate) const fn is_nonoverlapping( - src: *const (), - dst: *const (), - size: usize, - count: usize, -) -> bool { - #[inline] - fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool { - let src_usize = src.addr(); - let dst_usize = dst.addr(); - let Some(size) = size.checked_mul(count) else { - crate::panicking::panic_nounwind( - "is_nonoverlapping: `size_of::<T>() * count` overflows a usize", - ) - }; - let diff = src_usize.abs_diff(dst_usize); - // If the absolute distance between the ptrs is at least as big as the size of the buffer, - // they do not overlap. - diff >= size - } - - #[inline] - const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool { - true - } - - const_eval_select((src, dst, size, count), comptime, runtime) -} - /// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source /// and destination must *not* overlap. /// @@ -2957,7 +2863,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize); } - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", @@ -2968,9 +2874,9 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us align: usize = align_of::<T>(), count: usize = count, ) => - is_aligned_and_not_null(src, align) - && is_aligned_and_not_null(dst, align) - && is_nonoverlapping(src, dst, size, count) + ub_checks::is_aligned_and_not_null(src, align) + && ub_checks::is_aligned_and_not_null(dst, align) + && ub_checks::is_nonoverlapping(src, dst, size, count) ); // SAFETY: the safety contract for `copy_nonoverlapping` must be @@ -3061,7 +2967,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) { // SAFETY: the safety contract for `copy` must be upheld by the caller. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", @@ -3070,8 +2976,8 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) { dst: *mut () = dst as *mut (), align: usize = align_of::<T>(), ) => - is_aligned_and_not_null(src, align) - && is_aligned_and_not_null(dst, align) + ub_checks::is_aligned_and_not_null(src, align) + && ub_checks::is_aligned_and_not_null(dst, align) ); copy(src, dst, count) } @@ -3142,13 +3048,13 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) { // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::write_bytes requires that the destination pointer is aligned and non-null", ( addr: *const () = dst as *const (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); write_bytes(dst, val, count) } diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index b69f4f853b9..427a95f4665 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -470,7 +470,7 @@ extern "rust-intrinsic" { /// No matter whether the output is an array or an unsigned integer, it is treated as a single /// contiguous list of bits. The bitmask is always packed on the least-significant side of the /// output, and padded with 0s in the most-significant bits. The order of the bits depends on - /// endianess: + /// endianness: /// /// * On little endian, the least significant bit corresponds to the first vector element. /// * On big endian, the least significant bit corresponds to the last vector element. diff --git a/library/core/src/iter/traits/marker.rs b/library/core/src/iter/traits/marker.rs index 8bdbca120d7..ad4d63d83b5 100644 --- a/library/core/src/iter/traits/marker.rs +++ b/library/core/src/iter/traits/marker.rs @@ -28,6 +28,7 @@ pub unsafe trait TrustedFused {} #[rustc_unsafe_specialization_marker] // FIXME: this should be a #[marker] and have another blanket impl for T: TrustedFused // but that ICEs iter::Fuse specializations. +#[cfg_attr(not(bootstrap), lang = "fused_iterator")] pub trait FusedIterator: Iterator {} #[stable(feature = "fused", since = "1.26.0")] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 2718dd11473..f0448a98981 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -170,6 +170,8 @@ #![feature(const_try)] #![feature(const_type_id)] #![feature(const_type_name)] +#![feature(const_typed_swap)] +#![feature(const_ub_checks)] #![feature(const_unicode_case_lookup)] #![feature(const_unsafecell_get_mut)] #![feature(const_waker)] @@ -189,7 +191,6 @@ #![feature(ptr_metadata)] #![feature(set_ptr_value)] #![feature(slice_ptr_get)] -#![feature(slice_split_at_unchecked)] #![feature(split_at_checked)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] @@ -366,6 +367,7 @@ pub mod hint; pub mod intrinsics; pub mod mem; pub mod ptr; +mod ub_checks; /* Core language traits */ diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 0ee7e190e3d..a78842c8f8d 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1704,14 +1704,26 @@ pub(crate) mod builtin { } /// Unstable placeholder for type ascription. - #[rustc_builtin_macro] + #[allow_internal_unstable(builtin_syntax)] #[unstable( feature = "type_ascription", issue = "23416", reason = "placeholder syntax for type ascription" )] pub macro type_ascribe($expr:expr, $ty:ty) { - /* compiler built-in */ + builtin # type_ascribe($expr, $ty) + } + + #[cfg(not(bootstrap))] + /// Unstable placeholder for deref patterns. + #[allow_internal_unstable(builtin_syntax)] + #[unstable( + feature = "deref_patterns", + issue = "87121", + reason = "placeholder syntax for deref patterns" + )] + pub macro deref($pat:pat) { + builtin # deref($pat) } /// Unstable implementation detail of the `rustc` compiler, do not use. diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index d1dc6720271..75d42edbaa0 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -726,63 +726,9 @@ pub unsafe fn uninitialized<T>() -> T { #[rustc_const_unstable(feature = "const_swap", issue = "83163")] #[rustc_diagnostic_item = "mem_swap"] pub const fn swap<T>(x: &mut T, y: &mut T) { - // NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary - // reinterpretation of values as (chunkable) byte arrays, and the loop in the - // block optimization in `swap_slice` is hard to rewrite back - // into the (unoptimized) direct swapping implementation, so we disable it. - #[cfg(not(any(target_arch = "spirv")))] - { - // For types that are larger multiples of their alignment, the simple way - // tends to copy the whole thing to stack rather than doing it one part - // at a time, so instead treat them as one-element slices and piggy-back - // the slice optimizations that will split up the swaps. - if const { size_of::<T>() / align_of::<T>() > 2 } { - // SAFETY: exclusive references always point to one non-overlapping - // element and are non-null and properly aligned. - return unsafe { ptr::swap_nonoverlapping(x, y, 1) }; - } - } - - // If a scalar consists of just a small number of alignment units, let - // the codegen just swap those pieces directly, as it's likely just a - // few instructions and anything else is probably overcomplicated. - // - // Most importantly, this covers primitives and simd types that tend to - // have size=align where doing anything else can be a pessimization. - // (This will also be used for ZSTs, though any solution works for them.) - swap_simple(x, y); -} - -/// Same as [`swap`] semantically, but always uses the simple implementation. -/// -/// Used elsewhere in `mem` and `ptr` at the bottom layer of calls. -#[rustc_const_unstable(feature = "const_swap", issue = "83163")] -#[inline] -pub(crate) const fn swap_simple<T>(x: &mut T, y: &mut T) { - // We arrange for this to typically be called with small types, - // so this reads-and-writes approach is actually better than using - // copy_nonoverlapping as it easily puts things in LLVM registers - // directly and doesn't end up inlining allocas. - // And LLVM actually optimizes it to 3×memcpy if called with - // a type larger than it's willing to keep in a register. - // Having typed reads and writes in MIR here is also good as - // it lets Miri and CTFE understand them better, including things - // like enforcing type validity for them. - // Importantly, read+copy_nonoverlapping+write introduces confusing - // asymmetry to the behaviour where one value went through read+write - // whereas the other was copied over by the intrinsic (see #94371). - // Furthermore, using only read+write here benefits limited backends - // such as SPIR-V that work on an underlying *typed* view of memory, - // and thus have trouble with Rust's untyped memory operations. - - // SAFETY: exclusive references are always valid to read/write, - // including being aligned, and nothing here panics so it's drop-safe. - unsafe { - let a = ptr::read(x); - let b = ptr::read(y); - ptr::write(x, b); - ptr::write(y, a); - } + // SAFETY: `&mut` guarantees these are typed readable and writable + // as well as non-overlapping. + unsafe { intrinsics::typed_swap(x, y) } } /// Replaces `dest` with the default value of `T`, returning the previous `dest` value. diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index a8f637280df..c65ffbb98f2 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -9,6 +9,7 @@ use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign}; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::ptr; use crate::str::FromStr; +use crate::ub_checks; use super::from_str_radix; use super::{IntErrorKind, ParseIntError}; @@ -369,7 +370,7 @@ where None => { // SAFETY: The caller guarantees that `n` is non-zero, so this is unreachable. unsafe { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "NonZero::new_unchecked requires the argument to be non-zero", () => false, @@ -409,7 +410,7 @@ where None => { // SAFETY: The caller guarantees that `n` references a value that is non-zero, so this is unreachable. unsafe { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_library_ub, "NonZero::from_mut_unchecked requires the argument to dereference as non-zero", () => false, diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index b28d88fa5bf..65bda9177c7 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -1,6 +1,7 @@ -use crate::intrinsics::{assert_unsafe_precondition, unchecked_add, unchecked_sub}; +use crate::intrinsics::{unchecked_add, unchecked_sub}; use crate::iter::{FusedIterator, TrustedLen}; use crate::num::NonZero; +use crate::ub_checks; /// Like a `Range<usize>`, but with a safety invariant that `start <= end`. /// @@ -19,7 +20,7 @@ impl IndexRange { /// - `start <= end` #[inline] pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_library_ub, "IndexRange::new_unchecked requires `start <= end`", (start: usize = start, end: usize = end) => start <= end, diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 5027e326a9d..0083d15efae 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -558,17 +558,16 @@ use crate::panicking::{panic, panic_str}; use crate::pin::Pin; use crate::{ cmp, convert, hint, mem, - num::NonZero, ops::{self, ControlFlow, Deref, DerefMut}, slice, }; /// The `Option` type. See [the module level documentation](self) for more. -#[derive(Copy, PartialOrd, Eq, Ord, Debug, Hash)] +#[derive(Copy, Eq, Debug, Hash)] #[rustc_diagnostic_item = "Option"] #[lang = "Option"] #[stable(feature = "rust1", since = "1.0.0")] -#[allow(clippy::derived_hash_with_manual_eq)] // PartialEq is specialized +#[allow(clippy::derived_hash_with_manual_eq)] // PartialEq is manually implemented equivalently pub enum Option<T> { /// No value. #[lang = "None"] @@ -2146,83 +2145,52 @@ impl<'a, T> From<&'a mut Option<T>> for Option<&'a mut T> { } } +// Ideally, LLVM should be able to optimize our derive code to this. +// Once https://github.com/llvm/llvm-project/issues/52622 is fixed, we can +// go back to deriving `PartialEq`. #[stable(feature = "rust1", since = "1.0.0")] impl<T> crate::marker::StructuralPartialEq for Option<T> {} #[stable(feature = "rust1", since = "1.0.0")] impl<T: PartialEq> PartialEq for Option<T> { #[inline] fn eq(&self, other: &Self) -> bool { - SpecOptionPartialEq::eq(self, other) - } -} - -/// This specialization trait is a workaround for LLVM not currently (2023-01) -/// being able to optimize this itself, even though Alive confirms that it would -/// be legal to do so: <https://github.com/llvm/llvm-project/issues/52622> -/// -/// Once that's fixed, `Option` should go back to deriving `PartialEq`, as -/// it used to do before <https://github.com/rust-lang/rust/pull/103556>. -#[unstable(feature = "spec_option_partial_eq", issue = "none", reason = "exposed only for rustc")] -#[doc(hidden)] -pub trait SpecOptionPartialEq: Sized { - fn eq(l: &Option<Self>, other: &Option<Self>) -> bool; -} - -#[unstable(feature = "spec_option_partial_eq", issue = "none", reason = "exposed only for rustc")] -impl<T: PartialEq> SpecOptionPartialEq for T { - #[inline] - default fn eq(l: &Option<T>, r: &Option<T>) -> bool { - match (l, r) { + // Spelling out the cases explicitly optimizes better than + // `_ => false` + match (self, other) { (Some(l), Some(r)) => *l == *r, + (Some(_), None) => false, + (None, Some(_)) => false, (None, None) => true, - _ => false, } } } -macro_rules! non_zero_option { - ( $( #[$stability: meta] $NZ:ty; )+ ) => { - $( - #[$stability] - impl SpecOptionPartialEq for $NZ { - #[inline] - fn eq(l: &Option<Self>, r: &Option<Self>) -> bool { - l.map(Self::get).unwrap_or(0) == r.map(Self::get).unwrap_or(0) - } - } - )+ - }; -} - -non_zero_option! { - #[stable(feature = "nonzero", since = "1.28.0")] NonZero<u8>; - #[stable(feature = "nonzero", since = "1.28.0")] NonZero<u16>; - #[stable(feature = "nonzero", since = "1.28.0")] NonZero<u32>; - #[stable(feature = "nonzero", since = "1.28.0")] NonZero<u64>; - #[stable(feature = "nonzero", since = "1.28.0")] NonZero<u128>; - #[stable(feature = "nonzero", since = "1.28.0")] NonZero<usize>; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<i8>; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<i16>; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<i32>; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<i64>; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<i128>; - #[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<isize>; -} - -#[stable(feature = "nonnull", since = "1.25.0")] -impl<T> SpecOptionPartialEq for crate::ptr::NonNull<T> { +// Manually implementing here somewhat improves codegen for +// https://github.com/rust-lang/rust/issues/49892, although still +// not optimal. +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: PartialOrd> PartialOrd for Option<T> { #[inline] - fn eq(l: &Option<Self>, r: &Option<Self>) -> bool { - l.map(Self::as_ptr).unwrap_or_else(|| crate::ptr::null_mut()) - == r.map(Self::as_ptr).unwrap_or_else(|| crate::ptr::null_mut()) + fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { + match (self, other) { + (Some(l), Some(r)) => l.partial_cmp(r), + (Some(_), None) => Some(cmp::Ordering::Greater), + (None, Some(_)) => Some(cmp::Ordering::Less), + (None, None) => Some(cmp::Ordering::Equal), + } } } #[stable(feature = "rust1", since = "1.0.0")] -impl SpecOptionPartialEq for cmp::Ordering { +impl<T: Ord> Ord for Option<T> { #[inline] - fn eq(l: &Option<Self>, r: &Option<Self>) -> bool { - l.map_or(2, |x| x as i8) == r.map_or(2, |x| x as i8) + fn cmp(&self, other: &Self) -> cmp::Ordering { + match (self, other) { + (Some(l), Some(r)) => l.cmp(r), + (Some(_), None) => cmp::Ordering::Greater, + (None, Some(_)) => cmp::Ordering::Less, + (None, None) => cmp::Ordering::Equal, + } } } diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index c77e9675a6a..40326221258 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -161,11 +161,12 @@ impl fmt::Display for PanicInfo<'_> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("panicked at ")?; self.location.fmt(formatter)?; + formatter.write_str(":")?; if let Some(message) = self.message { - formatter.write_str(":\n")?; + formatter.write_str("\n")?; formatter.write_fmt(*message)?; } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() { - formatter.write_str(":\n")?; + formatter.write_str("\n")?; formatter.write_str(payload)?; } // NOTE: we cannot use downcast_ref::<String>() here diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 9e8dac88816..a8940d9cd1e 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -132,11 +132,11 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo #[rustc_const_unstable(feature = "panic_internals", issue = "none")] #[lang = "panic"] // needed by codegen for panic on overflow and other `Assert` MIR terminators pub const fn panic(expr: &'static str) -> ! { - // Use Arguments::new_v1 instead of format_args!("{expr}") to potentially + // Use Arguments::new_const instead of format_args!("{expr}") to potentially // reduce size overhead. The format_args! macro uses str's Display trait to // write expr, which calls Formatter::pad, which must accommodate string // truncation and padding (even though none is used here). Using - // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the + // Arguments::new_const may allow the compiler to omit Formatter::pad from the // output binary, saving up to a few kilobytes. panic_fmt(fmt::Arguments::new_const(&[expr])); } diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index a0227d9130b..d14cac9afb5 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -144,7 +144,7 @@ //! * e.g. [`drop`]ping the [`Future`] [^pin-drop-future] //! //! There are two possible ways to ensure the invariants required for 2. and 3. above (which -//! apply to any address-sensitive type, not just self-referrential types) do not get broken. +//! apply to any address-sensitive type, not just self-referential types) do not get broken. //! //! 1. Have the value detect when it is moved and update all the pointers that point to itself. //! 2. Guarantee that the address of the value does not change (and that memory is not re-used @@ -170,7 +170,7 @@ //! become viral throughout all code that interacts with the object. //! //! The second option is a viable solution to the problem for some use cases, in particular -//! for self-referrential types. Under this model, any type that has an address sensitive state +//! for self-referential types. Under this model, any type that has an address sensitive state //! would ultimately store its data in something like a [`Box<T>`], carefully manage internal //! access to that data to ensure no *moves* or other invalidation occurs, and finally //! provide a safe interface on top. @@ -186,8 +186,8 @@ //! //! Although there were other reason as well, this issue of expensive composition is the key thing //! that drove Rust towards adopting a different model. It is particularly a problem -//! when one considers, for exapmle, the implications of composing together the [`Future`]s which -//! will eventaully make up an asynchronous task (including address-sensitive `async fn` state +//! when one considers, for example, the implications of composing together the [`Future`]s which +//! will eventually make up an asynchronous task (including address-sensitive `async fn` state //! machines). It is plausible that there could be many layers of [`Future`]s composed together, //! including multiple layers of `async fn`s handling different parts of a task. It was deemed //! unacceptable to force indirection and allocation for each layer of composition in this case. @@ -359,7 +359,7 @@ //! Builtin types that are [`Unpin`] include all of the primitive types, like [`bool`], [`i32`], //! and [`f32`], references (<code>[&]T</code> and <code>[&mut] T</code>), etc., as well as many //! core and standard library types like [`Box<T>`], [`String`], and more. -//! These types are marked [`Unpin`] because they do not have an ddress-sensitive state like the +//! These types are marked [`Unpin`] because they do not have an address-sensitive state like the //! ones we discussed above. If they did have such a state, those parts of their interface would be //! unsound without being expressed through pinning, and they would then need to not //! implement [`Unpin`]. @@ -953,7 +953,7 @@ use crate::{ /// discussed below. /// /// We call such a [`Pin`]-wrapped pointer a **pinning pointer** (or pinning ref, or pinning -/// [`Box`], etc.) because its existince is the thing that is pinning the underlying pointee in +/// [`Box`], etc.) because its existence is the thing that is pinning the underlying pointee in /// place: it is the metaphorical "pin" securing the data in place on the pinboard (in memory). /// /// It is important to stress that the thing in the [`Pin`] is not the value which we want to pin @@ -962,7 +962,7 @@ use crate::{ /// /// The most common set of types which require pinning related guarantees for soundness are the /// compiler-generated state machines that implement [`Future`] for the return value of -/// `async fn`s. These compiler-generated [`Future`]s may contain self-referrential pointers, one +/// `async fn`s. These compiler-generated [`Future`]s may contain self-referential pointers, one /// of the most common use cases for [`Pin`]. More details on this point are provided in the /// [`pin` module] docs, but suffice it to say they require the guarantees provided by pinning to /// be implemented soundly. diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index 10525a16f3a..29f73bb4942 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -103,3 +103,11 @@ pub use crate::macros::builtin::cfg_eval; reason = "placeholder syntax for type ascription" )] pub use crate::macros::builtin::type_ascribe; + +#[cfg(not(bootstrap))] +#[unstable( + feature = "deref_patterns", + issue = "87121", + reason = "placeholder syntax for deref patterns" +)] +pub use crate::macros::builtin::deref; diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 8f44b7eb7c2..bc84fb5ccb0 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -1,7 +1,7 @@ use crate::convert::{TryFrom, TryInto}; -#[cfg(debug_assertions)] -use crate::intrinsics::assert_unsafe_precondition; use crate::num::NonZero; +#[cfg(debug_assertions)] +use crate::ub_checks::assert_unsafe_precondition; use crate::{cmp, fmt, hash, mem, num}; /// A type storing a `usize` which is a power of two, and thus diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 69c61602073..a6c00ff28d4 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -818,7 +818,7 @@ impl<T: ?Sized> *const T { intrinsics::const_eval_select((this, origin), comptime, runtime) } - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::sub_ptr requires `self >= origin`", ( diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 1f0204daf72..56378b437e7 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -388,10 +388,9 @@ use crate::cmp::Ordering; use crate::fmt; use crate::hash; -use crate::intrinsics::{ - self, assert_unsafe_precondition, is_aligned_and_not_null, is_nonoverlapping, -}; +use crate::intrinsics; use crate::marker::FnPtr; +use crate::ub_checks; use crate::mem::{self, align_of, size_of, MaybeUninit}; @@ -1019,7 +1018,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) { }; } - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", @@ -1030,9 +1029,9 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) { align: usize = align_of::<T>(), count: usize = count, ) => - is_aligned_and_not_null(x, align) - && is_aligned_and_not_null(y, align) - && is_nonoverlapping(x, y, size, count) + ub_checks::is_aligned_and_not_null(x, align) + && ub_checks::is_aligned_and_not_null(y, align) + && ub_checks::is_nonoverlapping(x, y, size, count) ); // Split up the slice into small power-of-two-sized chunks that LLVM is able @@ -1062,11 +1061,26 @@ const unsafe fn swap_nonoverlapping_simple_untyped<T>(x: *mut T, y: *mut T, coun let mut i = 0; while i < count { // SAFETY: By precondition, `i` is in-bounds because it's below `n` - let x = unsafe { &mut *x.add(i) }; + let x = unsafe { x.add(i) }; // SAFETY: By precondition, `i` is in-bounds because it's below `n` // and it's distinct from `x` since the ranges are non-overlapping - let y = unsafe { &mut *y.add(i) }; - mem::swap_simple::<MaybeUninit<T>>(x, y); + let y = unsafe { y.add(i) }; + + // If we end up here, it's because we're using a simple type -- like + // a small power-of-two-sized thing -- or a special type with particularly + // large alignment, particularly SIMD types. + // Thus we're fine just reading-and-writing it, as either it's small + // and that works well anyway or it's special and the type's author + // presumably wanted things to be done in the larger chunk. + + // SAFETY: we're only ever given pointers that are valid to read/write, + // including being aligned, and nothing here panics so it's drop-safe. + unsafe { + let a: MaybeUninit<T> = read(x); + let b: MaybeUninit<T> = read(y); + write(x, b); + write(y, a); + } i += 1; } @@ -1120,13 +1134,13 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T { // and cannot overlap `src` since `dst` must point to a distinct // allocated object. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::replace requires that the pointer argument is aligned and non-null", ( addr: *const () = dst as *const (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); mem::replace(&mut *dst, src) } @@ -1272,13 +1286,13 @@ pub const unsafe fn read<T>(src: *const T) -> T { // SAFETY: the caller must guarantee that `src` is valid for reads. unsafe { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::read requires that the pointer argument is aligned and non-null", ( addr: *const () = src as *const (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); crate::intrinsics::read_via_copy(src) } @@ -1481,13 +1495,13 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) { // to `dst` while `src` is owned by this function. unsafe { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::write requires that the pointer argument is aligned and non-null", ( addr: *mut () = dst as *mut (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); intrinsics::write_via_move(dst, src) } @@ -1653,13 +1667,13 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) { pub unsafe fn read_volatile<T>(src: *const T) -> T { // SAFETY: the caller must uphold the safety contract for `volatile_load`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::read_volatile requires that the pointer argument is aligned and non-null", ( addr: *const () = src as *const (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); intrinsics::volatile_load(src) } @@ -1732,13 +1746,13 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T { pub unsafe fn write_volatile<T>(dst: *mut T, src: T) { // SAFETY: the caller must uphold the safety contract for `volatile_store`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::write_volatile requires that the pointer argument is aligned and non-null", ( addr: *mut () = dst as *mut (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); intrinsics::volatile_store(dst, src); } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 9c0236c172a..e9488917acc 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -2,7 +2,6 @@ use crate::cmp::Ordering; use crate::fmt; use crate::hash; use crate::intrinsics; -use crate::intrinsics::assert_unsafe_precondition; use crate::marker::Unsize; use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; @@ -10,6 +9,7 @@ use crate::ops::{CoerceUnsized, DispatchFromDyn}; use crate::ptr; use crate::ptr::Unique; use crate::slice::{self, SliceIndex}; +use crate::ub_checks::assert_unsafe_precondition; /// `*mut T` but non-zero and [covariant]. /// @@ -1575,6 +1575,25 @@ impl<T> NonNull<[T]> { self.as_ptr().len() } + /// Returns `true` if the non-null raw slice has a length of 0. + /// + /// # Examples + /// + /// ```rust + /// #![feature(slice_ptr_is_empty_nonnull)] + /// use std::ptr::NonNull; + /// + /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3); + /// assert!(!slice.is_empty()); + /// ``` + #[unstable(feature = "slice_ptr_is_empty_nonnull", issue = "71146")] + #[rustc_const_unstable(feature = "const_slice_ptr_is_empty_nonnull", issue = "71146")] + #[must_use] + #[inline] + pub const fn is_empty(self) -> bool { + self.len() == 0 + } + /// Returns a non-null pointer to the slice's buffer. /// /// # Examples diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 210118817ab..127a407dae5 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,10 +1,10 @@ //! Indexing implementations for `[T]`. -use crate::intrinsics::assert_unsafe_precondition; use crate::intrinsics::const_eval_select; use crate::intrinsics::unchecked_sub; use crate::ops; use crate::ptr; +use crate::ub_checks::assert_unsafe_precondition; #[stable(feature = "rust1", since = "1.0.0")] impl<T, I> ops::Index<I> for [T] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 4a574bf0347..a16005abf46 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -9,7 +9,6 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::fmt; use crate::hint; -use crate::intrinsics::assert_unsafe_precondition; use crate::intrinsics::exact_div; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; @@ -17,6 +16,7 @@ use crate::ops::{Bound, OneSidedRange, Range, RangeBounds}; use crate::ptr; use crate::simd::{self, Simd}; use crate::slice; +use crate::ub_checks::assert_unsafe_precondition; #[unstable( feature = "slice_internals", @@ -1944,8 +1944,6 @@ impl<T> [T] { /// # Examples /// /// ``` - /// #![feature(slice_split_at_unchecked)] - /// /// let v = [1, 2, 3, 4, 5, 6]; /// /// unsafe { @@ -1966,7 +1964,7 @@ impl<T> [T] { /// assert_eq!(right, []); /// } /// ``` - #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] + #[stable(feature = "slice_split_at_unchecked", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "const_slice_split_at_unchecked", since = "1.77.0")] #[inline] #[must_use] @@ -2008,8 +2006,6 @@ impl<T> [T] { /// # Examples /// /// ``` - /// #![feature(slice_split_at_unchecked)] - /// /// let mut v = [1, 0, 3, 0, 5, 6]; /// // scoped to restrict the lifetime of the borrows /// unsafe { @@ -2021,7 +2017,7 @@ impl<T> [T] { /// } /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); /// ``` - #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] + #[stable(feature = "slice_split_at_unchecked", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")] #[inline] #[must_use] diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 2199614ce27..29a12f106c5 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -1,12 +1,10 @@ //! Free functions to create `&[T]` and `&mut [T]`. use crate::array; -use crate::intrinsics::{ - assert_unsafe_precondition, is_aligned_and_not_null, is_valid_allocation_size, -}; use crate::mem::{align_of, size_of}; use crate::ops::Range; use crate::ptr; +use crate::ub_checks; /// Forms a slice from a pointer and a length. /// @@ -95,7 +93,7 @@ use crate::ptr; pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", ( @@ -104,8 +102,8 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] align: usize = align_of::<T>(), len: usize = len, ) => - is_aligned_and_not_null(data, align) - && is_valid_allocation_size(size, len) + ub_checks::is_aligned_and_not_null(data, align) + && ub_checks::is_valid_allocation_size(size, len) ); &*ptr::slice_from_raw_parts(data, len) } @@ -149,7 +147,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", ( @@ -158,8 +156,8 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m align: usize = align_of::<T>(), len: usize = len, ) => - is_aligned_and_not_null(data, align) - && is_valid_allocation_size(size, len) + ub_checks::is_aligned_and_not_null(data, align) + && ub_checks::is_valid_allocation_size(size, len) ); &mut *ptr::slice_from_raw_parts_mut(data, len) } diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index ec81fd095d5..672af752149 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -1,10 +1,10 @@ //! Trait implementations for `str`. use crate::cmp::Ordering; -use crate::intrinsics::assert_unsafe_precondition; use crate::ops; use crate::ptr; use crate::slice::SliceIndex; +use crate::ub_checks::assert_unsafe_precondition; use super::ParseBoolError; diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs new file mode 100644 index 00000000000..ff6b2d30539 --- /dev/null +++ b/library/core/src/ub_checks.rs @@ -0,0 +1,158 @@ +//! Provides the [`assert_unsafe_precondition`] macro as well as some utility functions that cover +//! common preconditions. + +use crate::intrinsics::{self, const_eval_select}; + +/// Check that the preconditions of an unsafe function are followed. The check is enabled at +/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri +/// checks implemented with this macro for language UB are always ignored. +/// +/// This macro should be called as +/// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)` +/// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all +/// those arguments are passed to a function with the body `check_expr`. +/// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB +/// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation +/// of a documented library precondition that does not *immediately* lead to language UB. +/// +/// If `check_library_ub` is used but the check is actually guarding language UB, the check will +/// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice +/// diagnostic, but our ability to detect UB is unchanged. +/// But if `check_language_ub` is used when the check is actually for library UB, the check is +/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the +/// library UB, the backtrace Miri reports may be far removed from original cause. +/// +/// These checks are behind a condition which is evaluated at codegen time, not expansion time like +/// [`debug_assert`]. This means that a standard library built with optimizations and debug +/// assertions disabled will have these checks optimized out of its monomorphizations, but if a +/// caller of the standard library has debug assertions enabled and monomorphizes an expansion of +/// this macro, that monomorphization will contain the check. +/// +/// Since these checks cannot be optimized out in MIR, some care must be taken in both call and +/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to +/// this structure: +/// ```ignore (pseudocode) +/// if ::core::intrinsics::check_language_ub() { +/// precondition_check(args) +/// } +/// ``` +/// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and +/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is +/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in +/// MIR, but *can* be inlined and fully optimized by a codegen backend. +/// +/// Callers should avoid introducing any other `let` bindings or any code outside this macro in +/// order to call it. Since the precompiled standard library is built with full debuginfo and these +/// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough +/// debuginfo to have a measurable compile-time impact on debug builds. +#[allow_internal_unstable(const_ub_checks)] // permit this to be called in stably-const fn +macro_rules! assert_unsafe_precondition { + ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => { + { + // This check is inlineable, but not by the MIR inliner. + // The reason for this is that the MIR inliner is in an exceptionally bad position + // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`, + // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and + // would be bad for compile times. + // + // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without + // inlining the check. If it's `true`, it can inline it and get significantly better performance. + #[rustc_no_mir_inline] + #[inline] + #[rustc_nounwind] + #[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] + const fn precondition_check($($name:$ty),*) { + if !$e { + ::core::panicking::panic_nounwind( + concat!("unsafe precondition(s) violated: ", $message) + ); + } + } + + if ::core::ub_checks::$kind() { + precondition_check($($arg,)*); + } + } + }; +} +pub(crate) use assert_unsafe_precondition; + +/// Checking library UB is always enabled when UB-checking is done +/// (and we use a reexport so that there is no unnecessary wrapper function). +pub(crate) use intrinsics::ub_checks as check_library_ub; + +/// Determines whether we should check for language UB. +/// +/// The intention is to not do that when running in the interpreter, as that one has its own +/// language UB checks which generally produce better errors. +#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] +#[inline] +pub(crate) const fn check_language_ub() -> bool { + #[inline] + fn runtime() -> bool { + // Disable UB checks in Miri. + !cfg!(miri) + } + + #[inline] + const fn comptime() -> bool { + // Always disable UB checks. + false + } + + // Only used for UB checks so we may const_eval_select. + intrinsics::ub_checks() && const_eval_select((), comptime, runtime) +} + +/// Checks whether `ptr` is properly aligned with respect to +/// `align_of::<T>()`. +/// +/// In `const` this is approximate and can fail spuriously. It is primarily intended +/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the +/// check is anyway not executed in `const`. +#[inline] +pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { + !ptr.is_null() && ptr.is_aligned_to(align) +} + +#[inline] +pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool { + let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size }; + len <= max_len +} + +/// Checks whether the regions of memory starting at `src` and `dst` of size +/// `count * size` do *not* overlap. +/// +/// Note that in const-eval this function just returns `true` and therefore must +/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`. +#[inline] +pub(crate) const fn is_nonoverlapping( + src: *const (), + dst: *const (), + size: usize, + count: usize, +) -> bool { + #[inline] + fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool { + let src_usize = src.addr(); + let dst_usize = dst.addr(); + let Some(size) = size.checked_mul(count) else { + crate::panicking::panic_nounwind( + "is_nonoverlapping: `size_of::<T>() * count` overflows a usize", + ) + }; + let diff = src_usize.abs_diff(dst_usize); + // If the absolute distance between the ptrs is at least as big as the size of the buffer, + // they do not overlap. + diff >= size + } + + #[inline] + const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool { + true + } + + // This is just for safety checks so we can const_eval_select. + const_eval_select((src, dst, size, count), comptime, runtime) +} diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs index af18e19337c..fed4c52e83c 100644 --- a/library/panic_unwind/src/emcc.rs +++ b/library/panic_unwind/src/emcc.rs @@ -84,7 +84,7 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> { super::__rust_foreign_exception(); } - let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst); + let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::Relaxed); if was_caught { // Since cleanup() isn't allowed to panic, we just abort instead. intrinsics::abort(); diff --git a/library/portable-simd/.github/workflows/ci.yml b/library/portable-simd/.github/workflows/ci.yml index 90543044ea8..b292be2d6f9 100644 --- a/library/portable-simd/.github/workflows/ci.yml +++ b/library/portable-simd/.github/workflows/ci.yml @@ -141,6 +141,11 @@ jobs: - name: Test (release) run: cargo test --verbose --target=${{ matrix.target }} --release + - name: Generate docs + run: cargo doc --verbose --target=${{ matrix.target }} + env: + RUSTDOCFLAGS: -Dwarnings + wasm-tests: name: "wasm (firefox, ${{ matrix.name }})" runs-on: ubuntu-latest diff --git a/library/portable-simd/Cargo.lock b/library/portable-simd/Cargo.lock index 46312c09657..1584c704fb2 100644 --- a/library/portable-simd/Cargo.lock +++ b/library/portable-simd/Cargo.lock @@ -177,6 +177,9 @@ name = "std_float" version = "0.1.0" dependencies = [ "core_simd", + "test_helpers", + "wasm-bindgen", + "wasm-bindgen-test", ] [[package]] diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index a25723e11ce..7a161b7e01d 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -13,11 +13,12 @@ simd_ffi, staged_api, strict_provenance, + prelude_import, ptr_metadata )] #![cfg_attr( all( - any(target_arch = "aarch64", target_arch = "arm",), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm",), any( all(target_feature = "v6", not(target_feature = "mclass")), all(target_feature = "mclass", target_feature = "dsp"), @@ -33,12 +34,21 @@ any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) )] +#![cfg_attr( + all(target_arch = "x86_64", target_feature = "avx512f"), + feature(stdarch_x86_avx512) +)] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] +#![doc(test(attr(deny(warnings))))] #![allow(internal_features)] #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. +#[prelude_import] +#[allow(unused_imports)] +use core::prelude::v1::*; + #[path = "mod.rs"] mod core_simd; pub use self::core_simd::simd; diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index e480c25a51e..e6e27c76a5e 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -34,6 +34,7 @@ mod sealed { fn eq(self, other: Self) -> bool; fn to_usize(self) -> usize; + fn max_unsigned() -> u64; type Unsigned: SimdElement; @@ -78,6 +79,11 @@ macro_rules! impl_element { self as usize } + #[inline] + fn max_unsigned() -> u64 { + <$unsigned>::MAX as u64 + } + type Unsigned = $unsigned; const TRUE: Self = -1; diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs index ae9ff6894b0..8a1079042f0 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -16,7 +16,10 @@ where #[inline] pub fn swizzle_dyn(self, idxs: Simd<u8, N>) -> Self { #![allow(unused_imports, unused_unsafe)] - #[cfg(all(target_arch = "aarch64", target_endian = "little"))] + #[cfg(all( + any(target_arch = "aarch64", target_arch = "arm64ec"), + target_endian = "little" + ))] use core::arch::aarch64::{uint8x8_t, vqtbl1q_u8, vtbl1_u8}; #[cfg(all( target_arch = "arm", @@ -37,6 +40,7 @@ where #[cfg(all( any( target_arch = "aarch64", + target_arch = "arm64ec", all(target_arch = "arm", target_feature = "v7") ), target_feature = "neon", @@ -48,7 +52,7 @@ where #[cfg(target_feature = "simd128")] 16 => transize(wasm::i8x16_swizzle, self, idxs), #[cfg(all( - target_arch = "aarch64", + any(target_arch = "aarch64", target_arch = "arm64ec"), target_feature = "neon", target_endian = "little" ))] diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 9e97a3161bb..6c8205b112c 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -1,5 +1,6 @@ use crate::simd::{ cmp::SimdPartialOrd, + num::SimdUint, ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; @@ -262,6 +263,7 @@ where /// # Panics /// /// Panics if the slice's length is less than the vector's `Simd::N`. + /// Use `load_or_default` for an alternative that does not panic. /// /// # Example /// @@ -315,6 +317,143 @@ where unsafe { self.store(slice.as_mut_ptr().cast()) } } + /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for + /// the `slice`. Otherwise, the default value for the element type is returned. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; + /// let vec: Vec<i32> = vec![10, 11]; + /// + /// let result = Simd::<i32, 4>::load_or_default(&vec); + /// assert_eq!(result, Simd::from_array([10, 11, 0, 0])); + /// ``` + #[must_use] + #[inline] + pub fn load_or_default(slice: &[T]) -> Self + where + T: Default, + { + Self::load_or(slice, Default::default()) + } + + /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for + /// the `slice`. Otherwise, the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; + /// let vec: Vec<i32> = vec![10, 11]; + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_or(&vec, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, -2])); + /// ``` + #[must_use] + #[inline] + pub fn load_or(slice: &[T], or: Self) -> Self { + Self::load_select(slice, Mask::splat(true), or) + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled or out of bounds for the slice, that memory location + /// is not accessed and the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let enable = Mask::from_array([true, true, false, true]); + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_select(&vec, enable, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, 13])); + /// ``` + #[must_use] + #[inline] + pub fn load_select_or_default(slice: &[T], enable: Mask<<T as SimdElement>::Mask, N>) -> Self + where + T: Default, + { + Self::load_select(slice, enable, Default::default()) + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled or out of bounds for the slice, that memory location + /// is not accessed and the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let enable = Mask::from_array([true, true, false, true]); + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_select(&vec, enable, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, 13])); + /// ``` + #[must_use] + #[inline] + pub fn load_select( + slice: &[T], + mut enable: Mask<<T as SimdElement>::Mask, N>, + or: Self, + ) -> Self { + enable &= mask_up_to(slice.len()); + // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to + // the element. + unsafe { Self::load_select_ptr(slice.as_ptr(), enable, or) } + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled, that memory location is not accessed and the corresponding + /// value from `or` is passed through. + #[must_use] + #[inline] + pub unsafe fn load_select_unchecked( + slice: &[T], + enable: Mask<<T as SimdElement>::Mask, N>, + or: Self, + ) -> Self { + let ptr = slice.as_ptr(); + // SAFETY: The safety of reading elements from `slice` is ensured by the caller. + unsafe { Self::load_select_ptr(ptr, enable, or) } + } + + /// Reads contiguous elements starting at `ptr`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled, that memory location is not accessed and the corresponding + /// value from `or` is passed through. + #[must_use] + #[inline] + pub unsafe fn load_select_ptr( + ptr: *const T, + enable: Mask<<T as SimdElement>::Mask, N>, + or: Self, + ) -> Self { + // SAFETY: The safety of reading elements through `ptr` is ensured by the caller. + unsafe { core::intrinsics::simd::simd_masked_load(enable.to_int(), ptr, or) } + } + /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. /// If an index is out-of-bounds, the element is instead selected from the `or` vector. /// @@ -493,6 +632,77 @@ where unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) } } + /// Conditionally write contiguous elements to `slice`. The `enable` mask controls + /// which elements are written, as long as they're in-bounds of the `slice`. + /// If the element is disabled or out of bounds, no memory access to that location + /// is made. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let mut arr = [0i32; 4]; + /// let write = Simd::from_array([-5, -4, -3, -2]); + /// let enable = Mask::from_array([false, true, true, true]); + /// + /// write.store_select(&mut arr[..3], enable); + /// assert_eq!(arr, [0, -4, -3, 0]); + /// ``` + #[inline] + pub fn store_select(self, slice: &mut [T], mut enable: Mask<<T as SimdElement>::Mask, N>) { + enable &= mask_up_to(slice.len()); + // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to + // the element. + unsafe { self.store_select_ptr(slice.as_mut_ptr(), enable) } + } + + /// Conditionally write contiguous elements to `slice`. The `enable` mask controls + /// which elements are written. + /// + /// # Safety + /// + /// Every enabled element must be in bounds for the `slice`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let mut arr = [0i32; 4]; + /// let write = Simd::from_array([-5, -4, -3, -2]); + /// let enable = Mask::from_array([false, true, true, true]); + /// + /// unsafe { write.store_select_unchecked(&mut arr, enable) }; + /// assert_eq!(arr, [0, -4, -3, -2]); + /// ``` + #[inline] + pub unsafe fn store_select_unchecked( + self, + slice: &mut [T], + enable: Mask<<T as SimdElement>::Mask, N>, + ) { + let ptr = slice.as_mut_ptr(); + // SAFETY: The safety of writing elements in `slice` is ensured by the caller. + unsafe { self.store_select_ptr(ptr, enable) } + } + + /// Conditionally write contiguous elements starting from `ptr`. + /// The `enable` mask controls which elements are written. + /// When disabled, the memory location corresponding to that element is not accessed. + /// + /// # Safety + /// + /// Memory addresses for element are calculated [`pointer::wrapping_offset`] and + /// each enabled element must satisfy the same conditions as [`core::ptr::write`]. + #[inline] + pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<<T as SimdElement>::Mask, N>) { + // SAFETY: The safety of writing elements through `ptr` is ensured by the caller. + unsafe { core::intrinsics::simd::simd_masked_store(enable.to_int(), ptr, self) } + } + /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`. /// If an index is out-of-bounds, the write is suppressed without panicking. /// If two elements in the scattered vector would write to the same index @@ -980,3 +1190,37 @@ where { type Mask = isize; } + +#[inline] +fn lane_indices<const N: usize>() -> Simd<usize, N> +where + LaneCount<N>: SupportedLaneCount, +{ + let mut index = [0; N]; + for i in 0..N { + index[i] = i; + } + Simd::from_array(index) +} + +#[inline] +fn mask_up_to<M, const N: usize>(len: usize) -> Mask<M, N> +where + LaneCount<N>: SupportedLaneCount, + M: MaskElement, +{ + let index = lane_indices::<N>(); + let max_value: u64 = M::max_unsigned(); + macro_rules! case { + ($ty:ty) => { + if N < <$ty>::MAX as usize && max_value as $ty as u64 == max_value { + return index.cast().simd_lt(Simd::splat(len.min(N) as $ty)).cast(); + } + }; + } + case!(u8); + case!(u16); + case!(u32); + case!(u64); + index.simd_lt(Simd::splat(len)).cast() +} diff --git a/library/portable-simd/crates/core_simd/src/vendor.rs b/library/portable-simd/crates/core_simd/src/vendor.rs index 6223bedb4e1..1a34a3a8de5 100644 --- a/library/portable-simd/crates/core_simd/src/vendor.rs +++ b/library/portable-simd/crates/core_simd/src/vendor.rs @@ -24,7 +24,7 @@ mod x86; #[cfg(target_arch = "wasm32")] mod wasm32; -#[cfg(any(target_arch = "aarch64", target_arch = "arm",))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm",))] mod arm; #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] diff --git a/library/portable-simd/crates/core_simd/src/vendor/arm.rs b/library/portable-simd/crates/core_simd/src/vendor/arm.rs index ee5c6421373..f8878d11f09 100644 --- a/library/portable-simd/crates/core_simd/src/vendor/arm.rs +++ b/library/portable-simd/crates/core_simd/src/vendor/arm.rs @@ -4,12 +4,13 @@ use crate::simd::*; #[cfg(target_arch = "arm")] use core::arch::arm::*; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] use core::arch::aarch64::*; #[cfg(all( any( target_arch = "aarch64", + target_arch = "arm64ec", all(target_arch = "arm", target_feature = "v7"), ), target_endian = "little" @@ -69,7 +70,10 @@ mod simd32 { from_transmute! { unsafe Simd<i8, 4> => int8x4_t } } -#[cfg(target_arch = "aarch64")] +#[cfg(all( + any(target_arch = "aarch64", target_arch = "arm64ec"), + target_endian = "little" +))] mod aarch64 { use super::neon::*; use super::*; diff --git a/library/portable-simd/crates/core_simd/tests/masked_load_store.rs b/library/portable-simd/crates/core_simd/tests/masked_load_store.rs new file mode 100644 index 00000000000..3d38658e945 --- /dev/null +++ b/library/portable-simd/crates/core_simd/tests/masked_load_store.rs @@ -0,0 +1,35 @@ +#![feature(portable_simd)] +use core_simd::simd::prelude::*; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::*; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test_configure!(run_in_browser); + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn masked_load_store() { + let mut arr = [u8::MAX; 7]; + + u8x4::splat(0).store_select(&mut arr[5..], Mask::from_array([false, true, false, true])); + // write to index 8 is OOB and dropped + assert_eq!(arr, [255u8, 255, 255, 255, 255, 255, 0]); + + u8x4::from_array([0, 1, 2, 3]).store_select(&mut arr[1..], Mask::splat(true)); + assert_eq!(arr, [255u8, 0, 1, 2, 3, 255, 0]); + + // read from index 8 is OOB and dropped + assert_eq!( + u8x4::load_or(&arr[4..], u8x4::splat(42)), + u8x4::from_array([3, 255, 0, 42]) + ); + assert_eq!( + u8x4::load_select( + &arr[4..], + Mask::from_array([true, false, true, true]), + u8x4::splat(42) + ), + u8x4::from_array([3, 42, 0, 42]) + ); +} diff --git a/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs index f21a937f01c..19ffe1417c8 100644 --- a/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs @@ -1,6 +1,6 @@ #![feature(portable_simd)] use core::{fmt, ops::RangeInclusive}; -use test_helpers::{self, biteq, make_runner, prop_assert_biteq}; +use test_helpers::{biteq, make_runner, prop_assert_biteq}; fn swizzle_dyn_scalar_ver<const N: usize>(values: [u8; N], idxs: [u8; N]) -> [u8; N] { let mut array = [0; N]; diff --git a/library/portable-simd/crates/std_float/Cargo.toml b/library/portable-simd/crates/std_float/Cargo.toml index 84c69774cbd..0896094ee63 100644 --- a/library/portable-simd/crates/std_float/Cargo.toml +++ b/library/portable-simd/crates/std_float/Cargo.toml @@ -8,6 +8,13 @@ edition = "2021" [dependencies] core_simd = { path = "../core_simd", default-features = false } +[dev-dependencies.test_helpers] +path = "../test_helpers" + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3" + [features] default = ["as_crate"] as_crate = [] diff --git a/library/portable-simd/crates/std_float/src/lib.rs b/library/portable-simd/crates/std_float/src/lib.rs index 4c547777fde..148aa5f9f17 100644 --- a/library/portable-simd/crates/std_float/src/lib.rs +++ b/library/portable-simd/crates/std_float/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "as_crate", no_std)] // We are std! #![cfg_attr( feature = "as_crate", feature(core_intrinsics), @@ -44,7 +43,7 @@ use crate::sealed::Sealed; /// For now this trait is available to permit experimentation with SIMD float /// operations that may lack hardware support, such as `mul_add`. pub trait StdFloat: Sealed + Sized { - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error, + /// Elementwise fused multiply-add. Computes `(self * a) + b` with only one rounding error, /// yielding a more accurate result than an unfused multiply-add. /// /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target @@ -57,22 +56,65 @@ pub trait StdFloat: Sealed + Sized { unsafe { intrinsics::simd_fma(self, a, b) } } - /// Produces a vector where every lane has the square root value - /// of the equivalently-indexed lane in `self` + /// Produces a vector where every element has the square root value + /// of the equivalently-indexed element in `self` #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn sqrt(self) -> Self { unsafe { intrinsics::simd_fsqrt(self) } } - /// Returns the smallest integer greater than or equal to each lane. + /// Produces a vector where every element has the sine of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn sin(self) -> Self; + + /// Produces a vector where every element has the cosine of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn cos(self) -> Self; + + /// Produces a vector where every element has the exponential (base e) of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn exp(self) -> Self; + + /// Produces a vector where every element has the exponential (base 2) of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn exp2(self) -> Self; + + /// Produces a vector where every element has the natural logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn ln(self) -> Self; + + /// Produces a vector where every element has the logarithm with respect to an arbitrary + /// in the equivalently-indexed elements in `self` and `base`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log(self, base: Self) -> Self { + unsafe { intrinsics::simd_div(self.ln(), base.ln()) } + } + + /// Produces a vector where every element has the base-2 logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log2(self) -> Self; + + /// Produces a vector where every element has the base-10 logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log10(self) -> Self; + + /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn ceil(self) -> Self { unsafe { intrinsics::simd_ceil(self) } } - /// Returns the largest integer value less than or equal to each lane. + /// Returns the largest integer value less than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn floor(self) -> Self { @@ -101,46 +143,65 @@ pub trait StdFloat: Sealed + Sized { impl<const N: usize> Sealed for Simd<f32, N> where LaneCount<N>: SupportedLaneCount {} impl<const N: usize> Sealed for Simd<f64, N> where LaneCount<N>: SupportedLaneCount {} -// We can safely just use all the defaults. -impl<const N: usize> StdFloat for Simd<f32, N> -where - LaneCount<N>: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } -} - -impl<const N: usize> StdFloat for Simd<f64, N> -where - LaneCount<N>: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() +macro_rules! impl_float { + { + $($fn:ident: $intrinsic:ident,)* + } => { + impl<const N: usize> StdFloat for Simd<f32, N> + where + LaneCount<N>: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + unsafe { intrinsics::$intrinsic(self) } + } + )* + } + + impl<const N: usize> StdFloat for Simd<f64, N> + where + LaneCount<N>: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + // https://github.com/llvm/llvm-project/issues/83729 + #[cfg(target_arch = "aarch64")] + { + let mut ln = Self::splat(0f64); + for i in 0..N { + ln[i] = self[i].$fn() + } + ln + } + + #[cfg(not(target_arch = "aarch64"))] + { + unsafe { intrinsics::$intrinsic(self) } + } + } + )* + } } } -#[cfg(test)] -mod tests { - use super::*; - use simd::prelude::*; - - #[test] - fn everything_works() { - let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]); - let x2 = x + x; - let _xc = x.ceil(); - let _xf = x.floor(); - let _xr = x.round(); - let _xt = x.trunc(); - let _xfma = x.mul_add(x, x); - let _xsqrt = x.sqrt(); - let _ = x2.abs() * x2; - } +impl_float! { + sin: simd_fsin, + cos: simd_fcos, + exp: simd_fexp, + exp2: simd_fexp2, + ln: simd_flog, + log2: simd_flog2, + log10: simd_flog10, } diff --git a/library/portable-simd/crates/std_float/tests/float.rs b/library/portable-simd/crates/std_float/tests/float.rs new file mode 100644 index 00000000000..c66c968f8c6 --- /dev/null +++ b/library/portable-simd/crates/std_float/tests/float.rs @@ -0,0 +1,74 @@ +#![feature(portable_simd)] + +macro_rules! unary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func<const LANES: usize>() { + test_helpers::test_unary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_| true, + ) + } + )* + } + } +} + +macro_rules! binary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func<const LANES: usize>() { + test_helpers::test_binary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _| true, + ) + } + )* + } + } +} + +macro_rules! ternary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func<const LANES: usize>() { + test_helpers::test_ternary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _, _| true, + ) + } + )* + } + } +} + +macro_rules! impl_tests { + { $scalar:tt } => { + mod $scalar { + use std_float::StdFloat; + + unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc } + binary_test! { $scalar, log } + ternary_test! { $scalar, mul_add } + + test_helpers::test_lanes! { + fn fract<const LANES: usize>() { + test_helpers::test_unary_elementwise_flush_subnormals( + &core_simd::simd::Simd::<$scalar, LANES>::fract, + &$scalar::fract, + &|_| true, + ) + } + } + } + } +} + +impl_tests! { f32 } +impl_tests! { f64 } diff --git a/library/proc_macro/src/bridge/handle.rs b/library/proc_macro/src/bridge/handle.rs index 894acae217e..8c53bb609f6 100644 --- a/library/proc_macro/src/bridge/handle.rs +++ b/library/proc_macro/src/bridge/handle.rs @@ -21,7 +21,7 @@ impl<T> OwnedStore<T> { pub(super) fn new(counter: &'static AtomicU32) -> Self { // Ensure the handle counter isn't 0, which would panic later, // when `NonZero::new` (aka `Handle::new`) is called in `alloc`. - assert_ne!(counter.load(Ordering::SeqCst), 0); + assert_ne!(counter.load(Ordering::Relaxed), 0); OwnedStore { counter, data: BTreeMap::new() } } @@ -29,7 +29,7 @@ impl<T> OwnedStore<T> { impl<T> OwnedStore<T> { pub(super) fn alloc(&mut self, x: T) -> Handle { - let counter = self.counter.fetch_add(1, Ordering::SeqCst); + let counter = self.counter.fetch_add(1, Ordering::Relaxed); let handle = Handle::new(counter).expect("`proc_macro` handle counter overflowed"); assert!(self.data.insert(handle, x).is_none()); handle diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index a834b36697c..dc0e302a810 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -329,7 +329,7 @@ static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// ``` #[unstable(feature = "alloc_error_hook", issue = "51245")] pub fn set_alloc_error_hook(hook: fn(Layout)) { - HOOK.store(hook as *mut (), Ordering::SeqCst); + HOOK.store(hook as *mut (), Ordering::Release); } /// Unregisters the current allocation error hook, returning it. @@ -339,7 +339,7 @@ pub fn set_alloc_error_hook(hook: fn(Layout)) { /// If no custom hook is registered, the default hook will be returned. #[unstable(feature = "alloc_error_hook", issue = "51245")] pub fn take_alloc_error_hook() -> fn(Layout) { - let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); + let hook = HOOK.swap(ptr::null_mut(), Ordering::Acquire); if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } } } @@ -362,7 +362,7 @@ fn default_alloc_error_hook(layout: Layout) { #[alloc_error_handler] #[unstable(feature = "alloc_internals", issue = "none")] pub fn rust_oom(layout: Layout) -> ! { - let hook = HOOK.load(Ordering::SeqCst); + let hook = HOOK.load(Ordering::Acquire); let hook: fn(Layout) = if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; hook(layout); diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs index 37937b5ea95..d318d457f35 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/test.rs @@ -7,12 +7,12 @@ use crate::sync::atomic::{AtomicUsize, Ordering}; static PORT: AtomicUsize = AtomicUsize::new(0); pub fn next_test_ip4() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) } pub fn next_test_ip6() -> SocketAddr { - let port = PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port(); + let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + base_port(); SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), port, 0, 0)) } diff --git a/library/std/src/os/freebsd/net.rs b/library/std/src/os/freebsd/net.rs index 33990d54caa..b7e0fdc0a9a 100644 --- a/library/std/src/os/freebsd/net.rs +++ b/library/std/src/os/freebsd/net.rs @@ -2,6 +2,7 @@ #![unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; @@ -40,6 +41,15 @@ pub trait UnixSocketExt: Sealed { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()>; + + /// Get a filter name if one had been set previously on the socket. + #[unstable(feature = "acceptfilter", issue = "121891")] + fn acceptfilter(&self) -> io::Result<&CStr>; + + /// Set or disable a filter on the socket to filter incoming connections + /// to defer it before accept(2) + #[unstable(feature = "acceptfilter", issue = "121891")] + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()>; } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -51,6 +61,14 @@ impl UnixSocketExt for net::UnixDatagram { fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { self.as_inner().set_local_creds_persistent(local_creds_persistent) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -62,4 +80,12 @@ impl UnixSocketExt for net::UnixStream { fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { self.as_inner().set_local_creds_persistent(local_creds_persistent) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } diff --git a/library/std/src/os/netbsd/net.rs b/library/std/src/os/netbsd/net.rs index 5c82f43077d..b9679c7b3af 100644 --- a/library/std/src/os/netbsd/net.rs +++ b/library/std/src/os/netbsd/net.rs @@ -2,6 +2,7 @@ #![unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; @@ -40,6 +41,15 @@ pub trait UnixSocketExt: Sealed { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] fn set_local_creds(&self, local_creds: bool) -> io::Result<()>; + + /// Get a filter name if one had been set previously on the socket. + #[unstable(feature = "acceptfilter", issue = "121891")] + fn acceptfilter(&self) -> io::Result<&CStr>; + + /// Set or disable a filter on the socket to filter incoming connections + /// to defer it before accept(2) + #[unstable(feature = "acceptfilter", issue = "121891")] + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()>; } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -51,6 +61,14 @@ impl UnixSocketExt for net::UnixDatagram { fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { self.as_inner().set_local_creds(local_creds) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] @@ -62,4 +80,12 @@ impl UnixSocketExt for net::UnixStream { fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { self.as_inner().set_local_creds(local_creds) } + + fn acceptfilter(&self) -> io::Result<&CStr> { + self.as_inner().acceptfilter() + } + + fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + self.as_inner().set_acceptfilter(name) + } } diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 464a46264cb..e6e1d32fa54 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -272,7 +272,7 @@ fn default_hook(info: &PanicInfo<'_>) { drop(backtrace::print(err, crate::backtrace_rs::PrintFmt::Full)) } Some(BacktraceStyle::Off) => { - if FIRST_PANIC.swap(false, Ordering::SeqCst) { + if FIRST_PANIC.swap(false, Ordering::Relaxed) { let _ = writeln!( err, "note: run with `RUST_BACKTRACE=1` environment variable to display a \ @@ -746,7 +746,13 @@ fn rust_panic_with_hook( panic_count::MustAbort::PanicInHook => { // Don't try to print the message in this case // - perhaps that is causing the recursive panics. - rtprintpanic!("thread panicked while processing panic. aborting.\n"); + let panicinfo = PanicInfo::internal_constructor( + None, // no message + location, // but we want to show the location! + can_unwind, + force_no_backtrace, + ); + rtprintpanic!("{panicinfo}\nthread panicked while processing panic. aborting.\n"); } panic_count::MustAbort::AlwaysAbort => { // Unfortunately, this does not print a backtrace, because creating diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 7a7a7737635..36fa4e88b5b 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -91,6 +91,15 @@ pub use core::prelude::v1::cfg_eval; )] pub use core::prelude::v1::type_ascribe; +#[cfg(not(bootstrap))] +// Do not `doc(no_inline)` either. +#[unstable( + feature = "deref_patterns", + issue = "87121", + reason = "placeholder syntax for deref patterns" +)] +pub use core::prelude::v1::deref; + // The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated // rather than glob imported because we want docs to show these re-exports as // pointing to within `std`. diff --git a/library/std/src/sync/condvar/tests.rs b/library/std/src/sync/condvar/tests.rs index 24f467f0b03..12d13a6b20b 100644 --- a/library/std/src/sync/condvar/tests.rs +++ b/library/std/src/sync/condvar/tests.rs @@ -170,14 +170,14 @@ fn wait_timeout_wake() { let t = thread::spawn(move || { let _g = m2.lock().unwrap(); thread::sleep(Duration::from_millis(1)); - notified_copy.store(true, Ordering::SeqCst); + notified_copy.store(true, Ordering::Relaxed); c2.notify_one(); }); let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap(); assert!(!timeout_res.timed_out()); // spurious wakeups mean this isn't necessarily true // so execute test again, if not notified - if !notified.load(Ordering::SeqCst) { + if !notified.load(Ordering::Relaxed) { t.join().unwrap(); continue; } diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index 895fcbd6b7e..d417034f5af 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -396,7 +396,7 @@ impl<T: ?Sized> Mutex<T> { self.poison.get() } - /// Clear the poisoned state from a mutex + /// Clear the poisoned state from a mutex. /// /// If the mutex is poisoned, it will remain poisoned until this function is called. This /// allows recovering from a poisoned state and marking that it has recovered. For example, if diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index f7f098c082a..d648cd08994 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -439,7 +439,7 @@ impl<T: ?Sized> RwLock<T> { self.poison.get() } - /// Clear the poisoned state from a lock + /// Clear the poisoned state from a lock. /// /// If the lock is poisoned, it will remain poisoned until this function is called. This allows /// recovering from a poisoned state and marking that it has recovered. For example, if the diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs index c4d5da1627c..edb28e2300f 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/pal/sgx/net.rs @@ -524,6 +524,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -536,6 +537,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index ba53ed88f37..23aa4da14a7 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -10,14 +10,16 @@ //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::{self, Guid}; +use r_efi::protocols::{device_path, device_path_to_text}; +use crate::ffi::OsString; +use crate::io::{self, const_io_error}; use crate::mem::{size_of, MaybeUninit}; -use crate::os::uefi; +use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt}; use crate::ptr::NonNull; -use crate::{ - io::{self, const_io_error}, - os::uefi::env::boot_services, -}; +use crate::slice; +use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::sys_common::wstr::WStrUnits; const BOOT_SERVICES_UNAVAILABLE: io::Error = const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); @@ -142,9 +144,74 @@ pub(crate) unsafe fn close_event(evt: NonNull<crate::ffi::c_void>) -> io::Result /// Get the Protocol for current system handle. /// Note: Some protocols need to be manually freed. It is the callers responsibility to do so. -pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> Option<NonNull<T>> { - let system_handle = uefi::env::try_image_handle()?; - open_protocol(system_handle, protocol_guid).ok() +pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> io::Result<NonNull<T>> { + let system_handle = uefi::env::try_image_handle().ok_or(io::const_io_error!( + io::ErrorKind::NotFound, + "Protocol not found in Image handle" + ))?; + open_protocol(system_handle, protocol_guid) +} + +pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> { + fn path_to_text( + protocol: NonNull<device_path_to_text::Protocol>, + path: NonNull<device_path::Protocol>, + ) -> io::Result<OsString> { + let path_ptr: *mut r_efi::efi::Char16 = unsafe { + ((*protocol.as_ptr()).convert_device_path_to_text)( + path.as_ptr(), + // DisplayOnly + r_efi::efi::Boolean::FALSE, + // AllowShortcuts + r_efi::efi::Boolean::FALSE, + ) + }; + + // SAFETY: `convert_device_path_to_text` returns a pointer to a null-terminated UTF-16 + // string, and that string cannot be deallocated prior to dropping the `WStrUnits`, so + // it's safe for `WStrUnits` to use. + let path_len = unsafe { + WStrUnits::new(path_ptr) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))? + .count() + }; + + let path = OsString::from_wide(unsafe { slice::from_raw_parts(path_ptr.cast(), path_len) }); + + if let Some(boot_services) = crate::os::uefi::env::boot_services() { + let boot_services: NonNull<r_efi::efi::BootServices> = boot_services.cast(); + unsafe { + ((*boot_services.as_ptr()).free_pool)(path_ptr.cast()); + } + } + + Ok(path) + } + + static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + return path_to_text(protocol, path); + } + } + + let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?; + for handle in device_path_to_text_handles { + if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return path_to_text(protocol, path); + } + } + + Err(io::const_io_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) } /// Get RuntimeServices diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index e6693db68e6..58838c5876e 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,4 +1,4 @@ -use super::{unsupported, RawOsError}; +use super::{helpers, unsupported, RawOsError}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::fmt; @@ -7,6 +7,7 @@ use crate::marker::PhantomData; use crate::os::uefi; use crate::path::{self, PathBuf}; use crate::ptr::NonNull; +use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use r_efi::efi::Status; pub fn errno() -> RawOsError { @@ -164,7 +165,10 @@ impl fmt::Display for JoinPathsError { impl StdError for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { - unsupported() + let protocol = helpers::image_handle_protocol::<device_path::Protocol>( + loaded_image_device_path::PROTOCOL_GUID, + )?; + helpers::device_path_to_text(protocol).map(PathBuf::from) } pub struct Env(!); diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index 09750b6ffc8..1f140f7844f 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -453,6 +453,37 @@ impl Socket { Ok(raw as u32) } + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + if !name.to_bytes().is_empty() { + const AF_NAME_MAX: usize = 16; + let mut buf = [0; AF_NAME_MAX]; + for (src, dst) in name.to_bytes().iter().zip(&mut buf[..AF_NAME_MAX - 1]) { + *dst = *src as i8; + } + let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; + arg.af_name = buf; + setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) + } else { + setsockopt( + self, + libc::SOL_SOCKET, + libc::SO_ACCEPTFILTER, + core::ptr::null_mut() as *mut c_void, + ) + } + } + + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn acceptfilter(&self) -> io::Result<&CStr> { + let arg: libc::accept_filter_arg = + getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; + let s: &[u8] = + unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; + let name = CStr::from_bytes_with_nul(s).unwrap(); + Ok(name) + } + #[cfg(any(target_os = "android", target_os = "linux",))] pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) diff --git a/library/std/src/sys/pal/unix/thread_local_dtor.rs b/library/std/src/sys/pal/unix/thread_local_dtor.rs index 79b152cece9..e367ce5f906 100644 --- a/library/std/src/sys/pal/unix/thread_local_dtor.rs +++ b/library/std/src/sys/pal/unix/thread_local_dtor.rs @@ -35,7 +35,7 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { #[cfg(not(sanitizer_cfi_normalize_integers))] #[cfi_encoding = "i"] #[repr(transparent)] - pub struct c_int(pub libc::c_int); + pub struct c_int(#[allow(dead_code)] pub libc::c_int); extern "C" { #[linkage = "extern_weak"] diff --git a/library/std/src/sys/pal/unix/thread_parking/pthread.rs b/library/std/src/sys/pal/unix/thread_parking/pthread.rs index ae805d84399..bb79cf9548e 100644 --- a/library/std/src/sys/pal/unix/thread_parking/pthread.rs +++ b/library/std/src/sys/pal/unix/thread_parking/pthread.rs @@ -5,7 +5,7 @@ use crate::marker::PhantomPinned; use crate::pin::Pin; use crate::ptr::addr_of_mut; use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; #[cfg(not(target_os = "nto"))] use crate::sys::time::TIMESPEC_MAX; #[cfg(target_os = "nto")] @@ -150,16 +150,18 @@ impl Parker { // This implementation doesn't require `unsafe`, but other implementations // may assume this is only called by the thread that owns the Parker. + // + // For memory ordering, see std/src/sys_common/thread_parking/futex.rs pub unsafe fn park(self: Pin<&Self>) { // If we were previously notified then we consume this notification and // return quickly. - if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { + if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed).is_ok() { return; } // Otherwise we need to coordinate going to sleep lock(self.lock.get()); - match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { + match self.state.compare_exchange(EMPTY, PARKED, Relaxed, Relaxed) { Ok(_) => {} Err(NOTIFIED) => { // We must read here, even though we know it will be `NOTIFIED`. @@ -168,7 +170,7 @@ impl Parker { // acquire operation that synchronizes with that `unpark` to observe // any writes it made before the call to unpark. To do that we must // read from the write it made to `state`. - let old = self.state.swap(EMPTY, SeqCst); + let old = self.state.swap(EMPTY, Acquire); unlock(self.lock.get()); @@ -185,7 +187,7 @@ impl Parker { loop { wait(self.cvar.get(), self.lock.get()); - match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) { + match self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed) { Ok(_) => break, // got a notification Err(_) => {} // spurious wakeup, go back to sleep } @@ -201,16 +203,16 @@ impl Parker { // Like `park` above we have a fast path for an already-notified thread, and // afterwards we start coordinating for a sleep. // return quickly. - if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { + if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Relaxed).is_ok() { return; } lock(self.lock.get()); - match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { + match self.state.compare_exchange(EMPTY, PARKED, Relaxed, Relaxed) { Ok(_) => {} Err(NOTIFIED) => { // We must read again here, see `park`. - let old = self.state.swap(EMPTY, SeqCst); + let old = self.state.swap(EMPTY, Acquire); unlock(self.lock.get()); assert_eq!(old, NOTIFIED, "park state changed unexpectedly"); @@ -228,7 +230,7 @@ impl Parker { // parked. wait_timeout(self.cvar.get(), self.lock.get(), dur); - match self.state.swap(EMPTY, SeqCst) { + match self.state.swap(EMPTY, Acquire) { NOTIFIED => unlock(self.lock.get()), // got a notification, hurray! PARKED => unlock(self.lock.get()), // no notification, alas n => { @@ -245,7 +247,7 @@ impl Parker { // `state` is already `NOTIFIED`. That is why this must be a swap // rather than a compare-and-swap that returns if it reads `NOTIFIED` // on failure. - match self.state.swap(NOTIFIED, SeqCst) { + match self.state.swap(NOTIFIED, Release) { EMPTY => return, // no one was waiting NOTIFIED => return, // already unparked PARKED => {} // gotta go wake someone up diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/pal/unsupported/net.rs index 931fe9ba246..87e6106468f 100644 --- a/library/std/src/sys/pal/unsupported/net.rs +++ b/library/std/src/sys/pal/unsupported/net.rs @@ -346,6 +346,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -358,6 +359,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/pal/wasi/net.rs index 2098d05db0b..b4cf94c8781 100644 --- a/library/std/src/sys/pal/wasi/net.rs +++ b/library/std/src/sys/pal/wasi/net.rs @@ -520,6 +520,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in { + #[allow(dead_code)] pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, @@ -532,6 +533,7 @@ pub mod netc { #[derive(Copy, Clone)] pub struct sockaddr_in6 { + #[allow(dead_code)] pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, diff --git a/library/std/src/sys/pal/wasm/alloc.rs b/library/std/src/sys/pal/wasm/alloc.rs index 6dceb1689a8..b74ce0d4742 100644 --- a/library/std/src/sys/pal/wasm/alloc.rs +++ b/library/std/src/sys/pal/wasm/alloc.rs @@ -57,7 +57,10 @@ unsafe impl GlobalAlloc for System { #[cfg(target_feature = "atomics")] mod lock { - use crate::sync::atomic::{AtomicI32, Ordering::SeqCst}; + use crate::sync::atomic::{ + AtomicI32, + Ordering::{Acquire, Release}, + }; static LOCKED: AtomicI32 = AtomicI32::new(0); @@ -65,7 +68,7 @@ mod lock { pub fn lock() -> DropLock { loop { - if LOCKED.swap(1, SeqCst) == 0 { + if LOCKED.swap(1, Acquire) == 0 { return DropLock; } // Ok so here's where things get a little depressing. At this point @@ -143,7 +146,7 @@ mod lock { impl Drop for DropLock { fn drop(&mut self) { - let r = LOCKED.swap(0, SeqCst); + let r = LOCKED.swap(0, Release); debug_assert_eq!(r, 1); // Note that due to the above logic we don't actually need to wake diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 013f588676a..dfa938d4d57 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -7,7 +7,7 @@ use crate::path::Path; use crate::ptr; use crate::slice; use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::atomic::Ordering::Relaxed; use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; @@ -214,11 +214,11 @@ pub fn spawn_pipe_relay( fn random_number() -> usize { static N: AtomicUsize = AtomicUsize::new(0); loop { - if N.load(SeqCst) != 0 { - return N.fetch_add(1, SeqCst); + if N.load(Relaxed) != 0 { + return N.fetch_add(1, Relaxed); } - N.store(hashmap_random_keys().0 as usize, SeqCst); + N.store(hashmap_random_keys().0 as usize, Relaxed); } } diff --git a/library/std/src/sys/pal/xous/alloc.rs b/library/std/src/sys/pal/xous/alloc.rs index 0d540e95520..601411173aa 100644 --- a/library/std/src/sys/pal/xous/alloc.rs +++ b/library/std/src/sys/pal/xous/alloc.rs @@ -46,7 +46,10 @@ unsafe impl GlobalAlloc for System { } mod lock { - use crate::sync::atomic::{AtomicI32, Ordering::SeqCst}; + use crate::sync::atomic::{ + AtomicI32, + Ordering::{Acquire, Release}, + }; static LOCKED: AtomicI32 = AtomicI32::new(0); @@ -54,7 +57,7 @@ mod lock { pub fn lock() -> DropLock { loop { - if LOCKED.swap(1, SeqCst) == 0 { + if LOCKED.swap(1, Acquire) == 0 { return DropLock; } crate::os::xous::ffi::do_yield(); @@ -63,7 +66,7 @@ mod lock { impl Drop for DropLock { fn drop(&mut self) { - let r = LOCKED.swap(0, SeqCst); + let r = LOCKED.swap(0, Release); debug_assert_eq!(r, 1); } } diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/pal/xous/net/tcpstream.rs index 7149678118a..aebef02acda 100644 --- a/library/std/src/sys/pal/xous/net/tcpstream.rs +++ b/library/std/src/sys/pal/xous/net/tcpstream.rs @@ -406,7 +406,7 @@ impl TcpStream { } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.nonblocking.store(nonblocking, Ordering::SeqCst); + self.nonblocking.store(nonblocking, Ordering::Relaxed); Ok(()) } } diff --git a/library/std/src/sys/pal/xous/thread_local_key.rs b/library/std/src/sys/pal/xous/thread_local_key.rs index 59a668c3df6..2aaf46d0244 100644 --- a/library/std/src/sys/pal/xous/thread_local_key.rs +++ b/library/std/src/sys/pal/xous/thread_local_key.rs @@ -2,7 +2,7 @@ use crate::mem::ManuallyDrop; use crate::ptr; use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; +use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use core::arch::asm; use crate::os::xous::ffi::{map_memory, unmap_memory, MemoryFlags}; @@ -92,7 +92,7 @@ fn tls_table() -> &'static mut [*mut u8] { pub unsafe fn create(dtor: Option<Dtor>) -> Key { // Allocate a new TLS key. These keys are shared among all threads. #[allow(unused_unsafe)] - let key = unsafe { TLS_KEY_INDEX.fetch_add(1, SeqCst) }; + let key = unsafe { TLS_KEY_INDEX.fetch_add(1, Relaxed) }; if let Some(f) = dtor { unsafe { register_dtor(key, f) }; } @@ -154,11 +154,11 @@ unsafe fn register_dtor(key: Key, dtor: Dtor) { let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() })); #[allow(unused_unsafe)] - let mut head = unsafe { DTORS.load(SeqCst) }; + let mut head = unsafe { DTORS.load(Acquire) }; loop { node.next = head; #[allow(unused_unsafe)] - match unsafe { DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) } { + match unsafe { DTORS.compare_exchange(head, &mut **node, Release, Acquire) } { Ok(_) => return, // nothing to drop, we successfully added the node to the list Err(cur) => head = cur, } @@ -199,7 +199,7 @@ unsafe fn run_dtors() { } any_run = false; #[allow(unused_unsafe)] - let mut cur = unsafe { DTORS.load(SeqCst) }; + let mut cur = unsafe { DTORS.load(Acquire) }; while !cur.is_null() { let ptr = unsafe { get((*cur).key) }; diff --git a/library/std/src/sys/sync/mutex/xous.rs b/library/std/src/sys/sync/mutex/xous.rs index a8c9518ff0b..1426e48f8b7 100644 --- a/library/std/src/sys/sync/mutex/xous.rs +++ b/library/std/src/sys/sync/mutex/xous.rs @@ -1,6 +1,9 @@ use crate::os::xous::ffi::{blocking_scalar, do_yield}; use crate::os::xous::services::{ticktimer_server, TicktimerScalar}; -use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed, Ordering::SeqCst}; +use crate::sync::atomic::{ + AtomicBool, AtomicUsize, + Ordering::{Acquire, Relaxed, Release}, +}; pub struct Mutex { /// The "locked" value indicates how many threads are waiting on this @@ -68,7 +71,7 @@ impl Mutex { #[inline] pub unsafe fn unlock(&self) { - let prev = self.locked.fetch_sub(1, SeqCst); + let prev = self.locked.fetch_sub(1, Release); // If the previous value was 1, then this was a "fast path" unlock, so no // need to involve the Ticktimer server @@ -89,12 +92,12 @@ impl Mutex { #[inline] pub unsafe fn try_lock(&self) -> bool { - self.locked.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() + self.locked.compare_exchange(0, 1, Acquire, Relaxed).is_ok() } #[inline] pub unsafe fn try_lock_or_poison(&self) -> bool { - self.locked.fetch_add(1, SeqCst) == 0 + self.locked.fetch_add(1, Acquire) == 0 } } diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs index 204834984a2..7dcc1141099 100644 --- a/library/std/src/sys_common/thread_local_key.rs +++ b/library/std/src/sys_common/thread_local_key.rs @@ -128,7 +128,7 @@ impl StaticKey { #[inline] unsafe fn key(&self) -> imp::Key { - match self.key.load(Ordering::Relaxed) { + match self.key.load(Ordering::Acquire) { KEY_SENTVAL => self.lazy_init() as imp::Key, n => n as imp::Key, } @@ -156,8 +156,8 @@ impl StaticKey { match self.key.compare_exchange( KEY_SENTVAL, key as usize, - Ordering::SeqCst, - Ordering::SeqCst, + Ordering::Release, + Ordering::Acquire, ) { // The CAS succeeded, so we've created the actual key Ok(_) => key as usize, diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs index 964c7fc5b0c..25019b554bb 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/src/thread/local/tests.rs @@ -255,6 +255,9 @@ fn join_orders_after_tls_destructors() { // observe the channel in the `THREAD1_WAITING` state. If this does occur, // we switch to the “poison” state `THREAD2_JOINED` and panic all around. // (This is equivalent to “sending” from an alternate producer thread.) + // + // Relaxed memory ordering is fine because and spawn()/join() already provide all the + // synchronization we need here. const FRESH: u8 = 0; const THREAD2_LAUNCHED: u8 = 1; const THREAD1_WAITING: u8 = 2; @@ -263,7 +266,7 @@ fn join_orders_after_tls_destructors() { static SYNC_STATE: AtomicU8 = AtomicU8::new(FRESH); for _ in 0..10 { - SYNC_STATE.store(FRESH, Ordering::SeqCst); + SYNC_STATE.store(FRESH, Ordering::Relaxed); let jh = thread::Builder::new() .name("thread1".into()) @@ -272,7 +275,7 @@ fn join_orders_after_tls_destructors() { impl Drop for TlDrop { fn drop(&mut self) { - let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::SeqCst); + let mut sync_state = SYNC_STATE.swap(THREAD1_WAITING, Ordering::Relaxed); loop { match sync_state { THREAD2_LAUNCHED | THREAD1_WAITING => thread::yield_now(), @@ -282,7 +285,7 @@ fn join_orders_after_tls_destructors() { ), v => unreachable!("sync state: {}", v), } - sync_state = SYNC_STATE.load(Ordering::SeqCst); + sync_state = SYNC_STATE.load(Ordering::Relaxed); } } } @@ -294,7 +297,7 @@ fn join_orders_after_tls_destructors() { TL_DROP.with(|_| {}); loop { - match SYNC_STATE.load(Ordering::SeqCst) { + match SYNC_STATE.load(Ordering::Relaxed) { FRESH => thread::yield_now(), THREAD2_LAUNCHED => break, v => unreachable!("sync state: {}", v), @@ -306,9 +309,9 @@ fn join_orders_after_tls_destructors() { let jh2 = thread::Builder::new() .name("thread2".into()) .spawn(move || { - assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::SeqCst), FRESH); + assert_eq!(SYNC_STATE.swap(THREAD2_LAUNCHED, Ordering::Relaxed), FRESH); jh.join().unwrap(); - match SYNC_STATE.swap(THREAD2_JOINED, Ordering::SeqCst) { + match SYNC_STATE.swap(THREAD2_JOINED, Ordering::Relaxed) { MAIN_THREAD_RENDEZVOUS => return, THREAD2_LAUNCHED | THREAD1_WAITING => { panic!("Thread 2 running after thread 1 join before main thread rendezvous") @@ -322,8 +325,8 @@ fn join_orders_after_tls_destructors() { match SYNC_STATE.compare_exchange( THREAD1_WAITING, MAIN_THREAD_RENDEZVOUS, - Ordering::SeqCst, - Ordering::SeqCst, + Ordering::Relaxed, + Ordering::Relaxed, ) { Ok(_) => break, Err(FRESH) => thread::yield_now(), diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 8096e498263..f3918ba333a 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -46,7 +46,6 @@ pub struct ConsoleTestDiscoveryState { pub tests: usize, pub benchmarks: usize, pub ignored: usize, - pub options: Options, } impl ConsoleTestDiscoveryState { @@ -56,13 +55,7 @@ impl ConsoleTestDiscoveryState { None => None, }; - Ok(ConsoleTestDiscoveryState { - log_out, - tests: 0, - benchmarks: 0, - ignored: 0, - options: opts.options, - }) + Ok(ConsoleTestDiscoveryState { log_out, tests: 0, benchmarks: 0, ignored: 0 }) } pub fn write_log<F, S>(&mut self, msg: F) -> io::Result<()> diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 6e49bcc9744..30d728aa230 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -612,8 +612,14 @@ class RustBuild(object): self.fix_bin_or_dylib("{}/libexec/rust-analyzer-proc-macro-srv".format(bin_root)) lib_dir = "{}/lib".format(bin_root) for lib in os.listdir(lib_dir): - if lib.endswith(".so"): - self.fix_bin_or_dylib(os.path.join(lib_dir, lib)) + # .so is not necessarily the suffix, there can be version numbers afterwards. + if ".so" in lib: + elf_path = os.path.join(lib_dir, lib) + with open(elf_path, "rb") as f: + magic = f.read(4) + # Patchelf will skip non-ELF files, but issue a warning. + if magic == b"\x7fELF": + self.fix_bin_or_dylib(elf_path) with output(self.rustc_stamp()) as rust_stamp: rust_stamp.write(key) @@ -725,7 +731,7 @@ class RustBuild(object): os.path.join(os.path.realpath(nix_deps_dir), "lib") ] patchelf_args = ["--set-rpath", ":".join(rpath_entries)] - if not fname.endswith(".so"): + if ".so" not in fname: # Finally, set the correct .interp for binaries with open("{}/nix-support/dynamic-linker".format(nix_deps_dir)) as dynamic_linker: patchelf_args += ["--set-interpreter", dynamic_linker.read().rstrip()] diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 2076444ca0c..d40a3ea4c88 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2140,18 +2140,9 @@ pub struct CargoTarget<'a> { #[derive(Deserialize)] #[serde(tag = "reason", rename_all = "kebab-case")] pub enum CargoMessage<'a> { - CompilerArtifact { - package_id: Cow<'a, str>, - features: Vec<Cow<'a, str>>, - filenames: Vec<Cow<'a, str>>, - target: CargoTarget<'a>, - }, - BuildScriptExecuted { - package_id: Cow<'a, str>, - }, - BuildFinished { - success: bool, - }, + CompilerArtifact { filenames: Vec<Cow<'a, str>>, target: CargoTarget<'a> }, + BuildScriptExecuted, + BuildFinished, } pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path) { diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 251138388ca..75e0f646da6 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -1,6 +1,6 @@ use std::{ env, - ffi::{OsStr, OsString}, + ffi::OsString, fs::{self, File}, io::{BufRead, BufReader, BufWriter, ErrorKind, Write}, path::{Path, PathBuf}, @@ -183,7 +183,7 @@ impl Config { entries }; patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]); - if !fname.extension().map_or(false, |ext| ext == "so") { + if !path_is_dylib(fname) { // Finally, set the correct .interp for binaries let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker"); // FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ... @@ -440,7 +440,7 @@ impl Config { let lib_dir = bin_root.join("lib"); for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { let lib = t!(lib); - if lib.path().extension() == Some(OsStr::new("so")) { + if path_is_dylib(&lib.path()) { self.fix_bin_or_dylib(&lib.path()); } } @@ -545,7 +545,7 @@ impl Config { let lib_dir = bin_root.join("lib"); for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { let lib = t!(lib); - if lib.path().extension() == Some(OsStr::new("so")) { + if path_is_dylib(&lib.path()) { self.fix_bin_or_dylib(&lib.path()); } } @@ -697,7 +697,7 @@ download-rustc = false let llvm_lib = llvm_root.join("lib"); for entry in t!(fs::read_dir(llvm_lib)) { let lib = t!(entry).path(); - if lib.extension().map_or(false, |ext| ext == "so") { + if path_is_dylib(&lib) { self.fix_bin_or_dylib(&lib); } } @@ -743,3 +743,8 @@ download-rustc = false self.unpack(&tarball, &llvm_root, "rust-dev"); } } + +fn path_is_dylib(path: &Path) -> bool { + // The .so is not necessarily the extension, it might be libLLVM.so.18.1 + path.to_str().map_or(false, |path| path.contains(".so")) +} diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 64bb7bf01f7..d8397ab51de 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1009,15 +1009,23 @@ impl Build { let result = if !output.status.success() { if print_error { println!( - "\n\ncommand did not execute successfully: {:?}\n\ - expected success, got: {}\n\n\ - stdout ----\n{}\n\ - stderr ----\n{}\n\n", - command.command, + "\n\nCommand did not execute successfully.\ + \nExpected success, got: {}", output.status, - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) ); + + if !self.is_verbose() { + println!("Add `-v` to see more details.\n"); + } + + self.verbose(|| { + println!( + "\nSTDOUT ----\n{}\n\ + STDERR ----\n{}\n", + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) + }); } Err(()) } else { @@ -1389,6 +1397,13 @@ impl Build { if let Some(path) = finder.maybe_have("wasmtime") { if let Ok(mut path) = path.into_os_string().into_string() { path.push_str(" run -C cache=n --dir ."); + // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is + // required for libtest to work on beta/stable channels. + // + // NB: with Wasmtime 20 this can change to `-S inherit-env` to + // inherit the entire environment rather than just this single + // environment variable. + path.push_str(" --env RUSTC_BOOTSTRAP"); return Some(path); } } diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 944d9aed319..4de9afdb171 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -55,9 +55,9 @@ ENV RUST_CONFIGURE_ARGS \ ENV NO_DEBUG_ASSERTIONS=1 ENV NO_OVERFLOW_CHECKS=1 -RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v18.0.2/wasmtime-v18.0.2-x86_64-linux.tar.xz | \ +RUN curl -L https://github.com/bytecodealliance/wasmtime/releases/download/v19.0.0/wasmtime-v19.0.0-x86_64-linux.tar.xz | \ tar -xJ -ENV PATH "$PATH:/wasmtime-v18.0.2-x86_64-linux" +ENV PATH "$PATH:/wasmtime-v19.0.0-x86_64-linux" ENV WASM_TARGETS=wasm32-wasip1 ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_TARGETS \ diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 274745b9082..75d38dd20bd 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -339,6 +339,7 @@ target | std | host | notes `riscv32gc-unknown-linux-gnu` | | | RISC-V Linux (kernel 5.4, glibc 2.33) `riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl 1.2.3 + RISCV32 support patches) [`riscv32im-risc0-zkvm-elf`](platform-support/riscv32im-risc0-zkvm-elf.md) | ? | | RISC Zero's zero-knowledge Virtual Machine (RV32IM ISA) +[`riscv32ima-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32IMA ISA) [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF diff --git a/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md index 739b12bad8b..9a27a568b57 100644 --- a/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md +++ b/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md @@ -1,9 +1,13 @@ -# `riscv32{i,im,imc,imac,imafc}-unknown-none-elf` +# `riscv32{i,im,ima,imc,imac,imafc}-unknown-none-elf` **Tier: 2** Bare-metal target for RISC-V CPUs with the RV32I, RV32IM, RV32IMC, RV32IMAFC and RV32IMAC ISAs. +**Tier: 3** + +Bare-metal target for RISC-V CPUs with the RV32IMA ISA. + ## Target maintainers * Rust Embedded Working Group, [RISC-V team](https://github.com/rust-embedded/wg#the-risc-v-team) diff --git a/src/doc/unstable-book/src/language-features/postfix-match.md b/src/doc/unstable-book/src/language-features/postfix-match.md new file mode 100644 index 00000000000..cd6b6a7442c --- /dev/null +++ b/src/doc/unstable-book/src/language-features/postfix-match.md @@ -0,0 +1,22 @@ +# `postfix-match` + +`postfix-match` adds the feature for matching upon values postfix +the expressions that generate the values. + +```rust,edition2021 +#![feature(postfix_match)] + +enum Foo { + Bar, + Baz +} + +fn get_foo() -> Foo { + Foo::Bar +} + +get_foo().match { + Foo::Bar => {}, + Foo::Baz => panic!(), +} +``` diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 855fb132fc8..ef707d179ea 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -20,7 +20,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE}; use rustc_hir::PredicateOrigin; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir_analysis::lower_ty; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_middle::metadata::Reexport; use rustc_middle::middle::resolve_bound_vars as rbv; @@ -626,7 +626,10 @@ fn is_impl_trait(param: &hir::GenericParam<'_>) -> bool { /// /// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information. fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { - matches!(param.kind, hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided }) + matches!( + param.kind, + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided(_) } + ) } pub(crate) fn clean_generics<'tcx>( @@ -1257,12 +1260,8 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext hir::TraitItemKind::Type(bounds, Some(default)) => { let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); - let item_type = clean_middle_ty( - ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)), - cx, - None, - None, - ); + let item_type = + clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, default)), cx, None, None); AssocTypeItem( Box::new(TypeAlias { type_: clean_ty(default, cx), @@ -1303,12 +1302,8 @@ pub(crate) fn clean_impl_item<'tcx>( hir::ImplItemKind::Type(hir_ty) => { let type_ = clean_ty(hir_ty, cx); let generics = clean_generics(impl_.generics, cx); - let item_type = clean_middle_ty( - ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), - cx, - None, - None, - ); + let item_type = + clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), cx, None, None); AssocTypeItem( Box::new(TypeAlias { type_, @@ -1687,7 +1682,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type } hir::QPath::Resolved(Some(qself), p) => { // Try to normalize `<X as Y>::T` to a type - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = lower_ty(cx.tcx, hir_ty); // `hir_to_ty` can return projection types with escaping vars for GATs, e.g. `<() as Trait>::Gat<'_>` if !ty.has_escaping_bound_vars() && let Some(normalized_value) = normalize(cx, ty::Binder::dummy(ty)) @@ -1713,7 +1708,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type })) } hir::QPath::TypeRelative(qself, segment) => { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = lower_ty(cx.tcx, hir_ty); let self_type = clean_ty(qself, cx); let (trait_, should_show_cast) = match ty.kind() { @@ -2058,8 +2053,8 @@ pub(crate) fn clean_middle_ty<'tcx>( let n = print_const(cx, n); Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into()) } - ty::RawPtr(mt) => { - RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None, None))) + ty::RawPtr(ty, mutbl) => { + RawPointer(mutbl, Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))) } ty::Ref(r, ty, mutbl) => BorrowedRef { lifetime: clean_middle_region(r), @@ -2739,12 +2734,8 @@ fn clean_maybe_renamed_item<'tcx>( ItemKind::TyAlias(hir_ty, generics) => { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; let rustdoc_ty = clean_ty(hir_ty, cx); - let type_ = clean_middle_ty( - ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), - cx, - None, - None, - ); + let type_ = + clean_middle_ty(ty::Binder::dummy(lower_ty(cx.tcx, hir_ty)), cx, None, None); let generics = clean_generics(generics, cx); if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { *count -= 1; diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 365d63d9657..977b4bb45b6 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -329,6 +329,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { elts.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(", ") ), PatKind::Box(p) => return name_from_pat(&*p), + PatKind::Deref(p) => format!("deref!({})", name_from_pat(&*p)), PatKind::Ref(p, _) => return name_from_pat(&*p), PatKind::Lit(..) => { warn!( diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c6eb7be08cd..211921715b0 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -20,9 +20,9 @@ use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use rustc_target::spec::{Target, TargetTriple}; -use tempfile::Builder as TempFileBuilder; use std::env; +use std::fs::File; use std::io::{self, Write}; use std::panic; use std::path::{Path, PathBuf}; @@ -31,6 +31,8 @@ use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use tempfile::{Builder as TempFileBuilder, TempDir}; + use crate::clean::{types::AttributesExt, Attributes}; use crate::config::Options as RustdocOptions; use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; @@ -48,7 +50,55 @@ pub(crate) struct GlobalTestOptions { pub(crate) attrs: Vec<String>, } -pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { +pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> { + let mut file = File::create(file_path) + .map_err(|error| format!("failed to create args file: {error:?}"))?; + + // We now put the common arguments into the file we created. + let mut content = vec!["--crate-type=bin".to_string()]; + + for cfg in &options.cfgs { + content.push(format!("--cfg={cfg}")); + } + if !options.check_cfgs.is_empty() { + content.push("-Zunstable-options".to_string()); + for check_cfg in &options.check_cfgs { + content.push(format!("--check-cfg={check_cfg}")); + } + } + + if let Some(sysroot) = &options.maybe_sysroot { + content.push(format!("--sysroot={}", sysroot.display())); + } + for lib_str in &options.lib_strs { + content.push(format!("-L{lib_str}")); + } + for extern_str in &options.extern_strs { + content.push(format!("--extern={extern_str}")); + } + content.push("-Ccodegen-units=1".to_string()); + for codegen_options_str in &options.codegen_options_strs { + content.push(format!("-C{codegen_options_str}")); + } + for unstable_option_str in &options.unstable_opts_strs { + content.push(format!("-Z{unstable_option_str}")); + } + + let content = content.join("\n"); + + file.write(content.as_bytes()) + .map_err(|error| format!("failed to write arguments to temporary file: {error:?}"))?; + Ok(()) +} + +fn get_doctest_dir() -> io::Result<TempDir> { + TempFileBuilder::new().prefix("rustdoctest").tempdir() +} + +pub(crate) fn run( + dcx: &rustc_errors::DiagCtxt, + options: RustdocOptions, +) -> Result<(), ErrorGuaranteed> { let input = config::Input::File(options.input.clone()); let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name; @@ -118,6 +168,15 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { let externs = options.externs.clone(); let json_unused_externs = options.json_unused_externs; + let temp_dir = match get_doctest_dir() + .map_err(|error| format!("failed to create temporary directory: {error:?}")) + { + Ok(temp_dir) => temp_dir, + Err(error) => return crate::wrap_return(dcx, Err(error)), + }; + let file_path = temp_dir.path().join("rustdoc-cfgs"); + crate::wrap_return(dcx, generate_args_file(&file_path, &options))?; + let (tests, unused_extern_reports, compiling_test_count) = interface::run_compiler(config, |compiler| { compiler.enter(|queries| { @@ -134,6 +193,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { Some(compiler.sess.psess.clone_source_map()), None, enable_per_target_ignores, + file_path, ); let mut hir_collector = HirCollector { @@ -322,44 +382,35 @@ fn run_test( test: &str, crate_name: &str, line: usize, - rustdoc_options: RustdocOptions, + rustdoc_options: IndividualTestOptions, mut lang_string: LangString, no_run: bool, - runtool: Option<String>, - runtool_args: Vec<String>, - target: TargetTriple, opts: &GlobalTestOptions, edition: Edition, - outdir: DirState, path: PathBuf, - test_id: &str, report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { - let (test, line_offset, supports_color) = - make_test(test, Some(crate_name), lang_string.test_harness, opts, edition, Some(test_id)); + let (test, line_offset, supports_color) = make_test( + test, + Some(crate_name), + lang_string.test_harness, + opts, + edition, + Some(&rustdoc_options.test_id), + ); // Make sure we emit well-formed executable names for our target. - let rust_out = add_exe_suffix("rust_out".to_owned(), &target); - let output_file = outdir.path().join(rust_out); + let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); + let output_file = rustdoc_options.outdir.path().join(rust_out); let rustc_binary = rustdoc_options .test_builder .as_deref() .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); - compiler.arg("--crate-type").arg("bin"); - for cfg in &rustdoc_options.cfgs { - compiler.arg("--cfg").arg(&cfg); - } - if !rustdoc_options.check_cfgs.is_empty() { - compiler.arg("-Z").arg("unstable-options"); - for check_cfg in &rustdoc_options.check_cfgs { - compiler.arg("--check-cfg").arg(&check_cfg); - } - } - if let Some(sysroot) = rustdoc_options.maybe_sysroot { - compiler.arg("--sysroot").arg(sysroot); - } + + compiler.arg(&format!("@{}", rustdoc_options.arg_file.display())); + compiler.arg("--edition").arg(&edition.to_string()); compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path); compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize)); @@ -367,29 +418,17 @@ fn run_test( if lang_string.test_harness { compiler.arg("--test"); } - if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail { + if rustdoc_options.is_json_unused_externs_enabled && !lang_string.compile_fail { compiler.arg("--error-format=json"); compiler.arg("--json").arg("unused-externs"); - compiler.arg("-Z").arg("unstable-options"); compiler.arg("-W").arg("unused_crate_dependencies"); + compiler.arg("-Z").arg("unstable-options"); } - for lib_str in &rustdoc_options.lib_strs { - compiler.arg("-L").arg(&lib_str); - } - for extern_str in &rustdoc_options.extern_strs { - compiler.arg("--extern").arg(&extern_str); - } - compiler.arg("-Ccodegen-units=1"); - for codegen_options_str in &rustdoc_options.codegen_options_strs { - compiler.arg("-C").arg(&codegen_options_str); - } - for unstable_option_str in &rustdoc_options.unstable_opts_strs { - compiler.arg("-Z").arg(&unstable_option_str); - } - if no_run && !lang_string.compile_fail && rustdoc_options.persist_doctests.is_none() { + + if no_run && !lang_string.compile_fail && rustdoc_options.should_persist_doctests { compiler.arg("--emit=metadata"); } - compiler.arg("--target").arg(match target { + compiler.arg("--target").arg(match rustdoc_options.target { TargetTriple::TargetTriple(s) => s, TargetTriple::TargetJson { path_for_rustdoc, .. } => { path_for_rustdoc.to_str().expect("target path must be valid unicode").to_string() @@ -485,10 +524,10 @@ fn run_test( let mut cmd; let output_file = make_maybe_absolute_path(output_file); - if let Some(tool) = runtool { + if let Some(tool) = rustdoc_options.runtool { let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); - cmd.args(runtool_args); + cmd.args(rustdoc_options.runtool_args); cmd.arg(output_file); } else { cmd = Command::new(output_file); @@ -897,6 +936,56 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { (before, after, crates) } +pub(crate) struct IndividualTestOptions { + test_builder: Option<PathBuf>, + test_builder_wrappers: Vec<PathBuf>, + is_json_unused_externs_enabled: bool, + should_persist_doctests: bool, + error_format: ErrorOutputType, + test_run_directory: Option<PathBuf>, + nocapture: bool, + arg_file: PathBuf, + outdir: DirState, + runtool: Option<String>, + runtool_args: Vec<String>, + target: TargetTriple, + test_id: String, +} + +impl IndividualTestOptions { + fn new(options: &RustdocOptions, arg_file: &Path, test_id: String) -> Self { + let outdir = if let Some(ref path) = options.persist_doctests { + let mut path = path.clone(); + path.push(&test_id); + + if let Err(err) = std::fs::create_dir_all(&path) { + eprintln!("Couldn't create directory for doctest executables: {err}"); + panic::resume_unwind(Box::new(())); + } + + DirState::Perm(path) + } else { + DirState::Temp(get_doctest_dir().expect("rustdoc needs a tempdir")) + }; + + Self { + test_builder: options.test_builder.clone(), + test_builder_wrappers: options.test_builder_wrappers.clone(), + is_json_unused_externs_enabled: options.json_unused_externs.is_enabled(), + should_persist_doctests: options.persist_doctests.is_none(), + error_format: options.error_format, + test_run_directory: options.test_run_directory.clone(), + nocapture: options.nocapture, + arg_file: arg_file.into(), + outdir, + runtool: options.runtool.clone(), + runtool_args: options.runtool_args.clone(), + target: options.target.clone(), + test_id, + } + } +} + pub(crate) trait Tester { fn add_test(&mut self, test: String, config: LangString, line: usize); fn get_line(&self) -> usize { @@ -941,6 +1030,7 @@ pub(crate) struct Collector { visited_tests: FxHashMap<(String, usize), usize>, unused_extern_reports: Arc<Mutex<Vec<UnusedExterns>>>, compiling_test_count: AtomicUsize, + arg_file: PathBuf, } impl Collector { @@ -952,6 +1042,7 @@ impl Collector { source_map: Option<Lrc<SourceMap>>, filename: Option<PathBuf>, enable_per_target_ignores: bool, + arg_file: PathBuf, ) -> Collector { Collector { tests: Vec::new(), @@ -967,6 +1058,7 @@ impl Collector { visited_tests: FxHashMap::default(), unused_extern_reports: Default::default(), compiling_test_count: AtomicUsize::new(0), + arg_file, } } @@ -1009,13 +1101,9 @@ impl Tester for Collector { let crate_name = self.crate_name.clone(); let opts = self.opts.clone(); let edition = config.edition.unwrap_or(self.rustdoc_options.edition); - let rustdoc_options = self.rustdoc_options.clone(); - let runtool = self.rustdoc_options.runtool.clone(); - let runtool_args = self.rustdoc_options.runtool_args.clone(); - let target = self.rustdoc_options.target.clone(); - let target_str = target.to_string(); + let target_str = self.rustdoc_options.target.to_string(); let unused_externs = self.unused_extern_reports.clone(); - let no_run = config.no_run || rustdoc_options.no_run; + let no_run = config.no_run || self.rustdoc_options.no_run; if !config.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } @@ -1049,23 +1137,9 @@ impl Tester for Collector { self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0) }, ); - let outdir = if let Some(mut path) = rustdoc_options.persist_doctests.clone() { - path.push(&test_id); - if let Err(err) = std::fs::create_dir_all(&path) { - eprintln!("Couldn't create directory for doctest executables: {err}"); - panic::resume_unwind(Box::new(())); - } - - DirState::Perm(path) - } else { - DirState::Temp( - TempFileBuilder::new() - .prefix("rustdoctest") - .tempdir() - .expect("rustdoc needs a tempdir"), - ) - }; + let rustdoc_test_options = + IndividualTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); debug!("creating test {name}: {test}"); self.tests.push(test::TestDescAndFn { @@ -1096,17 +1170,12 @@ impl Tester for Collector { &test, &crate_name, line, - rustdoc_options, + rustdoc_test_options, config, no_run, - runtool, - runtool_args, - target, &opts, edition, - outdir, path, - &test_id, report_unused_externs, ); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 18651875130..b78fb4c4eee 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -664,7 +664,7 @@ fn usage(argv0: &str) { /// A result type used by several functions under `main()`. type MainResult = Result<(), ErrorGuaranteed>; -fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult { +pub(crate) fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult { match res { Ok(()) => dcx.has_errors().map_or(Ok(()), Err), Err(err) => Err(dcx.err(err)), @@ -731,7 +731,7 @@ fn main_args( match (options.should_test, options.markdown_input()) { (true, true) => return wrap_return(&diag, markdown::test(options)), - (true, false) => return doctest::run(options), + (true, false) => return doctest::run(&diag, options), (false, true) => { let input = options.input.clone(); let edition = options.edition; diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index b661ced0169..dcd2cf02a30 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -3,11 +3,13 @@ use std::fs::{create_dir_all, read_to_string, File}; use std::io::prelude::*; use std::path::Path; +use tempfile::tempdir; + use rustc_span::edition::Edition; use rustc_span::DUMMY_SP; use crate::config::{Options, RenderOptions}; -use crate::doctest::{Collector, GlobalTestOptions}; +use crate::doctest::{generate_args_file, Collector, GlobalTestOptions}; use crate::html::escape::Escape; use crate::html::markdown; use crate::html::markdown::{ @@ -146,6 +148,12 @@ pub(crate) fn test(options: Options) -> Result<(), String> { .map_err(|err| format!("{input}: {err}", input = options.input.display()))?; let mut opts = GlobalTestOptions::default(); opts.no_crate_inject = true; + + let temp_dir = + tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?; + let file_path = temp_dir.path().join("rustdoc-cfgs"); + generate_args_file(&file_path, &options)?; + let mut collector = Collector::new( options.input.display().to_string(), options.clone(), @@ -154,6 +162,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> { None, Some(options.input), options.enable_per_target_ignores, + file_path, ); collector.set_position(DUMMY_SP); let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 577d4b89c8d..a5c26429013 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -493,7 +493,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ty::Tuple(_) => Res::Primitive(Tuple), ty::Array(..) => Res::Primitive(Array), ty::Slice(_) => Res::Primitive(Slice), - ty::RawPtr(_) => Res::Primitive(RawPointer), + ty::RawPtr(_, _) => Res::Primitive(RawPointer), ty::Ref(..) => Res::Primitive(Reference), ty::FnDef(..) => panic!("type alias to a function definition"), ty::FnPtr(_) => Res::Primitive(Fn), diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 53c08ef5e5c..fbbb6152cfe 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -115,9 +115,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> // form that is valid for use in type inference. let ty = tcx.type_of(def_id).instantiate_identity(); match ty.kind() { - ty::Slice(ty) - | ty::Ref(_, ty, _) - | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { + ty::Slice(ty) | ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => { matches!(ty.kind(), ty::Param(..)) } ty::Tuple(tys) => tys.iter().all(|ty| matches!(ty.kind(), ty::Param(..))), diff --git a/src/llvm-project b/src/llvm-project -Subproject 84f190a4abf58bbd5301c0fc831f7a96acea246 +Subproject 0af6c732ec6ca189cd7725e4a7d4290793046e8 diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index eab9138b8fe..392a5a11967 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -126,6 +126,7 @@ static TARGETS: &[&str] = &[ "riscv32i-unknown-none-elf", "riscv32im-risc0-zkvm-elf", "riscv32im-unknown-none-elf", + "riscv32ima-unknown-none-elf", "riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf", "riscv32imafc-unknown-none-elf", diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 603f91a910b..8179e3e65b5 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -26,6 +26,12 @@ env: NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 +concurrency: + # For a given workflow, if we push to the same PR, cancel all previous builds on that PR. + # If the push is not attached to a PR, we will cancel all builds on the same branch. + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" + cancel-in-progress: true + jobs: base: # NOTE: If you modify this job, make sure you copy the changes to clippy_bors.yml @@ -33,10 +39,6 @@ jobs: steps: # Setup - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 0bc28c1f9d9..94515987eba 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -12,6 +12,11 @@ env: NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 +concurrency: + # For a given workflow, if we push to the same branch, cancel all previous builds on that branch. + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" + cancel-in-progress: true + defaults: run: shell: bash @@ -21,10 +26,6 @@ jobs: runs-on: ubuntu-latest steps: - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 with: @@ -67,10 +68,6 @@ jobs: # NOTE: If you modify this job, make sure you copy the changes to clippy.yml steps: # Setup - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 @@ -131,10 +128,6 @@ jobs: steps: # Setup - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 @@ -155,10 +148,6 @@ jobs: steps: # Setup - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 @@ -211,10 +200,6 @@ jobs: steps: # Setup - - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master - with: - github_token: "${{ secrets.github_token }}" - - name: Checkout uses: actions/checkout@v4 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index d3b2c0a7bf6..76ef84a48b8 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,11 +6,65 @@ document. ## Unreleased / Beta / In Rust Nightly -[a859e5cc...master](https://github.com/rust-lang/rust-clippy/compare/a859e5cc...master) +[66c29b97...master](https://github.com/rust-lang/rust-clippy/compare/66c29b97...master) + +## Rust 1.77 + +Current stable, released 2024-03-18 + +[View all 93 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-12-16T18%3A20%3A00Z..2024-01-25T18%3A15%3A56Z+base%3Amaster) + +### New Lints + +* [`suspicious_open_options`] + [#11608](https://github.com/rust-lang/rust-clippy/pull/11608) +* [`option_as_ref_cloned`] + [#12051](https://github.com/rust-lang/rust-clippy/pull/12051) +* [`thread_local_initializer_can_be_made_const`] + [#12026](https://github.com/rust-lang/rust-clippy/pull/12026) +* [`str_split_at_newline`] + [#11987](https://github.com/rust-lang/rust-clippy/pull/11987) +* [`empty_enum_variants_with_brackets`] + [#12047](https://github.com/rust-lang/rust-clippy/pull/12047) +* [`manual_is_variant_and`] + [#11865](https://github.com/rust-lang/rust-clippy/pull/11865) +* [`pub_underscore_fields`] + [#10283](https://github.com/rust-lang/rust-clippy/pull/10283) +* [`eager_transmute`] + [#11981](https://github.com/rust-lang/rust-clippy/pull/11981) +* [`iter_filter_is_some`] + [#12004](https://github.com/rust-lang/rust/pull/12004) +* [`iter_filter_is_ok`] + [#12004](https://github.com/rust-lang/rust/pull/12004) +* [`result_filter_map`] + [#11869](https://github.com/rust-lang/rust-clippy/pull/11869) +* [`unconditional_recursion`] + [#11938](https://github.com/rust-lang/rust-clippy/pull/11938) + +### Enhancements + +* [`multiple_crate_versions`]: Added the [`allowed-duplicate-crates`] configuration to allow specific crates + [#12179](https://github.com/rust-lang/rust-clippy/pull/12179) +* [`single_call_fn`]: No longer ignores `#[allow]` attributes + [#12183](https://github.com/rust-lang/rust-clippy/pull/12183) +* [`read_zero_byte_vec`]: Updated the heuristics used for linting + [#11766](https://github.com/rust-lang/rust-clippy/pull/11766) + +### ICE Fixes + +* [`unit_arg`]: No longer crashes when checking for const in nested bodies + [#11977](https://github.com/rust-lang/rust-clippy/pull/11977) +* [`indexing_slicing`]: No longer crashes when the array index exceeds `usize` + [#12266](https://github.com/rust-lang/rust-clippy/pull/12266) + +### Others + +* Warnings about invalid fields inside `clippy.toml` files now include suggestions for existing fields + [#12180](https://github.com/rust-lang/rust-clippy/pull/12180) ## Rust 1.76 -Current stable, released 2024-02-08 +Released 2024-02-08 [View all 85 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-11-02T20%3A23%3A40Z..2023-12-16T13%3A11%3A08Z+base%3Amaster) @@ -5110,6 +5164,7 @@ Released 2018-09-13 [`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty +[`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator [`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def @@ -5156,6 +5211,7 @@ Released 2018-09-13 [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref [`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument +[`duplicated_attributes`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicated_attributes [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec [`eager_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#eager_transmute [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else @@ -5279,6 +5335,7 @@ Released 2018-09-13 [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division +[`integer_division_remainder_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division_remainder_used [`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref [`into_iter_without_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_without_iter @@ -5376,6 +5433,7 @@ Released 2018-09-13 [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap [`manual_try_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold [`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or +[`manual_unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default [`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone @@ -5813,6 +5871,7 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr +[`zero_repeat_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_repeat_side_effects [`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values [`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 5d1d0ce2c42..2b37b54c004 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.78" +version = "0.1.79" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -24,7 +24,7 @@ path = "src/driver.rs" clippy_config = { path = "clippy_config" } clippy_lints = { path = "clippy_lints" } rustc_tools_util = "0.3.0" -tempfile = { version = "3.2", optional = true } +tempfile = { version = "3.3", optional = true } termize = "0.1" color-print = "0.3.4" anstream = "0.6.0" @@ -32,18 +32,18 @@ anstream = "0.6.0" [dev-dependencies] ui_test = "0.22.2" tester = "0.9" -regex = "1.5" +regex = "1.5.5" toml = "0.7.3" walkdir = "2.3" # This is used by the `collect-metadata` alias. -filetime = "0.2" +filetime = "0.2.9" itertools = "0.12" # UI test dependencies clippy_utils = { path = "clippy_utils" } if_chain = "1.0" -quote = "1.0" -serde = { version = "1.0.125", features = ["derive"] } +quote = "1.0.25" +serde = { version = "1.0.145", features = ["derive"] } syn = { version = "2.0", features = ["full"] } futures = "0.3" parking_lot = "0.12" diff --git a/src/tools/clippy/book/src/development/macro_expansions.md b/src/tools/clippy/book/src/development/macro_expansions.md index aecca9ef72e..125b6c4bc5b 100644 --- a/src/tools/clippy/book/src/development/macro_expansions.md +++ b/src/tools/clippy/book/src/development/macro_expansions.md @@ -52,7 +52,7 @@ if expr.span.from_expansion() { ### `Span.ctxt` method -The `span`'s context, given by the method [`ctxt`] and returning [SpanContext], +The `span`'s context, given by the method [`ctxt`] and returning [SyntaxContext], represents if the span is from a macro expansion and, if it is, which macro call expanded this span. @@ -155,4 +155,4 @@ if in_external_macro(cx.sess(), foo_span) { [`from_expansion`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion [`in_external_macro`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html [Span]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/struct.Span.html -[SpanContext]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html +[SyntaxContext]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html diff --git a/src/tools/clippy/book/src/development/type_checking.md b/src/tools/clippy/book/src/development/type_checking.md index 136b3fd0270..e6da4322a17 100644 --- a/src/tools/clippy/book/src/development/type_checking.md +++ b/src/tools/clippy/book/src/development/type_checking.md @@ -118,10 +118,10 @@ Here the HIR sees the types without "thinking" about them, it knows that the fun an `u32`. As far as `hir::Ty` is concerned those might be different types. But at the `ty::Ty` level the compiler understands that they're the same type, in-depth lifetimes, etc... -To get from a `hir::Ty` to a `ty::Ty`, you can use the [`hir_ty_to_ty`][hir_ty_to_ty] function outside of bodies or +To get from a `hir::Ty` to a `ty::Ty`, you can use the [`lower_ty`][lower_ty] function outside of bodies or the [`TypeckResults::node_type()`][node_type] method inside of bodies. -> **Warning**: Don't use `hir_ty_to_ty` inside of bodies, because this can cause ICEs. +> **Warning**: Don't use `lower_ty` inside of bodies, because this can cause ICEs. ## Creating Types programmatically @@ -162,6 +162,6 @@ in this chapter: [Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html [TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html -[middle_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty/struct.Ty.html -[hir_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/struct.Ty.html -[hir_ty_to_ty]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir_analysis/fn.hir_ty_to_ty.html +[middle_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html +[hir_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/struct.Ty.html +[lower_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/fn.lower_ty.html diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index a985346b3c0..a9234899746 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -602,6 +602,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio **Affected lints:** * [`almost_complete_range`](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range) * [`approx_constant`](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant) +* [`assigning_clones`](https://rust-lang.github.io/rust-clippy/master/index.html#assigning_clones) * [`borrow_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr) * [`cast_abs_to_unsigned`](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned) * [`checked_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions) diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index 8c405ac6a4e..62ed55beb1f 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -1,7 +1,10 @@ avoid-breaking-exported-api = false -# use the various `span_lint_*` methods instead, which also add a link to the docs -disallowed-methods = [ - "rustc_lint::context::LintContext::span_lint", - "rustc_middle::ty::context::TyCtxt::node_span_lint" -] +[[disallowed-methods]] +path = "rustc_lint::context::LintContext::span_lint" +reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" + + +[[disallowed-methods]] +path = "rustc_middle::ty::context::TyCtxt::node_span_lint" +reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 2edc5ed592c..8ba2ab56625 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.78" +version = "0.1.79" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 673b6328b39..3218fe7f456 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -262,7 +262,7 @@ define_Conf! { /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES. /// /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` #[default_text = ""] diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs index a8a32f7ed20..bf4da5f14fe 100644 --- a/src/tools/clippy/clippy_config/src/msrvs.rs +++ b/src/tools/clippy/clippy_config/src/msrvs.rs @@ -23,6 +23,7 @@ msrv_aliases! { 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } + 1,63,0 { ASSIGNING_CLONES } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 2222abff7ad..76ae26dddf4 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -689,6 +689,8 @@ fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { let mut seen_lints = HashSet::new(); let mut res: String = GENERATED_FILE_COMMENT.into(); + + res.push_str("#![allow(clippy::duplicated_attributes)]\n"); for lint in lints { if seen_lints.insert(&lint.new_name) { writeln!(res, "#![allow({})]", lint.new_name).unwrap(); diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 6ae089b3e03..1d954607eee 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.78" +version = "0.1.79" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index b1c552c7a8d..8e27b3ccefd 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -1,3 +1,4 @@ +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::HirNode; use clippy_utils::sugg::Sugg; @@ -6,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Instance, Mutability}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::def_id::DefId; use rustc_span::symbol::sym; use rustc_span::ExpnKind; @@ -49,7 +50,19 @@ declare_clippy_lint! { perf, "assigning the result of cloning may be inefficient" } -declare_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); + +pub struct AssigningClones { + msrv: Msrv, +} + +impl AssigningClones { + #[must_use] + pub fn new(msrv: Msrv) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx hir::Expr<'_>) { @@ -68,10 +81,12 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { return; }; - if is_ok_to_suggest(cx, lhs, &call) { + if is_ok_to_suggest(cx, lhs, &call, &self.msrv) { suggest(cx, assign_expr, lhs, &call); } } + + extract_msrv_attr!(LateContext); } // Try to resolve the call to `Clone::clone` or `ToOwned::to_owned`. @@ -135,14 +150,20 @@ fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option< // Return true if we find that the called method has a custom implementation and isn't derived or // provided by default by the corresponding trait. -fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) -> bool { +fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>, msrv: &Msrv) -> bool { + // For calls to .to_owned we suggest using .clone_into(), which was only stablilized in 1.63. + // If the current MSRV is below that, don't suggest the lint. + if !msrv.meets(msrvs::ASSIGNING_CLONES) && matches!(call.target, TargetTrait::ToOwned) { + return false; + } + // If the left-hand side is a local variable, it might be uninitialized at this point. // In that case we do not want to suggest the lint. if let Some(local) = path_to_local(lhs) { // TODO: This check currently bails if the local variable has no initializer. // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. - if let Some(Node::Local(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p)) + if let Some(Node::LetStmt(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p)) && local.init.is_none() { return false; diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs new file mode 100644 index 00000000000..3c5ac597fd5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -0,0 +1,64 @@ +use super::DUPLICATED_ATTRIBUTES; +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::{Attribute, MetaItem}; +use rustc_data_structures::fx::FxHashMap; +use rustc_lint::EarlyContext; +use rustc_span::{sym, Span}; +use std::collections::hash_map::Entry; + +fn emit_if_duplicated( + cx: &EarlyContext<'_>, + attr: &MetaItem, + attr_paths: &mut FxHashMap<String, Span>, + complete_path: String, +) { + match attr_paths.entry(complete_path) { + Entry::Vacant(v) => { + v.insert(attr.span); + }, + Entry::Occupied(o) => { + span_lint_and_then(cx, DUPLICATED_ATTRIBUTES, attr.span, "duplicated attribute", |diag| { + diag.span_note(*o.get(), "first defined here"); + diag.span_help(attr.span, "remove this attribute"); + }); + }, + } +} + +fn check_duplicated_attr( + cx: &EarlyContext<'_>, + attr: &MetaItem, + attr_paths: &mut FxHashMap<String, Span>, + parent: &mut Vec<String>, +) { + let Some(ident) = attr.ident() else { return }; + let name = ident.name; + if name == sym::doc || name == sym::cfg_attr { + // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg + // conditions are the same. + return; + } + if let Some(value) = attr.value_str() { + emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}={value}", parent.join(":"))); + } else if let Some(sub_attrs) = attr.meta_item_list() { + parent.push(name.as_str().to_string()); + for sub_attr in sub_attrs { + if let Some(meta) = sub_attr.meta_item() { + check_duplicated_attr(cx, meta, attr_paths, parent); + } + } + parent.pop(); + } else { + emit_if_duplicated(cx, attr, attr_paths, format!("{}:{name}", parent.join(":"))); + } +} + +pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { + let mut attr_paths = FxHashMap::default(); + + for attr in attrs { + if let Some(meta) = attr.meta() { + check_duplicated_attr(cx, &meta, &mut attr_paths, &mut Vec::new()); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index c4c65d3248a..675c428948f 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -4,6 +4,7 @@ mod allow_attributes_without_reason; mod blanket_clippy_restriction_lints; mod deprecated_cfg_attr; mod deprecated_semver; +mod duplicated_attributes; mod empty_line_after; mod inline_always; mod maybe_misused_cfg; @@ -16,7 +17,7 @@ mod useless_attribute; mod utils; use clippy_config::msrvs::Msrv; -use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem}; +use rustc_ast::{Attribute, Crate, MetaItemKind, NestedMetaItem}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, impl_lint_pass}; @@ -489,6 +490,32 @@ declare_clippy_lint! { "item has both inner and outer attributes" } +declare_clippy_lint! { + /// ### What it does + /// Checks for attributes that appear two or more times. + /// + /// ### Why is this bad? + /// Repeating an attribute on the same item (or globally on the same crate) + /// is unnecessary and doesn't have an effect. + /// + /// ### Example + /// ```no_run + /// #[allow(dead_code)] + /// #[allow(dead_code)] + /// fn foo() {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// #[allow(dead_code)] + /// fn foo() {} + /// ``` + #[clippy::version = "1.78.0"] + pub DUPLICATED_ATTRIBUTES, + suspicious, + "duplicated attribute" +} + declare_lint_pass!(Attributes => [ ALLOW_ATTRIBUTES_WITHOUT_REASON, INLINE_ALWAYS, @@ -568,12 +595,18 @@ impl_lint_pass!(EarlyAttributes => [ DEPRECATED_CLIPPY_CFG_ATTR, UNNECESSARY_CLIPPY_CFG, MIXED_ATTRIBUTES_STYLE, + DUPLICATED_ATTRIBUTES, ]); impl EarlyLintPass for EarlyAttributes { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + duplicated_attributes::check(cx, &krate.attrs); + } + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) { empty_line_after::check(cx, item); mixed_attributes_style::check(cx, item); + duplicated_attributes::check(cx, &item.attrs); } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 779ae03c464..8683cb86e8a 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -6,7 +6,7 @@ use clippy_utils::{is_default_equivalent, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_ty, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, Local, Node, QPath, Ty, TyKind}; +use rustc_hir::{Block, Expr, ExprKind, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::print::with_forced_trimmed_paths; @@ -70,7 +70,9 @@ impl LateLintPass<'_> for BoxDefault { "try", if is_plain_default(cx, arg_path) || given_type(cx, expr) { "Box::default()".into() - } else if let Some(arg_ty) = cx.typeck_results().expr_ty(arg).make_suggestable(cx.tcx, true) { + } else if let Some(arg_ty) = + cx.typeck_results().expr_ty(arg).make_suggestable(cx.tcx, true, None) + { // Check if we can copy from the source expression in the replacement. // We need the call to have no argument (see `explicit_default_type`). if inner_call_args.is_empty() @@ -137,7 +139,7 @@ impl<'tcx> Visitor<'tcx> for InferVisitor { fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match cx.tcx.parent_hir_node(expr.hir_id) { - Node::Local(Local { ty: Some(ty), .. }) => { + Node::LetStmt(LetStmt { ty: Some(ty), .. }) => { let mut v = InferVisitor::default(); v.visit_ty(ty); !v.0 diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs index 8bfb7383f14..a667ea04af0 100644 --- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -4,18 +4,13 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; -use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_middle::ty::{self, Ty}; use super::AS_PTR_CAST_MUT; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) { - if let ty::RawPtr(TypeAndMut { - mutbl: Mutability::Mut, - ty: ptrty, - }) = cast_to.kind() - && let ty::RawPtr(TypeAndMut { - mutbl: Mutability::Not, .. - }) = cx.typeck_results().node_type(cast_expr.hir_id).kind() + if let ty::RawPtr(ptrty, Mutability::Mut) = cast_to.kind() + && let ty::RawPtr(_, Mutability::Not) = cx.typeck_results().node_type(cast_expr.hir_id).kind() && let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind && method_name.ident.name == rustc_span::sym::as_ptr && let Some(as_ptr_did) = cx diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs index fe2455f4b23..86f4332d05a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -1,12 +1,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::in_constant; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_middle::ty::{self, FloatTy, Ty, UintTy}; use super::{utils, CAST_LOSSLESS}; @@ -16,6 +16,7 @@ pub(super) fn check( cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, + cast_to_hir: &rustc_hir::Ty<'_>, msrv: &Msrv, ) { if !should_lint(cx, expr, cast_from, cast_to, msrv) { @@ -24,11 +25,11 @@ pub(super) fn check( // The suggestion is to use a function call, so if the original expression // has parens on the outside, they are no longer needed. - let mut applicability = Applicability::MachineApplicable; + let mut app = Applicability::MachineApplicable; let opt = snippet_opt(cx, cast_op.span.source_callsite()); let sugg = opt.as_ref().map_or_else( || { - applicability = Applicability::HasPlaceholders; + app = Applicability::HasPlaceholders; ".." }, |snip| { @@ -40,10 +41,27 @@ pub(super) fn check( }, ); + // Display the type alias instead of the aliased type. Fixes #11285 + // + // FIXME: Once `lazy_type_alias` is stabilized(?) we should use `rustc_middle` types instead, + // this will allow us to display the right type with `cast_from` as well. + let cast_to_fmt = if let TyKind::Path(QPath::Resolved(None, path)) = cast_to_hir.kind + // It's a bit annoying but the turbofish is optional for types. A type in an `as` cast + // shouldn't have these if they're primitives, which are the only things we deal with. + // + // This could be removed for performance if this check is determined to have a pretty major + // effect. + && path.segments.iter().all(|segment| segment.args.is_none()) + { + snippet_with_applicability(cx, cast_to_hir.span, "..", &mut app) + } else { + cast_to.to_string().into() + }; + let message = if cast_from.is_bool() { - format!("casting `{cast_from:}` to `{cast_to:}` is more cleanly stated with `{cast_to:}::from(_)`") + format!("casting `{cast_from}` to `{cast_to_fmt}` is more cleanly stated with `{cast_to_fmt}::from(_)`") } else { - format!("casting `{cast_from}` to `{cast_to}` may become silently lossy if you later change the type") + format!("casting `{cast_from}` to `{cast_to_fmt}` may become silently lossy if you later change the type") }; span_lint_and_sugg( @@ -52,14 +70,17 @@ pub(super) fn check( expr.span, &message, "try", - format!("{cast_to}::from({sugg})"), - applicability, + format!("{cast_to_fmt}::from({sugg})"), + app, ); } fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). - if in_constant(cx, expr.hir_id) { + // + // If destination is u128, do not lint because source type cannot be larger + // If source is bool, still lint due to the lint message differing (refers to style) + if in_constant(cx, expr.hir_id) || (!cast_from.is_bool() && matches!(cast_to.kind(), ty::Uint(UintTy::U128))) { return false; } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index f12f03fbe79..4d1a0f678f4 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -33,13 +33,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { } fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { - if let ty::RawPtr(from_ptr_ty) = &cast_from.kind() - && let ty::RawPtr(to_ptr_ty) = &cast_to.kind() - && let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty) - && let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty) + if let ty::RawPtr(from_ptr_ty, _) = *cast_from.kind() + && let ty::RawPtr(to_ptr_ty, _) = *cast_to.kind() + && let Ok(from_layout) = cx.layout_of(from_ptr_ty) + && let Ok(to_layout) = cx.layout_of(to_ptr_ty) && from_layout.align.abi < to_layout.align.abi // with c_void, we inherently need to trust the user - && !is_c_void(cx, from_ptr_ty.ty) + && !is_c_void(cx, from_ptr_ty) // when casting from a ZST, we don't know enough to properly lint && !from_layout.is_zst() && !is_used_as_unaligned(cx, expr) diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs index a31943f0021..76d5c329179 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -87,7 +87,7 @@ fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// the type is one of those slices fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> { match ty.kind() { - ty::RawPtr(TypeAndMut { ty: slice_ty, mutbl }) => match slice_ty.kind() { + ty::RawPtr(slice_ty, mutbl) => match slice_ty.kind() { ty::Slice(ty) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }), _ => None, }, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 3db1e3e6d97..48629b6c5cc 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -25,8 +25,8 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> { pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) { if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) - && let ty::RawPtr(ptrty) = cast_to.kind() - && let ty::Slice(_) = ptrty.ty.kind() + && let ty::RawPtr(ptrty, _) = cast_to.kind() + && let ty::Slice(_) = ptrty.kind() && let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind && let ExprKind::Path(ref qpath) = fun.kind && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index 14f2f4a7f59..063aab28238 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -791,7 +791,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); + cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir, &self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs index 35e36e9ef3f..5a121e6a7eb 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, QPath, TyKind}; use rustc_hir_pretty::qpath_to_string; use rustc_lint::LateContext; -use rustc_middle::ty::{self, TypeAndMut}; +use rustc_middle::ty; use rustc_span::sym; use super::PTR_AS_PTR; @@ -33,8 +33,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind && let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)) - && let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind() - && let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind() + && let ty::RawPtr(_, from_mutbl) = cast_from.kind() + && let ty::RawPtr(to_pointee_ty, to_mutbl) = cast_to.kind() && matches!((from_mutbl, to_mutbl), (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)) // The `U` in `pointer::cast` have to be `Sized` diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs index ff069860a11..e88146331ca 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -4,7 +4,7 @@ use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_middle::ty::{self, Ty}; use super::PTR_CAST_CONSTNESS; @@ -17,14 +17,8 @@ pub(super) fn check<'tcx>( msrv: &Msrv, ) { if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) - && let ty::RawPtr(TypeAndMut { - mutbl: from_mutbl, - ty: from_ty, - }) = cast_from.kind() - && let ty::RawPtr(TypeAndMut { - mutbl: to_mutbl, - ty: to_ty, - }) = cast_to.kind() + && let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() + && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind() && matches!( (from_mutbl, to_mutbl), (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not) diff --git a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs index 9d5a486336d..f42bafce4dd 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs @@ -5,7 +5,7 @@ use clippy_utils::{expr_use_ctxt, is_no_std_crate, ExprUseNode}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, TypeAndMut}; +use rustc_middle::ty; use super::REF_AS_PTR; @@ -21,10 +21,10 @@ pub(super) fn check<'tcx>( ); if matches!(cast_from.kind(), ty::Ref(..)) - && let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, .. }) = cast_to.kind() + && let ty::RawPtr(_, to_mutbl) = cast_to.kind() && let Some(use_cx) = expr_use_ctxt(cx, expr) // TODO: only block the lint if `cast_expr` is a temporary - && !matches!(use_cx.node, ExprUseNode::Local(_) | ExprUseNode::ConstStatic(_)) + && !matches!(use_cx.node, ExprUseNode::LetStmt(_) | ExprUseNode::ConstStatic(_)) { let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; let fn_name = match to_mutbl { diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 08341ff32f3..148d52cb5dd 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -66,7 +66,7 @@ pub(super) fn check<'tcx>( && let QPath::Resolved(None, Path { res, .. }) = qpath && let Res::Local(hir_id) = res && let parent = cx.tcx.parent_hir_node(*hir_id) - && let Node::Local(local) = parent + && let Node::LetStmt(local) = parent { if let Some(ty) = local.ty && let TyKind::Path(qpath) = ty.kind @@ -275,7 +275,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx } // Local usage } else if let Res::Local(hir_id) = res - && let Node::Local(l) = cx.tcx.parent_hir_node(hir_id) + && let Node::LetStmt(l) = cx.tcx.parent_hir_node(hir_id) { if let Some(e) = l.init && is_cast_from_ty_alias(cx, e, cast_from) diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index d820413e111..e921b9b46a6 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::for_each_expr_with_closures; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; -use rustc_hir::{Block, ExprKind, HirId, LangItem, Local, Node, PatKind}; +use rustc_hir::{Block, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -58,7 +58,7 @@ static COLLECTIONS: [Symbol; 9] = [ ]; impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { // Look for local variables whose type is a container. Search surrounding bock for read access. if match_acceptable_type(cx, local, &COLLECTIONS) && let PatKind::Binding(_, local_id, _, _) = local.pat.kind @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { } } -fn match_acceptable_type(cx: &LateContext<'_>, local: &Local<'_>, collections: &[rustc_span::Symbol]) -> bool { +fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections: &[rustc_span::Symbol]) -> bool { let ty = cx.typeck_results().pat_ty(local.pat); collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym)) // String type is a lang item but not a diagnostic item for now so we need a separate check diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index ec66556cebf..e2296767431 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -1,12 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::macros::{macro_backtrace, MacroCall}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_in_cfg_test, is_in_test_function}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node}; +use rustc_hir::{Expr, ExprKind, HirId, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::sym; +use rustc_span::{sym, Span, SyntaxContext}; declare_clippy_lint! { /// ### What it does @@ -31,31 +33,38 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } -#[derive(Copy, Clone)] +#[derive(Clone)] pub struct DbgMacro { allow_dbg_in_tests: bool, + /// Tracks the `dbg!` macro callsites that are already checked. + checked_dbg_call_site: FxHashSet<Span>, + /// Tracks the previous `SyntaxContext`, to avoid walking the same context chain. + prev_ctxt: SyntaxContext, } impl_lint_pass!(DbgMacro => [DBG_MACRO]); impl DbgMacro { pub fn new(allow_dbg_in_tests: bool) -> Self { - DbgMacro { allow_dbg_in_tests } + DbgMacro { + allow_dbg_in_tests, + checked_dbg_call_site: FxHashSet::default(), + prev_ctxt: SyntaxContext::root(), + } } } impl LateLintPass<'_> for DbgMacro { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { - return; - }; - if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) { + let cur_syntax_ctxt = expr.span.ctxt(); + + if cur_syntax_ctxt != self.prev_ctxt && + let Some(macro_call) = first_dbg_macro_in_expansion(cx, expr.span) && + !in_external_macro(cx.sess(), macro_call.span) && + self.checked_dbg_call_site.insert(macro_call.span) && // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml - if self.allow_dbg_in_tests - && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) - { - return; - } + !(self.allow_dbg_in_tests && is_in_test(cx, expr.hir_id)) + { let mut applicability = Applicability::MachineApplicable; let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { @@ -101,6 +110,8 @@ impl LateLintPass<'_> for DbgMacro { _ => return, }; + self.prev_ctxt = cur_syntax_ctxt; + span_lint_and_sugg( cx, DBG_MACRO, @@ -112,4 +123,16 @@ impl LateLintPass<'_> for DbgMacro { ); } } + + fn check_crate_post(&mut self, _: &LateContext<'_>) { + self.checked_dbg_call_site = FxHashSet::default(); + } +} + +fn is_in_test(cx: &LateContext<'_>, hir_id: HirId) -> bool { + is_in_test_function(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id) +} + +fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<MacroCall> { + macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 2b324f5f96e..c8e148598a2 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -54,6 +54,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::attrs::DEPRECATED_CFG_ATTR_INFO, crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO, crate::attrs::DEPRECATED_SEMVER_INFO, + crate::attrs::DUPLICATED_ATTRIBUTES_INFO, crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::attrs::INLINE_ALWAYS_INFO, @@ -235,6 +236,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, crate::int_plus_one::INT_PLUS_ONE_INFO, + crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO, crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO, crate::item_name_repetitions::MODULE_INCEPTION_INFO, @@ -310,6 +312,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO, crate::manual_string_new::MANUAL_STRING_NEW_INFO, crate::manual_strip::MANUAL_STRIP_INFO, + crate::manual_unwrap_or_default::MANUAL_UNWRAP_OR_DEFAULT_INFO, crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO, crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO, crate::match_result_ok::MATCH_RESULT_OK_INFO, @@ -353,6 +356,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::CLONE_ON_COPY_INFO, crate::methods::CLONE_ON_REF_PTR_INFO, crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, + crate::methods::CONST_IS_EMPTY_INFO, crate::methods::DRAIN_COLLECT_INFO, crate::methods::ERR_EXPECT_INFO, crate::methods::EXPECT_FUN_CALL_INFO, @@ -750,5 +754,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::write::WRITE_LITERAL_INFO, crate::write::WRITE_WITH_NEWLINE_INFO, crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, + crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO, crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, ]; diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index f0f2c7d6658..c554edc8fce 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -11,7 +11,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, ToPredicate, TraitPredicate, Ty, + self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, ToPredicate, TraitPredicate, Ty, TyCtxt, }; use rustc_session::declare_lint_pass; @@ -502,7 +502,7 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { ClauseKind::Trait(TraitPredicate { trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]), - polarity: ImplPolarity::Positive, + polarity: ty::PredicatePolarity::Positive, }) .to_predicate(tcx) }), diff --git a/src/tools/clippy/clippy_lints/src/doc/markdown.rs b/src/tools/clippy/clippy_lints/src/doc/markdown.rs index d2f14756591..1add02af310 100644 --- a/src/tools/clippy/clippy_lints/src/doc/markdown.rs +++ b/src/tools/clippy/clippy_lints/src/doc/markdown.rs @@ -8,7 +8,14 @@ use url::Url; use crate::doc::DOC_MARKDOWN; -pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span, code_level: isize) { +pub fn check( + cx: &LateContext<'_>, + valid_idents: &FxHashSet<String>, + text: &str, + span: Span, + code_level: isize, + blockquote_level: isize, +) { for orig_word in text.split(|c: char| c.is_whitespace() || c == '\'') { // Trim punctuation as in `some comment (see foo::bar).` // ^^ @@ -46,11 +53,11 @@ pub fn check(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span.parent(), ); - check_word(cx, word, span, code_level); + check_word(cx, word, span, code_level, blockquote_level); } } -fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize) { +fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, blockquote_level: isize) { /// Checks if a string is upper-camel-case, i.e., starts with an uppercase and /// contains at least two uppercase letters (`Clippy` is ok) and one lower-case /// letter (`NASA` is ok). @@ -97,7 +104,9 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize) { } // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343) - if code_level > 0 || (has_underscore(word) && has_hyphen(word)) { + // + // We also assume that backticks are not necessary if inside a quote. (Issue #10262) + if code_level > 0 || blockquote_level > 0 || (has_underscore(word) && has_hyphen(word)) { return; } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 003d26b7b89..b135e4e3577 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -7,14 +7,14 @@ use clippy_utils::{is_entrypoint_fn, method_chain_args}; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; -use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; +use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Expr}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -538,7 +538,16 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[ suspicious_doc_comments::check(cx, attrs); - let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); + let (fragments, _) = attrs_to_doc_fragments( + attrs.iter().filter_map(|attr| { + if in_external_macro(cx.sess(), attr.span) { + None + } else { + Some((attr, None)) + } + }), + true, + ); let mut doc = fragments.iter().fold(String::new(), |mut acc, fragment| { add_doc_fragment(&mut acc, fragment); acc @@ -602,6 +611,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize let mut text_to_check: Vec<(CowStr<'_>, Range<usize>, isize)> = Vec::new(); let mut paragraph_range = 0..0; let mut code_level = 0; + let mut blockquote_level = 0; for (event, range) in events { match event { @@ -610,8 +620,14 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize code_level += 1; } else if tag.starts_with("</code") { code_level -= 1; + } else if tag.starts_with("<blockquote") || tag.starts_with("<q") { + blockquote_level += 1; + } else if tag.starts_with("</blockquote") || tag.starts_with("</q") { + blockquote_level -= 1; } }, + Start(BlockQuote) => blockquote_level += 1, + End(BlockQuote) => blockquote_level -= 1, Start(CodeBlock(ref kind)) => { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { @@ -663,7 +679,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize } else { for (text, range, assoc_code_level) in text_to_check { if let Some(span) = fragments.span(cx, range) { - markdown::check(cx, valid_idents, &text, span, assoc_code_level); + markdown::check(cx, valid_idents, &text, span, assoc_code_level, blockquote_level); } } } diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs index 47780cab9ed..a6ca7fe9e0b 100644 --- a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs +++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs @@ -49,24 +49,22 @@ declare_clippy_lint! { declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]); impl EarlyLintPass for ElseIfWithoutElse { - fn check_expr(&mut self, cx: &EarlyContext<'_>, mut item: &Expr) { + fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { if in_external_macro(cx.sess(), item.span) { return; } - while let ExprKind::If(_, _, Some(ref els)) = item.kind { - if let ExprKind::If(_, _, None) = els.kind { - span_lint_and_help( - cx, - ELSE_IF_WITHOUT_ELSE, - els.span, - "`if` expression with an `else if`, but without a final `else`", - None, - "add an `else` block here", - ); - } - - item = els; + if let ExprKind::If(_, _, Some(ref els)) = item.kind + && let ExprKind::If(_, _, None) = els.kind + { + span_lint_and_help( + cx, + ELSE_IF_WITHOUT_ELSE, + els.span, + "`if` expression with an `else if`, but without a final `else`", + None, + "add an `else` block here", + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index ebda2ad8387..dafbf6c8846 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -358,7 +358,7 @@ struct InsertSearcher<'cx, 'tcx> { can_use_entry: bool, /// Whether this expression is the final expression in this code path. This may be a statement. in_tail_pos: bool, - // Is this expression a single insert. A slightly better suggestion can be made in this case. + /// Is this expression a single insert. A slightly better suggestion can be made in this case. is_single_insert: bool, /// If the visitor has seen the map being used. is_map_used: bool, @@ -431,6 +431,9 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { self.is_single_insert = false; self.visit_expr(e); } + if let Some(els) = &l.els { + self.visit_block(els); + } }, StmtKind::Item(_) => { self.allow_insert_closure &= !self.in_tail_pos; diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 4e728d61b85..37442bf3e28 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -55,7 +55,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { | PatKind::Err(_) => false, PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), - PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x), + PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) => unary_pattern(x), PatKind::Path(_) | PatKind::Lit(_) => true, } } diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 40be71a0e5d..eccfc31fdd3 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -9,7 +9,7 @@ use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, QPat use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, ImplPolarity, List, Region, RegionKind, + self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults, }; use rustc_session::declare_lint_pass; @@ -173,7 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if let Ok((ClosureKind::FnMut, _)) = cx.tcx.infer_ctxt().build().type_implements_fn_trait( cx.param_env, Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ImplPolarity::Positive, + ty::PredicatePolarity::Positive, ) && path_to_local(callee).map_or(false, |l| { local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) }) { diff --git a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs index c8d10dc4b92..286ba2306c9 100644 --- a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::is_c_void; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{RawPtr, TypeAndMut}; +use rustc_middle::ty::{RawPtr}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -44,7 +44,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr { && seg.ident.name == sym!(from_raw) && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) && let arg_kind = cx.typeck_results().expr_ty(arg).kind() - && let RawPtr(TypeAndMut { ty, .. }) = arg_kind + && let RawPtr(ty, _) = arg_kind && is_c_void(cx, *ty) { let msg = format!("creating a `{type_str}` from a void raw pointer"); diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 96da2ec2a1a..9cc51fa8cd5 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -250,7 +250,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// A `Result` is at least as large as the `Err`-variant. While we - /// expect that variant to be seldomly used, the compiler needs to reserve + /// expect that variant to be seldom used, the compiler needs to reserve /// and move that much memory every single time. /// Furthermore, errors are often simply passed up the call-stack, making /// use of the `?`-operator and its type-conversion mechanics. If the diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 3aaf63ce340..d752d010f9f 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -207,7 +207,7 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) }, ty::Tuple(args) => args.iter().any(|ty| is_mutable_ty(cx, ty, tys)), ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, tys), - ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { + ty::RawPtr(ty, mutbl) | ty::Ref(_, ty, mutbl) => { mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys) }, // calling something constitutes a side effect, so return true on all callables diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index f2aa7b597a7..2d757883f26 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -75,7 +75,7 @@ fn check_raw_ptr<'tcx>( } fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<hir::HirId> { - if let (&hir::PatKind::Binding(_, id, _, _), Some(&ty::RawPtr(_))) = ( + if let (&hir::PatKind::Binding(_, id, _, _), Some(&ty::RawPtr(_, _))) = ( &arg.pat.kind, cx.maybe_typeck_results() .map(|typeck_results| typeck_results.pat_ty(arg.pat).kind()), diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index 9fb59a320d4..18f4e51ebd6 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { } let ret_ty = return_ty(cx, cx.tcx.local_def_id_to_hir_id(fn_def_id).expect_owner()); if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() { - let preds = cx.tcx.explicit_item_bounds(def_id); + let preds = cx.tcx.explicit_item_super_predicates(def_id); let mut is_future = false; for (p, _span) in preds.iter_instantiated_copied(cx.tcx, args) { if let Some(trait_pred) = p.as_trait_clause() { diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs index 80a537b9f94..a32201d8079 100644 --- a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { // Ignore function parameters return; }, - Node::Local(local) if local.ty.is_some() => { + Node::LetStmt(local) if local.ty.is_some() => { // Ignore let bindings with explicit type return; }, diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index a79bf66ae01..8acb138332c 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -5,7 +5,7 @@ use rustc_errors::Diag; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{Ty, TypeckResults}; @@ -227,7 +227,7 @@ impl<'tcx> ImplicitHasherType<'tcx> { .collect(); let params_len = params.len(); - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = lower_ty(cx.tcx, hir_ty); if is_type_diagnostic_item(cx, ty, sym::HashMap) && params_len == 2 { Some(ImplicitHasherType::HashMap( diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 74582f7f1de..9f4d7b51271 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -6,7 +6,7 @@ use rustc_hir::{ GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier, TyKind, TypeBinding, WherePredicate, }; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, ClauseKind, Generics, Ty, TyCtxt}; use rustc_session::declare_lint_pass; @@ -146,7 +146,7 @@ fn try_resolve_type<'tcx>( index: usize, ) -> Option<Ty<'tcx>> { match args.get(index - 1) { - Some(GenericArg::Type(ty)) => Some(hir_ty_to_ty(tcx, ty)), + Some(GenericArg::Type(ty)) => Some(lower_ty(tcx, ty)), Some(_) => None, None => Some(tcx.type_of(generics.params[index].def_id).skip_binder()), } diff --git a/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs b/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs new file mode 100644 index 00000000000..36dc45ca788 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs @@ -0,0 +1,50 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::BinOpKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of division (/) and remainder (%) operations + /// when performed on any integer types using the default Div and Rem trait implementations. + /// + /// ### Why is this bad? + /// In cryptographic contexts, division can result in timing sidechannel vulnerabilities, + /// and needs to be replaced with constant-time code instead (e.g. Barrett reduction). + /// + /// ### Example + /// ```no_run + /// let my_div = 10 / 2; + /// ``` + /// Use instead: + /// ```no_run + /// let my_div = 10 >> 1; + /// ``` + #[clippy::version = "1.78.0"] + pub INTEGER_DIVISION_REMAINDER_USED, + restriction, + "use of disallowed default division and remainder operations" +} + +declare_lint_pass!(IntegerDivisionRemainderUsed => [INTEGER_DIVISION_REMAINDER_USED]); + +impl LateLintPass<'_> for IntegerDivisionRemainderUsed { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Binary(op, lhs, rhs) = &expr.kind + && let BinOpKind::Div | BinOpKind::Rem = op.node + && let lhs_ty = cx.typeck_results().expr_ty(lhs) + && let rhs_ty = cx.typeck_results().expr_ty(rhs) + && let ty::Int(_) | ty::Uint(_) = lhs_ty.peel_refs().kind() + && let ty::Int(_) | ty::Uint(_) = rhs_ty.peel_refs().kind() + { + span_lint( + cx, + INTEGER_DIVISION_REMAINDER_USED, + expr.span.source_callsite(), + &format!("use of {} has been disallowed in this context", op.node.as_str()), + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs index f084d89ccc2..d4ddf76fb8a 100644 --- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs +++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::path_to_local_id; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; @@ -122,9 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { value=snippet(cx, value.span, "<value>"), default=snippet(cx, default.span, "<default>"), ); - span_lint_and_then( + span_lint_hir_and_then( cx, USELESS_LET_IF_SEQ, + local.hir_id, span, "`if _ { .. } else { .. }` is an expression", |diag| { diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 0ea53c39280..619e933b4ff 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths}; -use rustc_hir::{Local, LocalSource, PatKind}; +use rustc_hir::{LetStmt, LocalSource, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{GenericArgKind, IsSuggestable}; @@ -138,7 +138,7 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [ ]; impl<'tcx> LateLintPass<'tcx> for LetUnderscore { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if matches!(local.source, LocalSource::Normal) && !in_external_macro(cx.tcx.sess, local.span) && let PatKind::Wild = local.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index 5f3f9b43f45..593b29154b4 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; -use rustc_hir::{Local, TyKind}; +use rustc_hir::{LetStmt, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; @@ -26,7 +26,7 @@ declare_clippy_lint! { declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); impl LateLintPass<'_> for UnderscoreTyped { - fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &LetStmt<'_>) { if !in_external_macro(cx.tcx.sess, local.span) && let Some(ty) = local.ty // Ensure that it has a type defined && let TyKind::Infer = &ty.kind // that type is '_' diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index b930175c4d8..57fac351042 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -172,6 +172,7 @@ mod init_numbered_fields; mod inline_fn_without_body; mod instant_subtraction; mod int_plus_one; +mod integer_division_remainder_used; mod invalid_upcast_comparisons; mod item_name_repetitions; mod items_after_statements; @@ -211,6 +212,7 @@ mod manual_retain; mod manual_slice_size_calculation; mod manual_string_new; mod manual_strip; +mod manual_unwrap_or_default; mod map_unit_fn; mod match_result_ok; mod matches; @@ -373,6 +375,7 @@ mod visibility; mod wildcard_imports; mod write; mod zero_div_zero; +mod zero_repeat_side_effects; mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` @@ -1119,7 +1122,10 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); - store.register_late_pass(|_| Box::new(assigning_clones::AssigningClones)); + store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(msrv()))); + store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); + store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); + store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index 814ccaa36f5..eea5f2a94ea 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -10,7 +10,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, EarlyBinder, Ty, TypeAndMut}; +use rustc_middle::ty::{self, EarlyBinder, Ty}; use rustc_span::sym; pub(super) fn check( @@ -160,7 +160,7 @@ fn is_ref_iterable<'tcx>( let self_ty = if mutbl.is_mut() { self_ty } else { - Ty::new_ref(cx.tcx, region, TypeAndMut { ty, mutbl }) + Ty::new_ref(cx.tcx, region, ty, mutbl) }; if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = @@ -175,7 +175,7 @@ fn is_ref_iterable<'tcx>( && !self_ty.is_ref() { // Attempt to borrow - let self_ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, TypeAndMut { ty: self_ty, mutbl }); + let self_ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, self_ty, mutbl); if implements_trait(cx, self_ty, trait_id, &[]) && let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty]) && ty == res_ty diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 47dc3807e62..cf34c904dfe 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -273,7 +273,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { } return false; // no need to walk further *on the variable* }, - Res::Def(DefKind::Static{..} | DefKind::Const, ..) => { + Res::Def(DefKind::Static { .. } | DefKind::Const, ..) => { if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index 6cc79440f39..8aae7be4593 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -255,9 +255,7 @@ fn never_loop_expr<'tcx>( InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { NeverLoopResult::Normal }, - InlineAsmOperand::Label { block } => { - never_loop_block(cx, block, local_labels, main_loop_id) - } + InlineAsmOperand::Label { block } => never_loop_block(cx, block, local_labels, main_loop_id), })), ExprKind::OffsetOf(_, _) | ExprKind::Yield(_, _) diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index 0f35514b8ad..670a78d58c3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( if let Node::Pat(pat) = node && let PatKind::Binding(bind_ann, ..) = pat.kind && !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)) - && let Node::Local(parent_let_expr) = cx.tcx.parent_hir_node(hir_id) + && let Node::LetStmt(parent_let_expr) = cx.tcx.parent_hir_node(hir_id) && let Some(init) = parent_let_expr.init { match init.kind { diff --git a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs index dd7fae79d9b..31f0f1cfeba 100644 --- a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,62 +1,41 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; -use clippy_utils::{pat_is_wild, sugg}; +use clippy_utils::{match_def_path, pat_is_wild, sugg}; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; /// Checks for the `UNUSED_ENUMERATE_INDEX` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { - let PatKind::Tuple([index, elem], _) = pat.kind else { - return; - }; - - let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind else { - return; - }; - - let ty = cx.typeck_results().expr_ty(arg); - - if !pat_is_wild(cx, &index.kind, body) { - return; - } - - let name = match *ty.kind() { - ty::Adt(base, _substs) => cx.tcx.def_path_str(base.did()), - _ => return, - }; - - if name != "std::iter::Enumerate" && name != "core::iter::Enumerate" { - return; +/// +/// The lint is also partially implemented in `clippy_lints/src/methods/unused_enumerate_index.rs`. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_>, body: &'tcx Expr<'tcx>) { + if let PatKind::Tuple([index, elem], _) = pat.kind + && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind + && let ty = cx.typeck_results().expr_ty(arg) + && pat_is_wild(cx, &index.kind, body) + && let ty::Adt(base, _) = *ty.kind() + && match_def_path(cx, base.did(), &clippy_utils::paths::CORE_ITER_ENUMERATE_STRUCT) + && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) + && match_def_path(cx, call_id, &clippy_utils::paths::CORE_ITER_ENUMERATE_METHOD) + { + span_lint_and_then( + cx, + UNUSED_ENUMERATE_INDEX, + arg.span, + "you seem to use `.enumerate()` and immediately discard the index", + |diag| { + let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter"); + multispan_sugg( + diag, + "remove the `.enumerate()` call", + vec![ + (pat.span, snippet(cx, elem.span, "..").into_owned()), + (arg.span, base_iter.to_string()), + ], + ); + }, + ); } - - let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) else { - return; - }; - - let call_name = cx.tcx.def_path_str(call_id); - - if call_name != "std::iter::Iterator::enumerate" && call_name != "core::iter::Iterator::enumerate" { - return; - } - - span_lint_and_then( - cx, - UNUSED_ENUMERATE_INDEX, - arg.span, - "you seem to use `.enumerate()` and immediately discard the index", - |diag| { - let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter"); - multispan_sugg( - diag, - "remove the `.enumerate()` call", - vec![ - (pat.span, snippet(cx, elem.span, "..").into_owned()), - (arg.span, base_iter.to_string()), - ], - ); - }, - ); } diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs index 8bca33754e8..7b45cc95431 100644 --- a/src/tools/clippy/clippy_lints/src/loops/utils.rs +++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs @@ -3,7 +3,7 @@ use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_loc use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_local, Visitor}; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, PatKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -141,7 +141,7 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; - fn visit_local(&mut self, l: &'tcx Local<'_>) { + fn visit_local(&mut self, l: &'tcx LetStmt<'_>) { // Look for declarations of the variable if l.pat.hir_id == self.var_id && let PatKind::Binding(.., ident, _) = l.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs index 3511d24e813..3dff826cb85 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs @@ -101,7 +101,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { Res::Local(hir_id) => { self.ids.insert(hir_id); }, - Res::Def(DefKind::Static{..}, def_id) => { + Res::Def(DefKind::Static { .. }, def_id) => { let mutable = self.cx.tcx.is_mutable_static(def_id); self.def_ids.insert(def_id, mutable); }, diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs index 93774b89768..bd04827a1f0 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -5,13 +5,13 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::any_temporaries_need_ordered_drop; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, Local, MatchSource, Pat, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, StmtKind}; use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) { ([stmt, stmts @ ..], expr) => { - if let StmtKind::Let(&Local { + if let StmtKind::Let(&LetStmt { init: Some(e), els: None, .. diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index d070ee74985..194dd4752f9 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -6,7 +6,7 @@ use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutabl use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp}; +use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, LetStmt, Mutability, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::adjustment::Adjust; @@ -286,7 +286,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & self.cx.tcx.hir() } - fn visit_local(&mut self, l: &'tcx Local<'_>) { + fn visit_local(&mut self, l: &'tcx LetStmt<'_>) { if !self.after_loop { l.pat.each_binding_or_first(&mut |_, id, _, _| { if id == self.local_id { diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index 5cbab0ec977..f8f33cfc82e 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet_opt; use clippy_utils::visitors::{is_local_used, local_used_once}; use clippy_utils::{is_trait_method, path_to_local_id}; use rustc_errors::Applicability; -use rustc_hir::{BindingAnnotation, ExprKind, Local, Node, PatKind, StmtKind}; +use rustc_hir::{BindingAnnotation, ExprKind, LetStmt, Node, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::sym; @@ -60,7 +60,7 @@ impl ManualHashOne { impl_lint_pass!(ManualHashOne => [MANUAL_HASH_ONE]); impl LateLintPass<'_> for ManualHashOne { - fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &LetStmt<'_>) { // `let mut hasher = seg.build_hasher();` if let PatKind::Binding(BindingAnnotation::MUT, hasher, _, None) = local.pat.kind && let Some(init) = local.init diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs index 0bde62bd554..ab9bca170cf 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { // Apply only to params or locals with annotated types match cx.tcx.parent_hir_node(hir_id) { Node::Param(..) => (), - Node::Local(local) => { + Node::LetStmt(local) => { let Some(ty) = local.ty else { return }; if matches!(ty.kind, TyKind::Infer) { return; diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index 6f15fca089e..3ddb06a1e08 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq}; +use clippy_utils::{match_def_path, paths, SpanlessEq}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -69,17 +69,16 @@ impl_lint_pass!(ManualRetain => [MANUAL_RETAIN]); impl<'tcx> LateLintPass<'tcx> for ManualRetain { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if let Some(parent_expr) = get_parent_expr(cx, expr) - && let Assign(left_expr, collect_expr, _) = &parent_expr.kind + if let Assign(left_expr, collect_expr, _) = &expr.kind && let hir::ExprKind::MethodCall(seg, ..) = &collect_expr.kind && seg.args.is_none() && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id) { - check_into_iter(cx, left_expr, target_expr, parent_expr.span, &self.msrv); - check_iter(cx, left_expr, target_expr, parent_expr.span, &self.msrv); - check_to_owned(cx, left_expr, target_expr, parent_expr.span, &self.msrv); + check_into_iter(cx, left_expr, target_expr, expr.span, &self.msrv); + check_iter(cx, left_expr, target_expr, expr.span, &self.msrv); + check_to_owned(cx, left_expr, target_expr, expr.span, &self.msrv); } } diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs new file mode 100644 index 00000000000..ddaf97c463a --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs @@ -0,0 +1,181 @@ +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_default_equivalent; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::implements_trait; + +declare_clippy_lint! { + /// ### What it does + /// Checks if a `match` or `if let` expression can be simplified using + /// `.unwrap_or_default()`. + /// + /// ### Why is this bad? + /// It can be done in one call with `.unwrap_or_default()`. + /// + /// ### Example + /// ```no_run + /// let x: Option<String> = Some(String::new()); + /// let y: String = match x { + /// Some(v) => v, + /// None => String::new(), + /// }; + /// + /// let x: Option<Vec<String>> = Some(Vec::new()); + /// let y: Vec<String> = if let Some(v) = x { + /// v + /// } else { + /// Vec::new() + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// let x: Option<String> = Some(String::new()); + /// let y: String = x.unwrap_or_default(); + /// + /// let x: Option<Vec<String>> = Some(Vec::new()); + /// let y: Vec<String> = x.unwrap_or_default(); + /// ``` + #[clippy::version = "1.78.0"] + pub MANUAL_UNWRAP_OR_DEFAULT, + suspicious, + "check if a `match` or `if let` can be simplified with `unwrap_or_default`" +} + +declare_lint_pass!(ManualUnwrapOrDefault => [MANUAL_UNWRAP_OR_DEFAULT]); + +fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<HirId> { + if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && cx.tcx.lang_items().get(LangItem::OptionSome) == Some(def_id) + { + let mut bindings = Vec::new(); + pat.each_binding(|_, id, _, _| bindings.push(id)); + if let &[id] = bindings.as_slice() { + Some(id) + } else { + None + } + } else { + None + } +} + +fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let PatKind::Path(QPath::Resolved(_, path)) = arm.pat.kind + && let Some(def_id) = path.res.opt_def_id() + // Since it comes from a pattern binding, we need to get the parent to actually match + // against it. + && let Some(def_id) = cx.tcx.opt_parent(def_id) + && cx.tcx.lang_items().get(LangItem::OptionNone) == Some(def_id) + { + Some(arm.body) + } else if let PatKind::Wild = arm.pat.kind { + // We consider that the `Some` check will filter it out if it's not right. + Some(arm.body) + } else { + None + } +} + +fn get_some_and_none_bodies<'tcx>( + cx: &LateContext<'tcx>, + arm1: &'tcx Arm<'tcx>, + arm2: &'tcx Arm<'tcx>, +) -> Option<((&'tcx Expr<'tcx>, HirId), &'tcx Expr<'tcx>)> { + if let Some(binding_id) = get_some(cx, arm1.pat) + && let Some(body_none) = get_none(cx, arm2) + { + Some(((arm1.body, binding_id), body_none)) + } else if let Some(binding_id) = get_some(cx, arm2.pat) + && let Some(body_none) = get_none(cx, arm1) + { + Some(((arm2.body, binding_id), body_none)) + } else { + None + } +} + +fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let ExprKind::Match(match_expr, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) = expr.kind else { + return false; + }; + // We don't want conditions on the arms to simplify things. + if arm1.guard.is_none() + && arm2.guard.is_none() + // We check that the returned type implements the `Default` trait. + && let match_ty = cx.typeck_results().expr_ty(expr) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, match_ty, default_trait_id, &[]) + // We now get the bodies for both the `Some` and `None` arms. + && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) + // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. + && let ExprKind::Path(QPath::Resolved(_, path)) = body_some.peel_blocks().kind + && let Res::Local(local_id) = path.res + && local_id == binding_id + // We now check the `None` arm is calling a method equivalent to `Default::default`. + && let body_none = body_none.peel_blocks() + && is_default_equivalent(cx, body_none) + && let Some(match_expr_snippet) = snippet_opt(cx, match_expr.span) + { + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + "match can be simplified with `.unwrap_or_default()`", + "replace it with", + format!("{match_expr_snippet}.unwrap_or_default()"), + Applicability::MachineApplicable, + ); + } + true +} + +fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::If(cond, if_block, Some(else_expr)) = expr.kind + && let ExprKind::Let(let_) = cond.kind + && let ExprKind::Block(_, _) = else_expr.kind + // We check that the returned type implements the `Default` trait. + && let match_ty = cx.typeck_results().expr_ty(expr) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, match_ty, default_trait_id, &[]) + && let Some(binding_id) = get_some(cx, let_.pat) + // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. + && let ExprKind::Path(QPath::Resolved(_, path)) = if_block.peel_blocks().kind + && let Res::Local(local_id) = path.res + && local_id == binding_id + // We now check the `None` arm is calling a method equivalent to `Default::default`. + && let body_else = else_expr.peel_blocks() + && is_default_equivalent(cx, body_else) + && let Some(if_let_expr_snippet) = snippet_opt(cx, let_.init.span) + { + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR_DEFAULT, + expr.span, + "if let can be simplified with `.unwrap_or_default()`", + "replace it with", + format!("{if_let_expr_snippet}.unwrap_or_default()"), + Applicability::MachineApplicable, + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if expr.span.from_expansion() { + return; + } + if !handle_match(cx, expr) { + handle_if_let(cx, expr); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs index c8a48246e67..0f242e0b9e1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; -use rustc_hir::{ByRef, ExprKind, Local, MatchSource, PatKind, QPath}; +use rustc_hir::{ByRef, ExprKind, LetStmt, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; use super::INFALLIBLE_DESTRUCTURING_MATCH; -pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { +pub(crate) fn check(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { if !local.span.from_expansion() && let Some(expr) = local.init && let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index c7c453b7f6e..cd61e733694 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -243,7 +243,7 @@ impl<'a> NormalizedPat<'a> { fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, - PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Ref(pat, _) => { + PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => { Self::from_pat(cx, arena, pat) }, PatKind::Never => Self::Never, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs index 61977045fd4..864923b2773 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs @@ -148,7 +148,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<AssignmentExpr> { if let Node::Expr(parent_arm_expr) = cx.tcx.parent_hir_node(ex.hir_id) { return match cx.tcx.parent_hir_node(parent_arm_expr.hir_id) { - Node::Local(parent_let_expr) => Some(AssignmentExpr::Local { + Node::LetStmt(parent_let_expr) => Some(AssignmentExpr::Local { span: parent_let_expr.span, pat_span: parent_let_expr.pat.span(), }), diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 50494f4819f..580d4a64296 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -27,7 +27,7 @@ mod wild_in_or_pats; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_opt, walk_span_to_context}; use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, tokenize_with_text}; -use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; +use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat}; use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -1124,7 +1124,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { self.infallible_destructuring_match_linted |= local.els.is_none() && infallible_destructuring_match::check(cx, local); } diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 3d3f29e5fc6..cee77f62b61 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -125,7 +125,7 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { match cx.tcx.parent_hir_node(p_expr.hir_id) { // Compare match_expr ty with local in `let local = match match_expr {..}` - Node::Local(local) => { + Node::LetStmt(local) => { let results = cx.typeck_results(); return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); }, diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index b770ad0ddb5..10c3203725a 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -149,7 +149,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { false }, rustc_middle::ty::Array(ty, _) - | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. }) + | rustc_middle::ty::RawPtr(ty, _) | rustc_middle::ty::Ref(_, ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), _ => false, diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 86c414dafcc..a0db8e2db1f 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -55,23 +55,15 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: }; let ty = cx.typeck_results().expr_ty(ex); - if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) { - check_single_pattern(cx, ex, arms, expr, els); - check_opt_like(cx, ex, arms, expr, ty, els); + if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id)) && + (check_single_pattern(arms) || check_opt_like(cx, arms, ty)) { + report_single_pattern(cx, ex, arms, expr, els); } } } -fn check_single_pattern( - cx: &LateContext<'_>, - ex: &Expr<'_>, - arms: &[Arm<'_>], - expr: &Expr<'_>, - els: Option<&Expr<'_>>, -) { - if is_wild(arms[1].pat) { - report_single_pattern(cx, ex, arms, expr, els); - } +fn check_single_pattern(arms: &[Arm<'_>]) -> bool { + is_wild(arms[1].pat) } fn report_single_pattern( @@ -140,19 +132,10 @@ fn report_single_pattern( span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app); } -fn check_opt_like<'a>( - cx: &LateContext<'a>, - ex: &Expr<'_>, - arms: &[Arm<'_>], - expr: &Expr<'_>, - ty: Ty<'a>, - els: Option<&Expr<'_>>, -) { +fn check_opt_like<'a>(cx: &LateContext<'a>, arms: &[Arm<'_>], ty: Ty<'a>) -> bool { // We don't want to lint if the second arm contains an enum which could // have more variants in the future. - if form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) { - report_single_pattern(cx, ex, arms, expr, els); - } + form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) } /// Returns `true` if all of the types in the pattern are enums which we know diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index fb5c0c544a9..d4a5de3d1de 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -69,7 +69,7 @@ pub(super) fn check( _ => false, }, // local binding capturing a reference - Node::Local(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => { + Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => { return; }, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index e2c2997594a..4d8fb217f7f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -91,7 +91,7 @@ pub(super) fn check<'tcx>( }, hir::ExprKind::Path(ref p) => matches!( cx.qpath_res(p, arg.hir_id), - hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static{..}, _) + hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static { .. }, _) ), _ => false, } diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs new file mode 100644 index 00000000000..7fe66062251 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs @@ -0,0 +1,49 @@ +use clippy_utils::consts::constant_is_empty; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{find_binding_init, path_to_local}; +use rustc_hir::{Expr, HirId}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_span::sym; + +use super::CONST_IS_EMPTY; + +/// Expression whose initialization depend on a constant conditioned by a `#[cfg(…)]` directive will +/// not trigger the lint. +pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) || !receiver.span.eq_ctxt(expr.span) { + return; + } + let init_expr = expr_or_init(cx, receiver); + if !receiver.span.eq_ctxt(init_expr.span) { + return; + } + if let Some(init_is_empty) = constant_is_empty(cx, init_expr) { + span_lint( + cx, + CONST_IS_EMPTY, + expr.span, + &format!("this expression always evaluates to {init_is_empty:?}"), + ); + } +} + +fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { + cx.tcx + .hir() + .parent_id_iter(id) + .any(|id| cx.tcx.hir().attrs(id).iter().any(|attr| attr.has_name(sym::cfg))) +} + +/// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization +/// value depends on a `#[cfg(…)]` directive. +fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { + while let Some(init) = path_to_local(expr) + .and_then(|id| find_binding_init(cx, id)) + .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) + .filter(|init| !is_under_cfg(cx, init.hir_id)) + { + expr = init; + } + expr +} diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs index 12104310405..5b0b70b4b96 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs @@ -1,10 +1,10 @@ -use super::utils::derefs_to_slice; -use crate::methods::iter_nth_zero; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::get_type_diagnostic_name; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::symbol::sym; +use rustc_span::Span; use super::ITER_NTH; @@ -12,28 +12,33 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_recv: &'tcx hir::Expr<'tcx>, - nth_recv: &hir::Expr<'_>, - nth_arg: &hir::Expr<'_>, - is_mut: bool, -) { - let mut_str = if is_mut { "_mut" } else { "" }; - let caller_type = if derefs_to_slice(cx, iter_recv, cx.typeck_results().expr_ty(iter_recv)).is_some() { - "slice" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::Vec) { - "`Vec`" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::VecDeque) { - "`VecDeque`" - } else { - iter_nth_zero::check(cx, expr, nth_recv, nth_arg); - return; // caller is not a type that we want to lint + iter_method: &str, + iter_span: Span, + nth_span: Span, +) -> bool { + let caller_type = match get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(iter_recv).peel_refs()) { + Some(sym::Vec) => "`Vec`", + Some(sym::VecDeque) => "`VecDeque`", + _ if cx.typeck_results().expr_ty_adjusted(iter_recv).peel_refs().is_slice() => "slice", + // caller is not a type that we want to lint + _ => return false, }; - span_lint_and_help( + span_lint_and_then( cx, ITER_NTH, expr.span, - &format!("called `.iter{mut_str}().nth()` on a {caller_type}"), - None, - &format!("calling `.get{mut_str}()` is both faster and more readable"), + &format!("called `.{iter_method}().nth()` on a {caller_type}"), + |diag| { + let get_method = if iter_method == "iter_mut" { "get_mut" } else { "get" }; + diag.span_suggestion_verbose( + iter_span.to(nth_span), + format!("`{get_method}` is equivalent but more concise"), + get_method, + Applicability::MachineApplicable, + ); + }, ); + + true } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 4c7c56e7174..19b7e97339d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -50,7 +50,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re | ExprKind::Break(_, _) => true, _ => false, }, - Some((Node::Stmt(_) | Node::Local(_), _)) => false, + Some((Node::Stmt(_) | Node::LetStmt(_), _)) => false, _ => true, }; diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index 27e17b43b01..c3c7a3a0033 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -86,8 +86,11 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ } } }, - hir::ExprKind::Call(call, [_]) => { - if let hir::ExprKind::Path(qpath) = call.kind { + hir::ExprKind::Call(call, args) => { + if let hir::ExprKind::Path(qpath) = call.kind + && let [arg] = args + && ident_eq(name, arg) + { handle_path(cx, call, &qpath, e, recv); } }, @@ -118,7 +121,9 @@ fn handle_path( if let ty::Adt(_, args) = cx.typeck_results().expr_ty(recv).kind() && let args = args.as_slice() && let Some(ty) = args.iter().find_map(|generic_arg| generic_arg.as_type()) - && ty.is_ref() + && let ty::Ref(_, ty, Mutability::Not) = ty.kind() + && let ty::FnDef(_, lst) = cx.typeck_results().expr_ty(arg).kind() + && lst.iter().all(|l| l.as_type() == Some(*ty)) { lint_path(cx, e.span, recv.span, is_copy(cx, ty.peel_refs())); } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 8a24ccea3a1..b6c474212cd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -36,6 +36,7 @@ mod inefficient_to_string; mod inspect_for_each; mod into_iter_on_ref; mod is_digit_ascii_radix; +mod is_empty; mod iter_cloned_collect; mod iter_count; mod iter_filter; @@ -118,6 +119,7 @@ mod unnecessary_literal_unwrap; mod unnecessary_result_map_or_else; mod unnecessary_sort_by; mod unnecessary_to_owned; +mod unused_enumerate_index; mod unwrap_expect_used; mod useless_asref; mod utils; @@ -1235,12 +1237,11 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `.iter().nth()` (and the related - /// `.iter_mut().nth()`) on standard library types with *O*(1) element access. + /// Checks for usage of `.iter().nth()`/`.iter_mut().nth()` on standard library types that have + /// equivalent `.get()`/`.get_mut()` methods. /// /// ### Why is this bad? - /// `.get()` and `.get_mut()` are more efficient and more - /// readable. + /// `.get()` and `.get_mut()` are equivalent but more concise. /// /// ### Example /// ```no_run @@ -1256,7 +1257,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub ITER_NTH, - perf, + style, "using `.iter().nth()` on a standard library type with O(1) element access" } @@ -2848,7 +2849,7 @@ declare_clippy_lint! { /// the file is created from scratch, or ensure `truncate` is /// called so that the truncation behaviour is explicit. `truncate(true)` /// will ensure the file is entirely overwritten with new data, whereas - /// `truncate(false)` will explicitely keep the default behavior. + /// `truncate(false)` will explicitly keep the default behavior. /// /// ### Example /// ```rust,no_run @@ -2862,7 +2863,7 @@ declare_clippy_lint! { /// /// OpenOptions::new().create(true).truncate(true); /// ``` - #[clippy::version = "1.75.0"] + #[clippy::version = "1.77.0"] pub SUSPICIOUS_OPEN_OPTIONS, suspicious, "suspicious combination of options for opening a file" @@ -3182,8 +3183,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// - /// Checks an argument of `seek` method of `Seek` trait - /// and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead. + /// Checks if the `seek` method of the `Seek` trait is called with `SeekFrom::Current(0)`, + /// and if it is, suggests using `stream_position` instead. /// /// ### Why is this bad? /// @@ -3590,7 +3591,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.73.0"] pub READONLY_WRITE_LOCK, - nursery, + perf, "acquiring a write lock when a read lock would work" } @@ -3816,7 +3817,7 @@ declare_clippy_lint! { /// ```no_run /// let _ = std::iter::empty::<Result<i32, ()>>().flatten(); /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub RESULT_FILTER_MAP, complexity, "filtering `Result` for `Ok` then force-unwrapping, which can be one type-safe operation" @@ -3825,7 +3826,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for usage of `.filter(Option::is_some)` that may be replaced with a `.flatten()` call. - /// This lint will require additional changes to the follow-up calls as it appects the type. + /// This lint will require additional changes to the follow-up calls as it affects the type. /// /// ### Why is this bad? /// This pattern is often followed by manual unwrapping of the `Option`. The simplification @@ -3842,7 +3843,7 @@ declare_clippy_lint! { /// // example code which does not raise clippy warning /// vec![Some(1)].into_iter().flatten(); /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub ITER_FILTER_IS_SOME, pedantic, "filtering an iterator over `Option`s for `Some` can be achieved with `flatten`" @@ -3851,7 +3852,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for usage of `.filter(Result::is_ok)` that may be replaced with a `.flatten()` call. - /// This lint will require additional changes to the follow-up calls as it appects the type. + /// This lint will require additional changes to the follow-up calls as it affects the type. /// /// ### Why is this bad? /// This pattern is often followed by manual unwrapping of `Result`. The simplification @@ -3868,7 +3869,7 @@ declare_clippy_lint! { /// // example code which does not raise clippy warning /// vec![Ok::<i32, String>(1)].into_iter().flatten(); /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub ITER_FILTER_IS_OK, pedantic, "filtering an iterator over `Result`s for `Ok` can be achieved with `flatten`" @@ -3895,7 +3896,7 @@ declare_clippy_lint! { /// option.is_some_and(|a| a > 10); /// result.is_ok_and(|a| a > 10); /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub MANUAL_IS_VARIANT_AND, pedantic, "using `.map(f).unwrap_or_default()`, which is more succinctly expressed as `is_some_and(f)` or `is_ok_and(f)`" @@ -3925,7 +3926,7 @@ declare_clippy_lint! { /// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is /// valid. /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub STR_SPLIT_AT_NEWLINE, pedantic, "splitting a trimmed string at hard-coded newlines" @@ -4044,6 +4045,31 @@ declare_clippy_lint! { "calling `.get().is_some()` or `.get().is_none()` instead of `.contains()` or `.contains_key()`" } +declare_clippy_lint! { + /// ### What it does + /// It identifies calls to `.is_empty()` on constant values. + /// + /// ### Why is this bad? + /// String literals and constant values are known at compile time. Checking if they + /// are empty will always return the same value. This might not be the intention of + /// the expression. + /// + /// ### Example + /// ```no_run + /// let value = ""; + /// if value.is_empty() { + /// println!("the string is empty"); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// println!("the string is empty"); + /// ``` + #[clippy::version = "1.78.0"] + pub CONST_IS_EMPTY, + suspicious, + "is_empty() called on strings known at compile time" +} pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4092,6 +4118,7 @@ impl_lint_pass!(Methods => [ CLONE_ON_COPY, CLONE_ON_REF_PTR, COLLAPSIBLE_STR_REPLACE, + CONST_IS_EMPTY, ITER_OVEREAGER_CLONED, CLONED_INSTEAD_OF_COPIED, FLAT_MAP_OPTION, @@ -4403,6 +4430,7 @@ impl Methods { zst_offset::check(cx, expr, recv); }, ("all", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); if let Some(("cloned", recv2, [], _, _)) = method_call(recv) { iter_overeager_cloned::check( cx, @@ -4421,23 +4449,26 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, - ("any", [arg]) => match method_call(recv) { - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( - cx, - expr, - recv, - recv2, - iter_overeager_cloned::Op::NeedlessMove(arg), - false, - ), - Some(("chars", recv, _, _, _)) - if let ExprKind::Closure(arg) = arg.kind - && let body = cx.tcx.hir().body(arg.body) - && let [param] = body.params => - { - string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); - }, - _ => {}, + ("any", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); + match method_call(recv) { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::NeedlessMove(arg), + false, + ), + Some(("chars", recv, _, _, _)) + if let ExprKind::Closure(arg) = arg.kind + && let body = cx.tcx.hir().body(arg.body) + && let [param] = body.params => + { + string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + }, + _ => {}, + } }, ("arg", [arg]) => { suspicious_command_arg_space::check(cx, recv, arg, span); @@ -4445,7 +4476,7 @@ impl Methods { ("as_deref" | "as_deref_mut", []) => { needless_option_as_deref::check(cx, expr, recv, name); }, - ("as_bytes" | "is_empty", []) => { + ("as_bytes", []) => { if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); } @@ -4570,14 +4601,17 @@ impl Methods { } }, ("filter_map", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, name); filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); }, ("find_map", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, name); }, ("flat_map", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); flat_map_identity::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span); }, @@ -4599,17 +4633,20 @@ impl Methods { manual_try_fold::check(cx, expr, init, acc, call_span, &self.msrv); unnecessary_fold::check(cx, expr, init, acc, span); }, - ("for_each", [arg]) => match method_call(recv) { - Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), - Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( - cx, - expr, - recv, - recv2, - iter_overeager_cloned::Op::NeedlessMove(arg), - false, - ), - _ => {}, + ("for_each", [arg]) => { + unused_enumerate_index::check(cx, expr, recv, arg); + match method_call(recv) { + Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::NeedlessMove(arg), + false, + ), + _ => {}, + } }, ("get", [arg]) => { get_first::check(cx, expr, recv, arg); @@ -4619,6 +4656,12 @@ impl Methods { ("hash", [arg]) => { unit_hash::check(cx, expr, recv, arg); }, + ("is_empty", []) => { + if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { + redundant_as_str::check(cx, expr, recv, as_str_span, span); + } + is_empty::check(cx, expr, recv); + }, ("is_file", []) => filetype_is_file::check(cx, expr, recv), ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, call_span, false), @@ -4650,6 +4693,7 @@ impl Methods { }, (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { + unused_enumerate_index::check(cx, expr, recv, m_arg); map_clone::check(cx, expr, recv, m_arg, &self.msrv); match method_call(recv) { Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => { @@ -4723,8 +4767,11 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), - Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + Some((iter_method @ ("iter" | "iter_mut"), iter_recv, [], iter_span, _)) => { + if !iter_nth::check(cx, expr, iter_recv, iter_method, iter_span, span) { + iter_nth_zero::check(cx, expr, recv, n_arg); + } + }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 78540353005..9e2fd92255e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{ - BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind, + BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, PatKind, Stmt, StmtKind, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -85,7 +85,7 @@ pub(super) fn check<'tcx>( ); } }, - Node::Local(l) => { + Node::LetStmt(l) => { if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) = l.pat.kind && let ty = cx.typeck_results().expr_ty(collect_expr) && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList] @@ -424,7 +424,7 @@ fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v> match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)), StmtKind::Item(..) => None, - StmtKind::Let(Local { init, pat, .. }) => { + StmtKind::Let(LetStmt { init, pat, .. }) => { if let PatKind::Binding(_, hir_id, ..) = pat.kind { init.map(|init_expr| (init_expr, Some(hir_id))) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs index 81df32bdee2..a301a5f7d65 100644 --- a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs +++ b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs @@ -25,6 +25,7 @@ pub(super) fn check<'tcx>( && param1 == param2.as_str() { span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); + return; } if SpanlessEq::new(cx).eq_expr(arg1, arg2) { diff --git a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs index 6c6846c4b47..9b0180d9369 100644 --- a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs +++ b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver && let Node::Expr(unwrap_call_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_unwrap_call(cx, unwrap_call_expr) && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id) - && let Node::Local(local) = parent + && let Node::LetStmt(local) = parent && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id) && let Some((local, _)) = mir .local_decls diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 55cd1a38ec9..946cdb49d27 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -8,7 +8,7 @@ use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths} use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ - BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, + BindingAnnotation, Expr, ExprKind, HirId, LangItem, LetStmt, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, }; use rustc_lint::LateContext; use rustc_middle::ty; @@ -128,7 +128,7 @@ fn check_manual_split_once_indirect( ) -> Option<()> { let ctxt = expr.span.ctxt(); let mut parents = cx.tcx.hir().parent_iter(expr.hir_id); - if let (_, Node::Local(local)) = parents.next()? + if let (_, Node::LetStmt(local)) = parents.next()? && let PatKind::Binding(BindingAnnotation::MUT, iter_binding_id, iter_ident, None) = local.pat.kind && let (iter_stmt_id, Node::Stmt(_)) = parents.next()? && let (_, Node::Block(enclosing_block)) = parents.next()? @@ -198,7 +198,7 @@ fn indirect_usage<'tcx>( binding: HirId, ctxt: SyntaxContext, ) -> Option<IndirectUsage<'tcx>> { - if let StmtKind::Let(&Local { + if let StmtKind::Let(&LetStmt { pat: Pat { kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None), .. diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index 988f3e86fcf..ccc8d17970e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -20,7 +20,7 @@ fn needs_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { // some common cases where turbofish isn't needed: // - assigned to a local variable with a type annotation - if let hir::Node::Local(local) = parent + if let hir::Node::LetStmt(local) = parent && local.ty.is_some() { return false; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index c234e4f9b11..bc5dd10cad0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -18,7 +18,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{ - self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ImplPolarity, ParamTy, ProjectionPredicate, + self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; use rustc_span::{sym, Symbol}; @@ -336,12 +336,9 @@ fn check_other_call_arg<'tcx>( && let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) { Some((n_refs, receiver_ty)) } else if trait_predicate.def_id() != deref_trait_id { - Some((1, Ty::new_ref(cx.tcx, + Some((1, Ty::new_imm_ref(cx.tcx, cx.tcx.lifetimes.re_erased, - ty::TypeAndMut { - ty: receiver_ty, - mutbl: Mutability::Not, - }, + receiver_ty, ))) } else { None @@ -669,7 +666,7 @@ fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { && let Some(borrow_id) = cx.tcx.get_diagnostic_item(sym::Borrow) && cx.tcx.predicates_of(method_def_id).predicates.iter().any(|(pred, _)| { if let ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() - && trait_pred.polarity == ImplPolarity::Positive + && trait_pred.polarity == ty::PredicatePolarity::Positive && trait_pred.trait_ref.def_id == borrow_id { true diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs new file mode 100644 index 00000000000..e5cc898612e --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs @@ -0,0 +1,135 @@ +use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then}; +use clippy_utils::paths::{CORE_ITER_ENUMERATE_METHOD, CORE_ITER_ENUMERATE_STRUCT}; +use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::{expr_or_init, is_trait_method, match_def_path, pat_is_wild}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::AdtDef; +use rustc_span::{sym, Span}; + +use crate::loops::UNUSED_ENUMERATE_INDEX; + +/// Check for the `UNUSED_ENUMERATE_INDEX` lint outside of loops. +/// +/// The lint is declared in `clippy_lints/src/loops/mod.rs`. There, the following pattern is +/// checked: +/// ```ignore +/// for (_, x) in some_iter.enumerate() { +/// // Index is ignored +/// } +/// ``` +/// +/// This `check` function checks for chained method calls constructs where we can detect that the +/// index is unused. Currently, this checks only for the following patterns: +/// ```ignore +/// some_iter.enumerate().map_function(|(_, x)| ..) +/// let x = some_iter.enumerate(); +/// x.map_function(|(_, x)| ..) +/// ``` +/// where `map_function` is one of `all`, `any`, `filter_map`, `find_map`, `flat_map`, `for_each` or +/// `map`. +/// +/// # Preconditions +/// This function must be called not on the `enumerate` call expression itself, but on any of the +/// map functions listed above. It will ensure that `recv` is a `std::iter::Enumerate` instance and +/// that the method call is one of the `std::iter::Iterator` trait. +/// +/// * `call_expr`: The map function call expression +/// * `recv`: The receiver of the call +/// * `closure_arg`: The argument to the map function call containing the closure/function to apply +pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) { + let recv_ty = cx.typeck_results().expr_ty(recv); + if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) + // If we call a method on a `std::iter::Enumerate` instance + && match_def_path(cx, recv_ty_defid, &CORE_ITER_ENUMERATE_STRUCT) + // If we are calling a method of the `Iterator` trait + && is_trait_method(cx, call_expr, sym::Iterator) + // And the map argument is a closure + && let ExprKind::Closure(closure) = closure_arg.kind + && let closure_body = cx.tcx.hir().body(closure.body) + // And that closure has one argument ... + && let [closure_param] = closure_body.params + // .. which is a tuple of 2 elements + && let PatKind::Tuple([index, elem], ..) = closure_param.pat.kind + // And that the first element (the index) is either `_` or unused in the body + && pat_is_wild(cx, &index.kind, closure_body) + // Try to find the initializer for `recv`. This is needed in case `recv` is a local_binding. In the + // first example below, `expr_or_init` would return `recv`. + // ``` + // iter.enumerate().map(|(_, x)| x) + // ^^^^^^^^^^^^^^^^ `recv`, a call to `std::iter::Iterator::enumerate` + // + // let binding = iter.enumerate(); + // ^^^^^^^^^^^^^^^^ `recv_init_expr` + // binding.map(|(_, x)| x) + // ^^^^^^^ `recv`, not a call to `std::iter::Iterator::enumerate` + // ``` + && let recv_init_expr = expr_or_init(cx, recv) + // Make sure the initializer is a method call. It may be that the `Enumerate` comes from something + // that we cannot control. + // This would for instance happen with: + // ``` + // external_lib::some_function_returning_enumerate().map(|(_, x)| x) + // ``` + && let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind + && let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id) + // Make sure the method call is `std::iter::Iterator::enumerate`. + && match_def_path(cx, enumerate_defid, &CORE_ITER_ENUMERATE_METHOD) + { + // Check if the tuple type was explicit. It may be the type system _needs_ the type of the element + // that would be explicited in the closure. + let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) { + // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`. + // Fallback to `..` if we fail getting either snippet. + Some(ty_span) => snippet_opt(cx, elem.span) + .and_then(|binding_name| snippet_opt(cx, ty_span).map(|ty_name| format!("{binding_name}: {ty_name}"))) + .unwrap_or_else(|| "..".to_string()), + // Otherwise, we have no explicit type. We can replace with the binding name of the element. + None => snippet(cx, elem.span, "..").into_owned(), + }; + + // Suggest removing the tuple from the closure and the preceding call to `enumerate`, whose span we + // can get from the `MethodCall`. + span_lint_hir_and_then( + cx, + UNUSED_ENUMERATE_INDEX, + recv_init_expr.hir_id, + enumerate_span, + "you seem to use `.enumerate()` and immediately discard the index", + |diag| { + multispan_sugg_with_applicability( + diag, + "remove the `.enumerate()` call", + Applicability::MachineApplicable, + vec![ + (closure_param.span, new_closure_param), + ( + enumerate_span.with_lo(enumerate_recv.span.source_callsite().hi()), + String::new(), + ), + ], + ); + }, + ); + } +} + +/// Find the span of the explicit type of the element. +/// +/// # Returns +/// If the tuple argument: +/// * Has no explicit type, returns `None` +/// * Has an explicit tuple type with an implicit element type (`(usize, _)`), returns `None` +/// * Has an explicit tuple type with an explicit element type (`(_, i32)`), returns the span for +/// the element type. +fn find_elem_explicit_type_span(fn_decl: &FnDecl<'_>) -> Option<Span> { + if let [tuple_ty] = fn_decl.inputs + && let TyKind::Tup([_idx_ty, elem_ty]) = tuple_ty.kind + && !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer) + { + Some(elem_ty.span) + } else { + None + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs b/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs index 0b829d99aef..d33021c2a7b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs +++ b/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs @@ -6,7 +6,7 @@ use rustc_middle::ty; use super::ZST_OFFSET; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if let ty::RawPtr(ty::TypeAndMut { ty, .. }) = cx.typeck_results().expr_ty(recv).kind() + if let ty::RawPtr(ty, _) = cx.typeck_results().expr_ty(recv).kind() && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(*ty)) && layout.is_zst() { diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index bf4af7946f4..6878fb3349d 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -8,6 +8,7 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; +use clippy_utils::source::snippet_opt; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; @@ -32,6 +33,13 @@ declare_clippy_lint! { "detects missing documentation for private members" } +macro_rules! note_prev_span_then_ret { + ($prev_span:expr, $span:expr) => {{ + $prev_span = Some($span); + return; + }}; +} + pub struct MissingDoc { /// Whether to **only** check for missing documentation in items visible within the current /// crate. For example, `pub(crate)` items. @@ -39,6 +47,8 @@ pub struct MissingDoc { /// Stack of whether #[doc(hidden)] is set /// at each level which has lint attributes. doc_hidden_stack: Vec<bool>, + /// Used to keep tracking of the previous item, field or variants etc, to get the search span. + prev_span: Option<Span>, } impl Default for MissingDoc { @@ -54,6 +64,7 @@ impl MissingDoc { Self { crate_items_only, doc_hidden_stack: vec![false], + prev_span: None, } } @@ -108,7 +119,8 @@ impl MissingDoc { let has_doc = attrs .iter() - .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); + .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())) + || matches!(self.search_span(sp), Some(span) if span_to_snippet_contains_docs(cx, span)); if !has_doc { span_lint( @@ -119,6 +131,32 @@ impl MissingDoc { ); } } + + /// Return a span to search for doc comments manually. + /// + /// # Example + /// ```ignore + /// fn foo() { ... } + /// ^^^^^^^^^^^^^^^^ prev_span + /// ↑ + /// | search_span | + /// ↓ + /// fn bar() { ... } + /// ^^^^^^^^^^^^^^^^ cur_span + /// ``` + fn search_span(&self, cur_span: Span) -> Option<Span> { + let prev_span = self.prev_span?; + let start_pos = if prev_span.contains(cur_span) { + // In case when the prev_span is an entire struct, or enum, + // and the current span is a field, or variant, we need to search from + // the starting pos of the previous span. + prev_span.lo() + } else { + prev_span.hi() + }; + let search_span = cur_span.with_lo(start_pos).with_hi(cur_span.lo()); + Some(search_span) + } } impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]); @@ -138,6 +176,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { self.check_missing_docs_attrs(cx, CRATE_DEF_ID, attrs, cx.tcx.def_span(CRATE_DEF_ID), "the", "crate"); } + fn check_crate_post(&mut self, _: &LateContext<'tcx>) { + self.prev_span = None; + } + fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { match it.kind { hir::ItemKind::Fn(..) => { @@ -145,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if it.ident.name == sym::main { let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID; if at_root { - return; + note_prev_span_then_ret!(self.prev_span, it.span); } } }, @@ -164,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::GlobalAsm(..) | hir::ItemKind::Impl { .. } - | hir::ItemKind::Use(..) => return, + | hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span), }; let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id()); @@ -173,6 +215,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if !is_from_proc_macro(cx, it) { self.check_missing_docs_attrs(cx, it.owner_id.def_id, attrs, it.span, article, desc); } + self.prev_span = Some(it.span); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { @@ -182,16 +225,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if !is_from_proc_macro(cx, trait_item) { self.check_missing_docs_attrs(cx, trait_item.owner_id.def_id, attrs, trait_item.span, article, desc); } + self.prev_span = Some(trait_item.span); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { // If the method is an impl for a trait, don't doc. if let Some(cid) = cx.tcx.associated_item(impl_item.owner_id).impl_container(cx.tcx) { if cx.tcx.impl_trait_ref(cid).is_some() { - return; + note_prev_span_then_ret!(self.prev_span, impl_item.span); } } else { - return; + note_prev_span_then_ret!(self.prev_span, impl_item.span); } let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); @@ -199,6 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if !is_from_proc_macro(cx, impl_item) { self.check_missing_docs_attrs(cx, impl_item.owner_id.def_id, attrs, impl_item.span, article, desc); } + self.prev_span = Some(impl_item.span); } fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) { @@ -208,6 +253,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field"); } } + self.prev_span = Some(sf.span); } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { @@ -215,5 +261,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { if !is_from_proc_macro(cx, v) { self.check_missing_docs_attrs(cx, v.def_id, attrs, v.span, "a", "variant"); } + self.prev_span = Some(v.span); } } + +fn span_to_snippet_contains_docs(cx: &LateContext<'_>, search_span: Span) -> bool { + let Some(snippet) = snippet_opt(cx, search_span) else { + return false; + }; + snippet.lines().rev().any(|line| line.trim().starts_with("///")) +} diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 12c7c18afde..656fb907fcd 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind}; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { match stmt.kind { StmtKind::Let(local) => { - if let Local { init: Some(e), .. } = local { + if let LetStmt { init: Some(e), .. } = local { DivergenceVisitor { cx }.visit_expr(e); } }, diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 70fd07cd93c..648d780ac09 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -109,7 +109,14 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::Path(QPath::Resolved( _, hir::Path { - res: Res::Def(DefKind::Static{mutability:Mutability::Mut, ..}, _), + res: + Res::Def( + DefKind::Static { + mutability: Mutability::Mut, + .. + }, + _, + ), .. }, )) => { @@ -149,7 +156,13 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::Path(QPath::Resolved( _, hir::Path { - res: Res::Def(DefKind::Static{mutability:Mutability::Mut, ..}, _), + res: Res::Def( + DefKind::Static { + mutability: Mutability::Mut, + .. + }, + _ + ), .. } )) diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index c32025fcbb6..79f0a398d55 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { } } - fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) { if let hir::PatKind::Wild = local.pat.kind { return; } diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs index 72a2cca1e40..bc7b2c6b7c1 100644 --- a/src/tools/clippy/clippy_lints/src/mut_mut.rs +++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_hir}; use clippy_utils::higher; use rustc_hir as hir; use rustc_hir::intravisit; @@ -35,9 +35,34 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) { - use rustc_hir::intravisit::Visitor; + if in_external_macro(cx.sess(), ty.span) { + return; + } - MutVisitor { cx }.visit_ty(ty); + if let hir::TyKind::Ref( + _, + hir::MutTy { + ty: pty, + mutbl: hir::Mutability::Mut, + }, + ) = ty.kind + { + if let hir::TyKind::Ref( + _, + hir::MutTy { + mutbl: hir::Mutability::Mut, + .. + }, + ) = pty.kind + { + span_lint( + cx, + MUT_MUT, + ty.span, + "generally you want to avoid `&mut &mut _` if possible", + ); + } + } } } @@ -62,17 +87,19 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { intravisit::walk_expr(self, body); } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { - span_lint( + span_lint_hir( self.cx, MUT_MUT, + expr.hir_id, expr.span, "generally you want to avoid `&mut &mut _` if possible", ); } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { if ty.peel_refs().is_sized(self.cx.tcx, self.cx.param_env) { - span_lint( + span_lint_hir( self.cx, MUT_MUT, + expr.hir_id, expr.span, "this expression mutably borrows a mutable reference. Consider reborrowing", ); @@ -80,37 +107,4 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { } } } - - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { - if in_external_macro(self.cx.sess(), ty.span) { - return; - } - - if let hir::TyKind::Ref( - _, - hir::MutTy { - ty: pty, - mutbl: hir::Mutability::Mut, - }, - ) = ty.kind - { - if let hir::TyKind::Ref( - _, - hir::MutTy { - mutbl: hir::Mutability::Mut, - .. - }, - ) = pty.kind - { - span_lint( - self.cx, - MUT_MUT, - ty.span, - "generally you want to avoid `&mut &mut _` if possible", - ); - } - } - - intravisit::walk_ty(self, ty); - } } diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs index f905a4e5b64..14a1e6be738 100644 --- a/src/tools/clippy/clippy_lints/src/mut_reference.rs +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -84,9 +84,7 @@ fn check_arguments<'tcx>( for (argument, parameter) in iter::zip(arguments, parameters) { match parameter.kind() { ty::Ref(_, _, Mutability::Not) - | ty::RawPtr(ty::TypeAndMut { - mutbl: Mutability::Not, .. - }) => { + | ty::RawPtr(_, Mutability::Not) => { if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 4ae4fc9b096..61243c83731 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -127,7 +127,7 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> { IntTy::I128 => None, } }, - ty::RawPtr(_) => Some("AtomicPtr"), + ty::RawPtr(_, _) => Some("AtomicPtr"), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index 4cda4b171e3..810799acb2e 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -6,7 +6,7 @@ use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_loca use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{ - BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, + BindingAnnotation, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; @@ -237,7 +237,7 @@ fn first_usage<'tcx>( }) } -fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &Local<'_>) -> Option<String> { +fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option<String> { let span = local.span.with_hi(match local.ty { // let <pat>: <ty>; // ~~~~~~~~~~~~~~~ @@ -252,7 +252,7 @@ fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &Local<'_>) -> O fn check<'tcx>( cx: &LateContext<'tcx>, - local: &'tcx Local<'tcx>, + local: &'tcx LetStmt<'tcx>, local_stmt: &'tcx Stmt<'tcx>, block: &'tcx Block<'tcx>, binding_id: HirId, @@ -363,9 +363,9 @@ fn check<'tcx>( } impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); - if let Local { + if let LetStmt { init: None, pat: &Pat { diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index cebd2385656..6cb84bb78b6 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -290,14 +290,21 @@ impl NonCopyConst { promoted: None, }; let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx); - let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, rustc_span::DUMMY_SP); + let result = cx + .tcx + .const_eval_global_id_for_typeck(param_env, cid, rustc_span::DUMMY_SP); self.is_value_unfrozen_raw(cx, result, ty) } fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { let args = cx.typeck_results().node_args(hir_id); - let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), rustc_span::DUMMY_SP); + let result = Self::const_eval_resolve( + cx.tcx, + cx.param_env, + ty::UnevaluatedConst::new(def_id, args), + rustc_span::DUMMY_SP, + ); self.is_value_unfrozen_raw(cx, result, ty) } diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs index 793a3a9545c..408216229a4 100644 --- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -219,7 +219,7 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t } }, // Raw pointers are `!Send` but allowed by the heuristic - ty::RawPtr(_) => true, + ty::RawPtr(_, _) => true, _ => false, } } @@ -229,7 +229,7 @@ fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> b for ty_node in target_ty.walk() { if let GenericArgKind::Type(inner_ty) = ty_node.unpack() { match inner_ty.kind() { - ty::RawPtr(_) => { + ty::RawPtr(_, _) => { return true; }, ty::Adt(adt_def, _) => { diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs index fbca4329342..127801de7de 100644 --- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{intravisit, Body, Expr, ExprKind, FnDecl, Let, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind}; +use rustc_hir::{intravisit, Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { } } } - if let ExprKind::Let(Let { pat, .. }) = expr.kind { + if let ExprKind::Let(LetExpr { pat, .. }) = expr.kind { apply_lint(cx, pat, DerefPossible::Possible); } } diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 2587b3881bb..896c99a7104 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -604,7 +604,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: match get_expr_use_or_unification_node(self.cx.tcx, e) { Some((Node::Stmt(_), _)) => (), - Some((Node::Local(l), _)) => { + Some((Node::LetStmt(l), _)) => { // Only trace simple bindings. e.g `let x = y;` if let PatKind::Binding(BindingAnnotation::NONE, id, _, None) = l.pat.kind { self.bindings.insert(id, args_idx); diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 831c291ed7c..f1db571e113 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -14,7 +14,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ - BindingAnnotation, Block, ByRef, Expr, ExprKind, Local, Node, PatKind, PathSegment, QPath, Stmt, StmtKind, + BindingAnnotation, Block, ByRef, Expr, ExprKind, LetStmt, Node, PatKind, PathSegment, QPath, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; @@ -109,7 +109,7 @@ fn find_let_else_ret_expression<'hir>(block: &'hir Block<'hir>) -> Option<&'hir } fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if let StmtKind::Let(Local { + if let StmtKind::Let(LetStmt { pat, init: Some(init_expr), els: Some(els), diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index ac29d27303c..7e71f48c6d9 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -108,7 +108,7 @@ impl EarlyLintPass for RawStrings { } } - let req = { + let mut req = { let mut following_quote = false; let mut req = 0; // `once` so a raw string ending in hashes is still checked @@ -136,7 +136,9 @@ impl EarlyLintPass for RawStrings { ControlFlow::Continue(num) | ControlFlow::Break(num) => num, } }; - + if self.allow_one_hash_in_raw_strings { + req = req.max(1); + } if req < max { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs index d0b37cd92e0..7f4735c6a88 100644 --- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs +++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs @@ -1,9 +1,9 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::get_enclosing_block; use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::source::snippet; -use hir::{Expr, ExprKind, HirId, Local, PatKind, PathSegment, QPath, StmtKind}; +use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { } if let StmtKind::Let(local) = stmt.kind - && let Local { + && let LetStmt { pat, init: Some(init), .. } = local && let PatKind::Binding(_, id, ident, _) = pat.kind @@ -77,37 +77,53 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { if let Some(expr) = visitor.read_zero_expr { let applicability = Applicability::MaybeIncorrect; match vec_init_kind { - VecInitKind::WithConstCapacity(len) => { - span_lint_and_sugg( + VecInitKind::WithConstCapacity(len) => span_lint_hir_and_then( + cx, + READ_ZERO_BYTE_VEC, + expr.hir_id, + expr.span, + "reading zero byte data to `Vec`", + |diag| { + diag.span_suggestion( + expr.span, + "try", + format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")), + applicability, + ); + }, + ), + VecInitKind::WithExprCapacity(hir_id) => { + let e = cx.tcx.hir().expect_expr(hir_id); + span_lint_hir_and_then( cx, READ_ZERO_BYTE_VEC, + expr.hir_id, expr.span, "reading zero byte data to `Vec`", - "try", - format!("{}.resize({len}, 0); {}", ident.as_str(), snippet(cx, expr.span, "..")), - applicability, + |diag| { + diag.span_suggestion( + expr.span, + "try", + format!( + "{}.resize({}, 0); {}", + ident.as_str(), + snippet(cx, e.span, ".."), + snippet(cx, expr.span, "..") + ), + applicability, + ); + }, ); }, - VecInitKind::WithExprCapacity(hir_id) => { - let e = cx.tcx.hir().expect_expr(hir_id); - span_lint_and_sugg( + _ => { + span_lint_hir( cx, READ_ZERO_BYTE_VEC, + expr.hir_id, expr.span, "reading zero byte data to `Vec`", - "try", - format!( - "{}.resize({}, 0); {}", - ident.as_str(), - snippet(cx, e.span, ".."), - snippet(cx, expr.span, "..") - ), - applicability, ); }, - _ => { - span_lint(cx, READ_ZERO_BYTE_VEC, expr.span, "reading zero byte data to `Vec`"); - }, } } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index c2673bc409f..435899ddaa7 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -1,5 +1,5 @@ use crate::rustc_lint::LintContext; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir}; use clippy_utils::get_parent_expr; use clippy_utils::sugg::Sugg; use hir::Param; @@ -273,9 +273,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { && ident == path.segments[0].ident && count_closure_usage(cx, block, path) == 1 { - span_lint( + span_lint_hir( cx, REDUNDANT_CLOSURE_CALL, + second.hir_id, second.span, "closure called just once immediately after it was declared", ); diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs index fb434fb7450..3bdf13dbbea 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_else.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -105,7 +105,7 @@ impl<'ast> Visitor<'ast> for BreakVisitor { fn visit_expr(&mut self, expr: &'ast Expr) { self.is_break = match expr.kind { ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true, - ExprKind::Match(_, ref arms) => arms.iter().all(|arm| + ExprKind::Match(_, ref arms, _) => arms.iter().all(|arm| arm.body.is_none() || arm.body.as_deref().is_some_and(|body| self.check_expr(body)) ), ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els), diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 6528a7b369f..0f579f779df 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -3,7 +3,7 @@ use clippy_utils::is_from_proc_macro; use clippy_utils::ty::needs_ordered_drop; use rustc_ast::Mutability; use rustc_hir::def::Res; -use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, Local, Node, Pat, PatKind, QPath}; +use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, LetStmt, Node, Pat, PatKind, QPath}; use rustc_hir_typeck::expr_use_visitor::PlaceBase; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -47,7 +47,7 @@ declare_clippy_lint! { declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]); impl<'tcx> LateLintPass<'tcx> for RedundantLocals { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { if !local.span.is_desugaring(DesugaringKind::Async) // the pattern is a single by-value binding && let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs index 079e6500e3c..96f6f0ec36f 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs @@ -131,7 +131,7 @@ fn extract_primty(ty_kind: &hir::TyKind<'_>) -> Option<hir::PrimTy> { } impl LateLintPass<'_> for RedundantTypeAnnotations { - fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'tcx>) { + fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::LetStmt<'tcx>) { if !is_lint_allowed(cx, REDUNDANT_TYPE_ANNOTATIONS, local.hir_id) // type annotation part && !local.span.from_expansion() diff --git a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs index ca7a0c7c87b..c227b5b22f4 100644 --- a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use clippy_utils::{is_from_proc_macro, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization { self.searcher = None; } - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { if let Some(init_expr) = local.init && let PatKind::Binding(BindingAnnotation::MUT, id, _, None) = local.pat.kind && !in_external_macro(cx.sess(), local.span) diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 19697527467..8bc24eda465 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; @@ -380,7 +380,7 @@ fn check_final_expr<'tcx>( return; } - emit_return_lint(cx, ret_span, semi_spans, &replacement); + emit_return_lint(cx, ret_span, semi_spans, &replacement, expr.hir_id); }, ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); @@ -415,18 +415,31 @@ fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool { contains_if(expr, false) } -fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: &RetReplacement<'_>) { +fn emit_return_lint( + cx: &LateContext<'_>, + ret_span: Span, + semi_spans: Vec<Span>, + replacement: &RetReplacement<'_>, + at: HirId, +) { if ret_span.from_expansion() { return; } - span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - let suggestions = std::iter::once((ret_span, replacement.to_string())) - .chain(semi_spans.into_iter().map(|span| (span, String::new()))) - .collect(); + span_lint_hir_and_then( + cx, + NEEDLESS_RETURN, + at, + ret_span, + "unneeded `return` statement", + |diag| { + let suggestions = std::iter::once((ret_span, replacement.to_string())) + .chain(semi_spans.into_iter().map(|span| (span, String::new()))) + .collect(); - diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); - }); + diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); + }, + ); } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { @@ -452,8 +465,8 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) // Go backwards while encountering whitespace and extend the given Span to that point. fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { - let ws = [' ', '\t', '\n']; - if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) { + let ws = [b' ', b'\t', b'\n']; + if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { let len = prev_source.len() - non_ws_pos - 1; return sp.with_lo(sp.lo() - BytePos::from_usize(len)); } diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index c74364d89d6..d98b37bda35 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::hir_id::ItemLocalId; -use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp}; +use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, LetExpr, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol}; @@ -238,10 +238,10 @@ fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<' let init = match node { Node::Arm(_) | Node::Pat(_) => continue, Node::Expr(expr) => match expr.kind { - ExprKind::Match(e, _, _) | ExprKind::Let(&Let { init: e, .. }) => Some(e), + ExprKind::Match(e, _, _) | ExprKind::Let(&LetExpr { init: e, .. }) => Some(e), _ => None, }, - Node::Local(local) => local.init, + Node::LetStmt(local) => local.init, _ => None, }; return init; diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index f8726aa173a..d3540bc8e1c 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -7,7 +7,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{self as hir}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::{GenericArgKind, Ty, TypeAndMut}; +use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, DUMMY_SP}; @@ -199,7 +199,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> { false }, rustc_middle::ty::Array(ty, _) - | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. }) + | rustc_middle::ty::RawPtr(ty, _) | rustc_middle::ty::Ref(_, ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty), _ => false, diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index 756e47cbdf0..01f0e3cfadb 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty, TypeAndMut}; +use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -107,7 +107,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( && METHODS.iter().any(|m| *m == method_ident) // Get the pointee type - && let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = + && let ty::RawPtr(pointee_ty, _) = cx.typeck_results().expr_ty(ptr_self).kind() { return Some((*pointee_ty, count)); diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index c0e4f3c368a..cf839941123 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -109,6 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { sym::core => (STD_INSTEAD_OF_CORE, "std", "core"), sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"), _ => { + self.prev_span = first_segment.ident.span; return; }, }, @@ -116,6 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { if cx.tcx.crate_name(def_id.krate) == sym::core { (ALLOC_INSTEAD_OF_CORE, "alloc", "core") } else { + self.prev_span = first_segment.ident.span; return; } }, diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index 60e9d262e7e..ab1b3043f0c 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -552,7 +552,7 @@ fn ident_difference_expr_with_base_location( | (Gen(_, _, _), Gen(_, _, _)) | (Block(_, _), Block(_, _)) | (Closure(_), Closure(_)) - | (Match(_, _), Match(_, _)) + | (Match(_, _, _), Match(_, _, _)) | (Loop(_, _, _), Loop(_, _, _)) | (ForLoop { .. }, ForLoop { .. }) | (While(_, _, _), While(_, _, _)) diff --git a/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs b/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs index 1af3733ebfa..f8bdb866ca3 100644 --- a/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs +++ b/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// static BUF: String = const { String::new() }; /// } /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST, perf, "suggest using `const` in `thread_local!` macro" diff --git a/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs index c4b9d82fc73..102aee1cb95 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/crosspointer_transmute.rs @@ -7,8 +7,8 @@ use rustc_middle::ty::{self, Ty}; /// Checks for `crosspointer_transmute` lint. /// Returns `true` if it's triggered, otherwise returns `false`. pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::RawPtr(from_ptr), _) if from_ptr.ty == to_ty => { + match (*from_ty.kind(), *to_ty.kind()) { + (ty::RawPtr(from_ptr_ty, _), _) if from_ptr_ty == to_ty => { span_lint( cx, CROSSPOINTER_TRANSMUTE, @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty ); true }, - (_, ty::RawPtr(to_ptr)) if to_ptr.ty == from_ty => { + (_, ty::RawPtr(to_ptr_ty, _)) if to_ptr_ty == from_ty => { span_lint( cx, CROSSPOINTER_TRANSMUTE, diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index e47b14bf63b..3c11b481310 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -514,7 +514,7 @@ declare_clippy_lint! { /// ^^^^ ^^ `bool::then` only executes the closure if the condition is true! /// } /// ``` - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub EAGER_TRANSMUTE, correctness, "eager evaluation of `transmute`" diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index 4ae4359eea0..1476ea8e7a4 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - (ty::RawPtr(_), ty::RawPtr(to_ty)) => { + (ty::RawPtr(_, _), ty::RawPtr(to_ty, to_mutbl)) => { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( "transmute from a pointer to a pointer", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg = arg.as_ty(Ty::new_ptr(cx.tcx, *to_ty)); + let sugg = arg.as_ty(Ty::new_ptr(cx.tcx, *to_ty, *to_mutbl)); diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified); } }, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 4ab3afbe714..cf78709583c 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -20,7 +20,7 @@ pub(super) fn check<'tcx>( msrv: &Msrv, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => { + (ty::RawPtr(from_ptr_ty, _), ty::Ref(_, to_ref_ty, mutbl)) => { span_lint_and_then( cx, TRANSMUTE_PTR_TO_REF, @@ -44,7 +44,7 @@ pub(super) fn check<'tcx>( } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() } - } else if from_ptr_ty.ty == *to_ref_ty { + } else if *from_ptr_ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { if msrv.meets(msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 6c885ebdea1..73321c56f3f 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( ) -> bool { let mut triggered = false; - if let (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) = (&from_ty.kind(), &to_ty.kind()) { + if let (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) = (*from_ty.kind(), *to_ty.kind()) { if let ty::Slice(slice_ty) = *ty_from.kind() && ty_to.is_str() && let ty::Uint(ty::UintTy::U8) = slice_ty.kind() @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>( { let Some(top_crate) = std_or_core(cx) else { return true }; - let postfix = if *from_mutbl == Mutability::Mut { "_mut" } else { "" }; + let postfix = if from_mutbl == Mutability::Mut { "_mut" } else { "" }; let snippet = snippet(cx, arg.span, ".."); @@ -53,18 +53,10 @@ pub(super) fn check<'tcx>( "transmute from a reference to a reference", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let ty_from_and_mut = ty::TypeAndMut { - ty: *ty_from, - mutbl: *from_mutbl, - }; - let ty_to_and_mut = ty::TypeAndMut { - ty: *ty_to, - mutbl: *to_mutbl, - }; let sugg_paren = arg - .as_ty(Ty::new_ptr(cx.tcx, ty_from_and_mut)) - .as_ty(Ty::new_ptr(cx.tcx, ty_to_and_mut)); - let sugg = if *to_mutbl == Mutability::Mut { + .as_ty(Ty::new_ptr(cx.tcx, ty_from, from_mutbl)) + .as_ty(Ty::new_ptr(cx.tcx, ty_to, to_mutbl)); + let sugg = if to_mutbl == Mutability::Mut { sugg_paren.mut_addr_deref() } else { sugg_paren.addr_deref() diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index a6f03c85b4f..33c4031fa87 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_c_void; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::{self, GenericArgsRef, IntTy, Ty, TypeAndMut, UintTy}; +use rustc_middle::ty::{self, GenericArgsRef, IntTy, Ty, UintTy}; #[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( @@ -45,8 +45,8 @@ pub(super) fn check<'tcx>( // ptr <-> ptr (ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty)) - if matches!(from_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) - && matches!(to_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) => + if matches!(from_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_, _)) + && matches!(to_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_, _)) => { from_ty = from_sub_ty; to_ty = to_sub_ty; @@ -196,21 +196,21 @@ fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: T let (from_fat_ptr, to_fat_ptr) = loop { break match (from_ty.kind(), to_ty.kind()) { ( - &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })), - &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })), + &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(from_sub_ty, _)), + &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(to_sub_ty, _)), ) => { - from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_)); + from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_, _)); from_ty = from_sub_ty; - to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_)); + to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_, _)); to_ty = to_sub_ty; continue; }, - (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _) + (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(unsized_ty, _)), _) if !unsized_ty.is_sized(cx.tcx, cx.param_env) => { (true, false) }, - (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))) + (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(unsized_ty, _))) if !unsized_ty.is_sized(cx.tcx, cx.param_env) => { (false, true) diff --git a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs index 088c8fda87a..70628f3d4f4 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>( to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, ) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { + match (*from_ty.kind(), *to_ty.kind()) { _ if from_ty == to_ty && !from_ty.has_erased_regions() => { span_lint( cx, @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>( ); true }, - (ty::Ref(_, rty, rty_mutbl), ty::RawPtr(ptr_ty)) => { + (ty::Ref(_, rty, rty_mutbl), ty::RawPtr(ptr_ty, ptr_mutbl)) => { // No way to give the correct suggestion here. Avoid linting for now. if !rty.has_erased_regions() { span_lint_and_then( @@ -35,15 +35,10 @@ pub(super) fn check<'tcx>( "transmute from a reference to a pointer", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let rty_and_mut = ty::TypeAndMut { - ty: *rty, - mutbl: *rty_mutbl, - }; - - let sugg = if *ptr_ty == rty_and_mut { + let sugg = if ptr_ty == rty && rty_mutbl == ptr_mutbl { arg.as_ty(to_ty) } else { - arg.as_ty(Ty::new_ptr(cx.tcx, rty_and_mut)).as_ty(to_ty) + arg.as_ty(Ty::new_ptr(cx.tcx, rty, rty_mutbl)).as_ty(to_ty) }; diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified); @@ -53,7 +48,7 @@ pub(super) fn check<'tcx>( } true }, - (ty::Int(_) | ty::Uint(_), ty::RawPtr(_)) => { + (ty::Int(_) | ty::Uint(_), ty::RawPtr(_, _)) => { span_lint_and_then( cx, USELESS_TRANSMUTE, diff --git a/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs index d1965565b92..ed815884a76 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/wrong_transmute.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::{self, Ty}; /// Returns `true` if it's triggered, otherwise returns `false`. pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - (ty::Float(_) | ty::Char, ty::Ref(..) | ty::RawPtr(_)) => { + (ty::Float(_) | ty::Char, ty::Ref(..) | ty::RawPtr(_, _)) => { span_lint( cx, WRONG_TRANSMUTE, diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs index 0d84a9ab395..564b065d0ba 100644 --- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs @@ -159,7 +159,7 @@ fn all_bindings_are_for_conv<'tcx>( .iter() .map(|node| match node { Node::Pat(pat) => kind.eq(&pat.kind).then_some(pat.hir_id), - Node::Local(l) => Some(l.hir_id), + Node::LetStmt(l) => Some(l.hir_id), _ => None, }) .all_equal() @@ -170,7 +170,7 @@ fn all_bindings_are_for_conv<'tcx>( && local_parents.first().is_some_and(|node| { let Some(ty) = match node { Node::Pat(pat) => Some(pat.hir_id), - Node::Local(l) => Some(l.hir_id), + Node::LetStmt(l) => Some(l.hir_id), _ => None, } .map(|hir_id| cx.typeck_results().node_type(hir_id)) else { diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index bdef82e9c5e..0802cb2b7c7 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -12,7 +12,7 @@ mod vec_box; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem, + Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, LetStmt, MutTy, QPath, TraitItem, TraitItemKind, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; @@ -392,6 +392,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &hir::FieldDef<'tcx>) { + if field.span.from_expansion() { + return; + } + let is_exported = cx.effective_visibilities.is_exported(field.def_id); self.check_ty( @@ -421,7 +425,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } } - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if let Some(ty) = local.ty { self.check_ty( cx, diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs index a0d609501a0..37437cbfbec 100644 --- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs +++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs @@ -4,7 +4,7 @@ use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath, TyKind}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir_analysis::lower_ty; use rustc_lint::LateContext; use rustc_middle::ty::TypeVisitableExt; use rustc_span::symbol::sym; @@ -56,10 +56,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: }; let inner_span = match qpath_generic_tys(inner_qpath).next() { Some(hir_ty) => { - // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use + // Reallocation of a fat pointer causes it to become thin. `lower_ty` is safe to use // here because `mod.rs` guarantees this lint is only run on types outside of bodies and // is not run on locals. - let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let ty = lower_ty(cx.tcx, hir_ty); if ty.has_escaping_bound_vars() || !ty.is_sized(cx.tcx, cx.param_env) { return false; } diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs index 7926738d68f..29996a6f783 100644 --- a/src/tools/clippy/clippy_lints/src/types/vec_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, GenericArg, LangItem, QPath, TyKind}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir_analysis::lower_ty; use rustc_lint::LateContext; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::TypeVisitableExt; @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( && let Some(GenericArg::Type(boxed_ty)) = last.args.first() // extract allocator from the Box for later && let boxed_alloc_ty = last.args.get(1) - && let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty) + && let ty_ty = lower_ty(cx.tcx, boxed_ty) && !ty_ty.has_escaping_bound_vars() && ty_ty.is_sized(cx.tcx, cx.param_env) && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()) @@ -55,7 +55,7 @@ pub(super) fn check<'tcx>( } }, (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) => - hir_ty_to_ty(cx.tcx, l) == hir_ty_to_ty(cx.tcx, r), + lower_ty(cx.tcx, l) == lower_ty(cx.tcx, r), _ => false } { diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index 224ec475c51..8764e3006c6 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{expr_or_init, get_trait_def_id, path_def_id}; +use clippy_utils::{expr_or_init, fn_def_id_with_node_args, path_def_id}; use rustc_ast::BinOpKind; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; @@ -7,7 +7,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_body, walk_expr, FnKind, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Node, QPath, TyKind}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; @@ -19,11 +19,11 @@ use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; declare_clippy_lint! { /// ### What it does - /// Checks that there isn't an infinite recursion in `PartialEq` trait - /// implementation. + /// Checks that there isn't an infinite recursion in trait + /// implementations. /// /// ### Why is this bad? - /// This is a hard to find infinite recursion which will crashing any code + /// This is a hard to find infinite recursion that will crash any code. /// using it. /// /// ### Example @@ -42,7 +42,7 @@ declare_clippy_lint! { /// Use instead: /// /// In such cases, either use `#[derive(PartialEq)]` or don't implement it. - #[clippy::version = "1.76.0"] + #[clippy::version = "1.77.0"] pub UNCONDITIONAL_RECURSION, suspicious, "detect unconditional recursion in some traits implementation" @@ -74,7 +74,7 @@ fn get_hir_ty_def_id<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: rustc_hir::Ty<'tcx>) -> Op match qpath { QPath::Resolved(_, path) => path.res.opt_def_id(), QPath::TypeRelative(_, _) => { - let ty = hir_ty_to_ty(tcx, &hir_ty); + let ty = lower_ty(tcx, &hir_ty); match ty.kind() { ty::Alias(ty::Projection, proj) => { @@ -201,7 +201,6 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca } } -#[allow(clippy::unnecessary_def_path)] fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) { let args = cx .tcx @@ -224,7 +223,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local && let Some(trait_) = impl_.of_trait && let Some(trait_def_id) = trait_.trait_def_id() // The trait is `ToString`. - && Some(trait_def_id) == get_trait_def_id(cx, &["alloc", "string", "ToString"]) + && cx.tcx.is_diagnostic_item(sym::ToString, trait_def_id) { let is_bad = match expr.kind { ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => { @@ -291,7 +290,6 @@ where self.map } - #[allow(clippy::unnecessary_def_path)] fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { if self.found_default_call { return; @@ -303,7 +301,7 @@ where && is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id) && let Some(method_def_id) = path_def_id(self.cx, f) && let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id) - && Some(trait_def_id) == get_trait_def_id(self.cx, &["core", "default", "Default"]) + && self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id) { self.found_default_call = true; span_error(self.cx, self.method_span, expr); @@ -312,10 +310,9 @@ where } impl UnconditionalRecursion { - #[allow(clippy::unnecessary_def_path)] fn init_default_impl_for_type_if_needed(&mut self, cx: &LateContext<'_>) { if self.default_impl_for_type.is_empty() - && let Some(default_trait_id) = get_trait_def_id(cx, &["core", "default", "Default"]) + && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) { let impls = cx.tcx.trait_impls_of(default_trait_id); for (ty, impl_def_ids) in impls.non_blanket_impls() { @@ -394,6 +391,34 @@ impl UnconditionalRecursion { } } +fn check_from(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, expr: &Expr<'_>) { + let Some(sig) = cx + .typeck_results() + .liberated_fn_sigs() + .get(cx.tcx.local_def_id_to_hir_id(method_def_id)) + else { + return; + }; + + // Check if we are calling `Into::into` where the node args match with our `From::from` signature: + // From::from signature: fn(S1) -> S2 + // <S1 as Into<S2>>::into(s1), node_args=[S1, S2] + // If they do match, then it must mean that it is the blanket impl, + // which calls back into our `From::from` again (`Into` is not specializable). + // rustc's unconditional_recursion already catches calling `From::from` directly + if let Some((fn_def_id, node_args)) = fn_def_id_with_node_args(cx, expr) + && let [s1, s2] = **node_args + && let (Some(s1), Some(s2)) = (s1.as_type(), s2.as_type()) + && let Some(trait_def_id) = cx.tcx.trait_of_item(fn_def_id) + && cx.tcx.is_diagnostic_item(sym::Into, trait_def_id) + && get_impl_trait_def_id(cx, method_def_id) == cx.tcx.get_diagnostic_item(sym::From) + && s1 == sig.inputs()[0] + && s2 == sig.output() + { + span_error(cx, method_span, expr); + } +} + impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion { fn check_fn( &mut self, @@ -410,10 +435,11 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion { // Doesn't have a conditional return. && !has_conditional_return(body, expr) { - if name.name == sym::eq || name.name == sym::ne { - check_partial_eq(cx, method_span, method_def_id, name, expr); - } else if name.name == sym::to_string { - check_to_string(cx, method_span, method_def_id, name, expr); + match name.name { + sym::eq | sym::ne => check_partial_eq(cx, method_span, method_def_id, name, expr), + sym::to_string => check_to_string(cx, method_span, method_def_id, name, expr), + sym::from => check_from(cx, method_span, method_def_id, expr), + _ => {}, } self.check_default_new(cx, decl, body, method_span, method_def_id); } diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 0efa65b28e2..5fe4b74b3a7 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -158,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) { - let (hir::StmtKind::Let(&hir::Local { init: Some(expr), .. }) + let (hir::StmtKind::Let(&hir::LetStmt { init: Some(expr), .. }) | hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr)) = stmt.kind else { @@ -342,7 +342,7 @@ fn block_parents_have_safety_comment( ) -> bool { let (span, hir_id) = match cx.tcx.parent_hir_node(id) { Node::Expr(expr) => match cx.tcx.parent_hir_node(expr.hir_id) { - Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id), + Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), span, @@ -358,12 +358,12 @@ fn block_parents_have_safety_comment( }, Node::Stmt(hir::Stmt { kind: - hir::StmtKind::Let(hir::Local { span, hir_id, .. }) + hir::StmtKind::Let(hir::LetStmt { span, hir_id, .. }) | hir::StmtKind::Expr(hir::Expr { span, hir_id, .. }) | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), .. }) - | Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id), + | Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), span, @@ -603,7 +603,7 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> { for (_, node) in map.parent_iter(body.hir_id) { match node { Node::Expr(e) => span = e.span, - Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (), + Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::LetStmt(_) => (), Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), .. diff --git a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs index 6732a43a19e..88039372ebd 100644 --- a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs +++ b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnRetTy, TyKind, UnOp}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; @@ -71,7 +71,7 @@ impl LateLintPass<'_> for UninhabitedReferences { } if let FnRetTy::Return(hir_ty) = fndecl.output && let TyKind::Ref(_, mut_ty) = hir_ty.kind - && hir_ty_to_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.param_env) + && lower_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.param_env) { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index 9406d160769..ffb909d7907 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -4,14 +4,14 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind}; +use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, LetStmt, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::{in_external_macro, is_from_async_await}; use rustc_middle::ty; use super::LET_UNIT_VALUE; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { // skip `let () = { ... }` if let PatKind::Tuple(fields, ..) = local.pat.kind && fields.is_empty() @@ -102,7 +102,7 @@ fn expr_needs_inferred_result<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) - return false; } while let Some(id) = locals_to_check.pop() { - if let Node::Local(l) = cx.tcx.parent_hir_node(id) { + if let Node::LetStmt(l) = cx.tcx.parent_hir_node(id) { if !l.ty.map_or(true, |ty| matches!(ty.kind, TyKind::Infer)) { return false; } diff --git a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs index 0abd48e6423..e016bd3434b 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs @@ -3,7 +3,7 @@ mod unit_arg; mod unit_cmp; mod utils; -use rustc_hir::{Expr, Local}; +use rustc_hir::{Expr, LetStmt}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -99,7 +99,7 @@ declare_clippy_lint! { declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]); impl<'tcx> LateLintPass<'tcx> for UnitTypes { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { let_unit_value::check(cx, local); } diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 7246214f9bf..d4dd31e1178 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -242,6 +242,8 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us |k| matches!(k, Box(_)), |k| always_pat!(k, Box(p) => p), ), + // FIXME(deref_patterns): Should we merge patterns here? + Deref(_) => false, // Transform `&mut x | ... | &mut y` into `&mut (x | y)`. Ref(target, Mutability::Mut) => extend_with_matching( target, start, alternatives, diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index eb64dd633f6..d8c5f1b7382 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -1,7 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks}; -use hir::{ExprKind, PatKind}; +use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -131,26 +131,26 @@ fn non_consuming_ok_arm<'a>(cx: &LateContext<'a>, arm: &hir::Arm<'a>) -> bool { fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { match expr.kind { hir::ExprKind::If(cond, _, _) - if let ExprKind::Let(hir::Let { pat, init, .. }) = cond.kind + if let ExprKind::Let(hir::LetExpr { pat, init, .. }) = cond.kind && is_ok_wild_or_dotdot_pattern(cx, pat) && let Some(op) = should_lint(cx, init) => { - emit_lint(cx, cond.span, op, &[pat.span]); + emit_lint(cx, cond.span, cond.hir_id, op, &[pat.span]); }, // we will capture only the case where the match is Ok( ) or Err( ) // prefer to match the minimum possible, and expand later if needed // to avoid false positives on something as used as this hir::ExprKind::Match(expr, [arm1, arm2], hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => { if non_consuming_ok_arm(cx, arm1) && non_consuming_err_arm(cx, arm2) { - emit_lint(cx, expr.span, op, &[arm1.pat.span]); + emit_lint(cx, expr.span, expr.hir_id, op, &[arm1.pat.span]); } if non_consuming_ok_arm(cx, arm2) && non_consuming_err_arm(cx, arm1) { - emit_lint(cx, expr.span, op, &[arm2.pat.span]); + emit_lint(cx, expr.span, expr.hir_id, op, &[arm2.pat.span]); } }, hir::ExprKind::Match(_, _, hir::MatchSource::Normal) => {}, _ if let Some(op) = should_lint(cx, expr) => { - emit_lint(cx, expr.span, op, &[]); + emit_lint(cx, expr.span, expr.hir_id, op, &[]); }, _ => {}, }; @@ -279,7 +279,7 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option<IoOp> { } } -fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) { +fn emit_lint(cx: &LateContext<'_>, span: Span, at: HirId, op: IoOp, wild_cards: &[Span]) { let (msg, help) = match op { IoOp::AsyncRead(false) => ( "read amount is not handled", @@ -301,7 +301,7 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) { IoOp::SyncWrite(true) | IoOp::AsyncWrite(true) => ("written amount is not handled", None), }; - span_lint_and_then(cx, UNUSED_IO_AMOUNT, span, msg, |diag| { + span_lint_hir_and_then(cx, UNUSED_IO_AMOUNT, at, span, msg, |diag| { if let Some(help_str) = help { diag.help(help_str); } diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs index f1d0c22b1ae..e6f799335d7 100644 --- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs +++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs @@ -1,9 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators}; use rustc_ast::Mutability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_session::declare_lint_pass; @@ -79,13 +79,15 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { } if !vis.found_peek_call { - span_lint_and_help( + span_lint_hir_and_then( cx, UNUSED_PEEKABLE, + local.hir_id, ident.span, "`peek` never called on `Peekable` iterator", - None, - "consider removing the call to `peekable`", + |diag| { + diag.help("consider removing the call to `peekable`"); + }, ); } } @@ -188,7 +190,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { }, } }, - Node::Local(Local { init: Some(init), .. }) => { + Node::LetStmt(LetStmt { init: Some(init), .. }) => { if arg_is_mut_peekable(self.cx, init) { self.found_peek_call = true; } diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index a1b08d105b9..a6b411d6c0f 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -8,11 +8,12 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor}; use rustc_hir::{ - self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArg, GenericArgsParentheses, GenericParam, GenericParamKind, - HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, + self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl, + ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, }; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty as MiddleTy; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -95,10 +96,9 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { let stack_item = if let ItemKind::Impl(Impl { self_ty, generics, .. }) = item.kind && let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind && let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args - && parameters.as_ref().map_or(true, |params| { - params.parenthesized == GenericArgsParentheses::No - && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) - }) + && parameters + .as_ref() + .map_or(true, |params| params.parenthesized == GenericArgsParentheses::No) && !item.span.from_expansion() && !is_from_proc_macro(cx, item) // expensive, should be last check @@ -193,7 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } fn check_body(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) { - // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies + // `lower_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`. // However the `node_type()` method can *only* be called in bodies. if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() { @@ -224,9 +224,14 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && let ty = if in_body > 0 { cx.typeck_results().node_type(hir_ty.hir_id) } else { - hir_ty_to_ty(cx.tcx, hir_ty) + lower_ty(cx.tcx, hir_ty) } - && same_type_and_consts(ty, cx.tcx.type_of(impl_id).instantiate_identity()) + && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity() + && same_type_and_consts(ty, impl_ty) + // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that + // the lifetime parameters of `ty` are ellided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in + // which case we must still trigger the lint. + && (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty)) { span_lint(cx, hir_ty.span); } @@ -318,3 +323,37 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { span_lint(cx, span); } } + +/// Returns `true` if types `a` and `b` have the same lifetime parameters, otherwise returns +/// `false`. +/// +/// This function does not check that types `a` and `b` are the same types. +fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { + use rustc_middle::ty::{Adt, GenericArgKind}; + match (&a.kind(), &b.kind()) { + (&Adt(_, args_a), &Adt(_, args_b)) => { + args_a + .iter() + .zip(args_b.iter()) + .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) { + // TODO: Handle inferred lifetimes + (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), + _ => true, + }) + }, + _ => a == b, + } +} + +/// Returns `true` if `ty` has no lifetime parameter, otherwise returns `false`. +fn has_no_lifetime(ty: MiddleTy<'_>) -> bool { + use rustc_middle::ty::{Adt, GenericArgKind}; + match ty.kind() { + &Adt(_, args) => !args + .iter() + // TODO: Handle inferred lifetimes + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(..))), + _ => true, + } +} diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 187bfda129c..5319915b2ea 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -689,6 +689,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { kind!("Box({pat})"); self.pat(pat); }, + PatKind::Deref(pat) => { + bind!(self, pat); + kind!("Deref({pat})"); + self.pat(pat); + }, PatKind::Ref(pat, muta) => { bind!(self, pat); kind!("Ref({pat}, Mutability::{muta:?})"); diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 97b509a84f9..c56c8ddc7a9 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -925,7 +925,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { && let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr)) && match_type(self.cx, expr_ty, &paths::LINT) { - if let hir::def::Res::Def(DefKind::Static(..), _) = path.res { + if let hir::def::Res::Def(DefKind::Static { .. }, _) = path.res { let lint_name = last_path_segment(qpath).ident.name; self.lints.push(sym_to_string(lint_name).to_ascii_lowercase()); } else if let Some(local) = get_parent_local(self.cx, expr) { @@ -1006,7 +1006,7 @@ fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) - fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { match cx.tcx.parent_hir_node(hir_id) { - hir::Node::Local(local) => Some(local), + hir::Node::LetStmt(local) => Some(local), hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id), _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 38c832931fc..304a1379374 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -217,13 +217,13 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve match peel_hir_expr_refs(expr).0.kind { ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { - if let Node::Local(Local { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { + if let Node::LetStmt(Local { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { path_to_matched_type(cx, init) } else { None } }, - Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path( + Res::Def(DefKind::Static { .. }, def_id) => read_mir_alloc_def_path( cx, cx.tcx.eval_static_initializer(def_id).ok()?.inner(), cx.tcx.type_of(def_id).instantiate_identity(), diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 7cfcf3fe946..27ead55bf39 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -9,7 +9,7 @@ use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{get_parent_expr, higher, is_trait_method}; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Local, Mutability, Node, Pat, PatKind}; +use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::layout::LayoutOf; @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { match cx.tcx.parent_hir_node(expr.hir_id) { // search for `let foo = vec![_]` expressions where all uses of `foo` // adjust to slices or call a method that exist on slices (e.g. len) - Node::Local(Local { + Node::LetStmt(LetStmt { ty: None, pat: Pat { @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { } }, // if the local pattern has a specified type, do not lint. - Node::Local(Local { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => { + Node::LetStmt(LetStmt { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => { self.span_to_lint_map.insert(callsite, None); }, // search for `for _ in vec![...]` diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index ac3b2bdaf65..b58a4fb8474 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -7,7 +7,7 @@ use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - BindingAnnotation, Block, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp, + BindingAnnotation, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -157,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { self.searcher = None; } - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { if let Some(init_expr) = local.init && let PatKind::Binding(BindingAnnotation::MUT, id, name, None) = local.pat.kind && !in_external_macro(cx.sess(), local.span) diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 5410e8ac117..436f0cb79fb 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -213,7 +213,7 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { // Allow skipping imports containing user configured segments, // i.e. "...::utils::...::*" if user put `allowed-wildcard-imports = ["utils"]` in `Clippy.toml` fn is_allowed_via_config(segments: &[PathSegment<'_>], allowed_segments: &FxHashSet<String>) -> bool { - // segment matching need to be exact instead of using 'contains', in case user unintentionaly put + // segment matching need to be exact instead of using 'contains', in case user unintentionally put // a single character in the config thus skipping most of the warnings. segments.iter().any(|seg| allowed_segments.contains(seg.ident.as_str())) } diff --git a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs new file mode 100644 index 00000000000..143fecdd237 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs @@ -0,0 +1,154 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher::VecArgs; +use clippy_utils::source::snippet; +use clippy_utils::visitors::for_each_expr; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{ExprKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, ConstKind, Ty}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks for array or vec initializations which call a function or method, + /// but which have a repeat count of zero. + /// + /// ### Why is this bad? + /// Such an initialization, despite having a repeat length of 0, will still call the inner function. + /// This may not be obvious and as such there may be unintended side effects in code. + /// + /// ### Example + /// ```no_run + /// fn side_effect() -> i32 { + /// println!("side effect"); + /// 10 + /// } + /// let a = [side_effect(); 0]; + /// ``` + /// Use instead: + /// ```no_run + /// fn side_effect() -> i32 { + /// println!("side effect"); + /// 10 + /// } + /// side_effect(); + /// let a: [i32; 0] = []; + /// ``` + #[clippy::version = "1.75.0"] + pub ZERO_REPEAT_SIDE_EFFECTS, + suspicious, + "usage of zero-sized initializations of arrays or vecs causing side effects" +} + +declare_lint_pass!(ZeroRepeatSideEffects => [ZERO_REPEAT_SIDE_EFFECTS]); + +impl LateLintPass<'_> for ZeroRepeatSideEffects { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>) { + if let Some(args) = VecArgs::hir(cx, expr) + && let VecArgs::Repeat(inner_expr, len) = args + && let ExprKind::Lit(l) = len.kind + && let LitKind::Int(i, _) = l.node + && i.0 == 0 + { + inner_check(cx, expr, inner_expr, true); + } else if let ExprKind::Repeat(inner_expr, _) = expr.kind + && let ty::Array(_, cst) = cx.typeck_results().expr_ty(expr).kind() + && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() + && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) + && element_count == 0 + { + inner_check(cx, expr, inner_expr, false); + } + } +} + +fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: &'_ rustc_hir::Expr<'_>, is_vec: bool) { + // check if expr is a call or has a call inside it + if for_each_expr(inner_expr, |x| { + if let ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) = x.kind { + std::ops::ControlFlow::Break(()) + } else { + std::ops::ControlFlow::Continue(()) + } + }) + .is_some() + { + let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); + let return_type = cx.typeck_results().expr_ty(expr); + + if let Node::LetStmt(l) = parent_hir_node { + array_span_lint( + cx, + l.span, + inner_expr.span, + l.pat.span, + Some(return_type), + is_vec, + false, + ); + } else if let Node::Expr(x) = parent_hir_node + && let ExprKind::Assign(l, _, _) = x.kind + { + array_span_lint(cx, x.span, inner_expr.span, l.span, Some(return_type), is_vec, true); + } else { + span_lint_and_sugg( + cx, + ZERO_REPEAT_SIDE_EFFECTS, + expr.span.source_callsite(), + "function or method calls as the initial value in zero-sized array initializers may cause side effects", + "consider using", + format!( + "{{ {}; {}[] as {return_type} }}", + snippet(cx, inner_expr.span.source_callsite(), ".."), + if is_vec { "vec!" } else { "" }, + ), + Applicability::Unspecified, + ); + } + } +} + +fn array_span_lint( + cx: &LateContext<'_>, + expr_span: Span, + func_call_span: Span, + variable_name_span: Span, + expr_ty: Option<Ty<'_>>, + is_vec: bool, + is_assign: bool, +) { + let has_ty = expr_ty.is_some(); + + span_lint_and_sugg( + cx, + ZERO_REPEAT_SIDE_EFFECTS, + expr_span.source_callsite(), + "function or method calls as the initial value in zero-sized array initializers may cause side effects", + "consider using", + format!( + "{}; {}{}{} = {}[]{}{}", + snippet(cx, func_call_span.source_callsite(), ".."), + if has_ty && !is_assign { "let " } else { "" }, + snippet(cx, variable_name_span.source_callsite(), ".."), + if let Some(ty) = expr_ty + && !is_assign + { + format!(": {ty}") + } else { + String::new() + }, + if is_vec { "vec!" } else { "" }, + if let Some(ty) = expr_ty + && is_assign + { + format!(" as {ty}") + } else { + String::new() + }, + if is_assign { "" } else { ";" } + ), + Applicability::Unspecified, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs index 4aaf3b0a0b6..d1f7c6417c7 100644 --- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs +++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item}; use rustc_hir::{self as hir, HirId, ItemKind, Node}; -use rustc_hir_analysis::hir_ty_to_ty; +use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::{Adt, Ty, TypeVisitableExt}; @@ -91,5 +91,5 @@ fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'t None } }) - .unwrap_or_else(|| hir_ty_to_ty(cx.tcx, hir_ty)) + .unwrap_or_else(|| lower_ty(cx.tcx, hir_ty)) } diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index bf55040ddbc..d2bb719a517 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.78" +version = "0.1.79" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 3874c1169e4..f7532121aeb 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -198,7 +198,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { }, (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), - (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm), + (Match(ls, la, lkind), Match(rs, ra, rkind)) => (lkind == rkind) && eq_expr(ls, rs) && over(la, ra, eq_arm), ( Closure(box ast::Closure { binder: lb, diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index e75d5953fae..6b86630339f 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -10,8 +10,10 @@ use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{alloc_range, Scalar}; +use rustc_middle::mir::ConstValue; use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy}; use rustc_middle::{bug, mir, span_bug}; +use rustc_span::def_id::DefId; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::SyntaxContext; use rustc_target::abi::Size; @@ -307,6 +309,12 @@ impl ConstantSource { } } +/// Attempts to check whether the expression is a constant representing an empty slice, str, array, +/// etc… +pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option<bool> { + ConstEvalLateContext::new(lcx, lcx.typeck_results()).expr_is_empty(e) +} + /// Attempts to evaluate the expression as a constant. pub fn constant<'tcx>( lcx: &LateContext<'tcx>, @@ -406,7 +414,13 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), - ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)), + ExprKind::Path(ref qpath) => { + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { + let result = mir_to_const(this.lcx, result)?; + this.source = ConstantSource::Constant; + Some(result) + }) + }, ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, "cfg").is_some() { @@ -472,6 +486,49 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } + /// Simple constant folding to determine if an expression is an empty slice, str, array, … + /// `None` will be returned if the constness cannot be determined, or if the resolution + /// leaves the local crate. + pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> { + match e.kind { + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), + ExprKind::DropTemps(e) => self.expr_is_empty(e), + ExprKind::Path(ref qpath) => { + if !self + .typeck_results + .qpath_res(qpath, e.hir_id) + .opt_def_id() + .is_some_and(DefId::is_local) + { + return None; + } + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { + mir_is_empty(this.lcx, result) + }) + }, + ExprKind::Lit(lit) => { + if is_direct_expn_of(e.span, "cfg").is_some() { + None + } else { + match &lit.node { + LitKind::Str(is, _) => Some(is.is_empty()), + LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => Some(s.is_empty()), + _ => None, + } + } + }, + ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()), + ExprKind::Repeat(..) => { + if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() { + Some(n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)? == 0) + } else { + span_bug!(e.span, "typeck error"); + } + }, + _ => None, + } + } + #[expect(clippy::cast_possible_wrap)] fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option<Constant<'tcx>> { use self::Constant::{Bool, Int}; @@ -519,8 +576,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>() } - /// Lookup a possibly constant expression from an `ExprKind::Path`. - fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant<'tcx>> { + /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. + fn fetch_path_and_apply<T, F>(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T> + where + F: FnOnce(&mut Self, rustc_middle::mir::Const<'tcx>) -> Option<T>, + { let res = self.typeck_results.qpath_res(qpath, id); match res { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { @@ -553,9 +613,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty))?; - let result = mir_to_const(self.lcx, result)?; - self.source = ConstantSource::Constant; - Some(result) + f(self, result) }, _ => None, } @@ -746,7 +804,6 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> { - use rustc_middle::mir::ConstValue; let mir::Const::Val(val, _) = result else { // We only work on evaluated consts. return None; @@ -762,7 +819,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits( int.try_into().expect("invalid f64 bit representation"), ))), - ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))), + ty::RawPtr(_, _) => Some(Constant::RawPtr(int.assert_bits(int.size()))), _ => None, }, (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => { @@ -794,6 +851,42 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> } } +fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<bool> { + let mir::Const::Val(val, _) = result else { + // We only work on evaluated consts. + return None; + }; + match (val, result.ty().kind()) { + (_, ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { + ty::Str | ty::Slice(_) => { + if let ConstValue::Indirect { alloc_id, offset } = val { + // Get the length from the slice, using the same formula as + // [`ConstValue::try_get_slice_bytes_for_diagnostics`]. + let a = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let ptr_size = lcx.tcx.data_layout.pointer_size; + if a.size() < offset + 2 * ptr_size { + // (partially) dangling reference + return None; + } + let len = a + .read_scalar(&lcx.tcx, alloc_range(offset + ptr_size, ptr_size), false) + .ok()? + .to_target_usize(&lcx.tcx) + .ok()?; + Some(len == 0) + } else { + None + } + }, + ty::Array(_, len) => Some(len.try_to_target_usize(lcx.tcx)? == 0), + _ => None, + }, + (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(lcx.tcx)? == 0), + (ConstValue::ZeroSized, _) => Some(true), + _ => None, + } +} + fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, lcx: &LateContext<'tcx>, diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 6ed46e5dde0..0352696f93e 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -36,6 +36,20 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) { /// Usually it's nicer to provide more context for lint messages. /// Be sure the output is understandable when you use this method. /// +/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine +/// the lint level. +/// For the `span_lint` function, the node that was passed into the `LintPass::check_*` function is +/// used. +/// +/// If you're emitting the lint at the span of a different node than the one provided by the +/// `LintPass::check_*` function, consider using [`span_lint_hir`] instead. +/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node +/// highlighted in the displayed warning. +/// +/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// where you would expect it to. +/// If it doesn't, you likely need to use [`span_lint_hir`] instead. +/// /// # Example /// /// ```ignore @@ -61,6 +75,20 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult /// /// If you change the signature, remember to update the internal lint `CollapsibleCalls` /// +/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine +/// the lint level. +/// For the `span_lint_and_help` function, the node that was passed into the `LintPass::check_*` +/// function is used. +/// +/// If you're emitting the lint at the span of a different node than the one provided by the +/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead. +/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node +/// highlighted in the displayed warning. +/// +/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// where you would expect it to. +/// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. +/// /// # Example /// /// ```text @@ -99,6 +127,20 @@ pub fn span_lint_and_help<T: LintContext>( /// /// If you change the signature, remember to update the internal lint `CollapsibleCalls` /// +/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine +/// the lint level. +/// For the `span_lint_and_note` function, the node that was passed into the `LintPass::check_*` +/// function is used. +/// +/// If you're emitting the lint at the span of a different node than the one provided by the +/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead. +/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node +/// highlighted in the displayed warning. +/// +/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// where you would expect it to. +/// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. +/// /// # Example /// /// ```text @@ -139,6 +181,20 @@ pub fn span_lint_and_note<T: LintContext>( /// /// If you need to customize your lint output a lot, use this function. /// If you change the signature, remember to update the internal lint `CollapsibleCalls` +/// +/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine +/// the lint level. +/// For the `span_lint_and_then` function, the node that was passed into the `LintPass::check_*` +/// function is used. +/// +/// If you're emitting the lint at the span of a different node than the one provided by the +/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead. +/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node +/// highlighted in the displayed warning. +/// +/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// where you would expect it to. +/// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. pub fn span_lint_and_then<C, S, F>(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F) where C: LintContext, @@ -152,6 +208,30 @@ where }); } +/// Like [`span_lint`], but emits the lint at the node identified by the given `HirId`. +/// +/// This is in contrast to [`span_lint`], which always emits the lint at the node that was last +/// passed to the `LintPass::check_*` function. +/// +/// The `HirId` is used for checking lint level attributes and to fulfill lint expectations defined +/// via the `#[expect]` attribute. +/// +/// For example: +/// ```ignore +/// fn f() { /* <node_1> */ +/// +/// #[allow(clippy::some_lint)] +/// let _x = /* <expr_1> */; +/// } +/// ``` +/// If `some_lint` does its analysis in `LintPass::check_fn` (at `<node_1>`) and emits a lint at +/// `<expr_1>` using [`span_lint`], then allowing the lint at `<expr_1>` as attempted in the snippet +/// will not work! +/// Even though that is where the warning points at, which would be confusing to users. +/// +/// Instead, use this function and also pass the `HirId` of `<expr_1>`, which will let +/// the compiler check lint level attributes at the place of the expression and +/// the `#[allow]` will work. pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) { #[expect(clippy::disallowed_methods)] cx.tcx.node_span_lint(lint, hir_id, sp, msg.to_string(), |diag| { @@ -159,6 +239,30 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s }); } +/// Like [`span_lint_and_then`], but emits the lint at the node identified by the given `HirId`. +/// +/// This is in contrast to [`span_lint_and_then`], which always emits the lint at the node that was +/// last passed to the `LintPass::check_*` function. +/// +/// The `HirId` is used for checking lint level attributes and to fulfill lint expectations defined +/// via the `#[expect]` attribute. +/// +/// For example: +/// ```ignore +/// fn f() { /* <node_1> */ +/// +/// #[allow(clippy::some_lint)] +/// let _x = /* <expr_1> */; +/// } +/// ``` +/// If `some_lint` does its analysis in `LintPass::check_fn` (at `<node_1>`) and emits a lint at +/// `<expr_1>` using [`span_lint`], then allowing the lint at `<expr_1>` as attempted in the snippet +/// will not work! +/// Even though that is where the warning points at, which would be confusing to users. +/// +/// Instead, use this function and also pass the `HirId` of `<expr_1>`, which will let +/// the compiler check lint level attributes at the place of the expression and +/// the `#[allow]` will work. pub fn span_lint_hir_and_then( cx: &LateContext<'_>, lint: &'static Lint, @@ -182,6 +286,20 @@ pub fn span_lint_hir_and_then( /// /// If you change the signature, remember to update the internal lint `CollapsibleCalls` /// +/// NOTE: Lint emissions are always bound to a node in the HIR, which is used to determine +/// the lint level. +/// For the `span_lint_and_sugg` function, the node that was passed into the `LintPass::check_*` +/// function is used. +/// +/// If you're emitting the lint at the span of a different node than the one provided by the +/// `LintPass::check_*` function, consider using [`span_lint_hir_and_then`] instead. +/// This is needed for `#[allow]` and `#[expect]` attributes to work on the node +/// highlighted in the displayed warning. +/// +/// If you're unsure which function you should use, you can test if the `#[allow]` attribute works +/// where you would expect it to. +/// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. +/// /// # Example /// /// ```text diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index ba682813dad..8ce19998a08 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -102,7 +102,7 @@ impl<'hir> IfLet<'hir> { if let ExprKind::If( Expr { kind: - ExprKind::Let(&hir::Let { + ExprKind::Let(&hir::LetExpr { pat: let_pat, init: let_expr, span: let_span, @@ -379,7 +379,7 @@ impl<'hir> WhileLet<'hir> { ExprKind::If( Expr { kind: - ExprKind::Let(&hir::Let { + ExprKind::Let(&hir::LetExpr { pat: let_pat, init: let_expr, span: let_span, diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 106d1d0d77f..7a4eba9790e 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -8,7 +8,7 @@ use rustc_hir::def::Res; use rustc_hir::MatchSource::TryDesugar; use rustc_hir::{ ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, - GenericArgs, HirId, HirIdMap, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, + GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lexer::{tokenize, TokenKind}; @@ -837,7 +837,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } }, - ExprKind::Let(Let { pat, init, ty, .. }) => { + ExprKind::Let(LetExpr { pat, init, ty, .. }) => { self.hash_expr(init); if let Some(ty) = ty { self.hash_ty(ty); @@ -955,6 +955,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } }, PatKind::Box(pat) => self.hash_pat(pat), + PatKind::Deref(pat) => self.hash_pat(pat), PatKind::Lit(expr) => self.hash_expr(expr), PatKind::Or(pats) => { for pat in pats { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index a38db0ebec0..3b1b99caebe 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -99,7 +99,7 @@ use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, - ItemKind, LangItem, Local, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, + ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, }; use rustc_lexer::{tokenize, TokenKind}; @@ -112,7 +112,7 @@ use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy, ParamEnv, - ParamEnvAnd, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UintTy, UpvarCapture, + ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; @@ -184,7 +184,7 @@ pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { if let Node::Pat(pat) = cx.tcx.hir_node(hir_id) && matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)) - && let Node::Local(local) = cx.tcx.parent_hir_node(hir_id) + && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id) { return local.init; } @@ -1040,7 +1040,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { .get(child_id) .map_or(&[][..], |x| &**x) { - if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) = + if let rustc_ty::RawPtr(_, mutability) | rustc_ty::Ref(_, _, mutability) = *adjust.last().map_or(target, |a| a.target).kind() { return CaptureKind::Ref(mutability); @@ -1079,7 +1079,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { }, _ => break, }, - Node::Local(l) => match pat_capture_kind(cx, l.pat) { + Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) { CaptureKind::Value => break, capture @ CaptureKind::Ref(_) => return capture, }, @@ -1357,7 +1357,7 @@ pub fn get_enclosing_loop_or_multi_call_closure<'tcx>( ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e), _ => (), }, - Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (), + Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) => (), _ => break, } } @@ -1462,7 +1462,7 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { let mut child_id = expr.hir_id; for (parent_id, node) in tcx.hir().parent_iter(child_id) { - if let Node::Local(Local { + if let Node::LetStmt(LetStmt { init: Some(init), els: Some(els), .. @@ -1482,7 +1482,7 @@ pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { let mut child_id = expr.hir_id; for (parent_id, node) in tcx.hir().parent_iter(child_id) { - if let Node::Local(Local { els: Some(els), .. }) = node + if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node && els.hir_id == child_id { return true; @@ -1678,7 +1678,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { match pat.kind { PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable. PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)), - PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), + PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), PatKind::Or(pats) => { // TODO: should be the honest check, that pats is exhaustive set @@ -2158,7 +2158,7 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { Node::Stmt(Stmt { kind: StmtKind::Expr(_) | StmtKind::Semi(_) - | StmtKind::Let(Local { + | StmtKind::Let(LetStmt { pat: Pat { kind: PatKind::Wild, .. @@ -2246,8 +2246,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool { /// Returns the `DefId` of the callee if the given expression is a function or method call. pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> { + fn_def_id_with_node_args(cx, expr).map(|(did, _)| did) +} + +/// Returns the `DefId` of the callee if the given expression is a function or method call, +/// as well as its node args. +pub fn fn_def_id_with_node_args<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'_>, +) -> Option<(DefId, rustc_ty::GenericArgsRef<'tcx>)> { + let typeck = cx.typeck_results(); match &expr.kind { - ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), + ExprKind::MethodCall(..) => Some(( + typeck.type_dependent_def_id(expr.hir_id)?, + typeck.node_args(expr.hir_id), + )), ExprKind::Call( Expr { kind: ExprKind::Path(qpath), @@ -2259,9 +2272,9 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> { // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or // deref to fn pointers, dyn Fn, impl Fn - #8850 if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) = - cx.typeck_results().qpath_res(qpath, *path_hir_id) + typeck.qpath_res(qpath, *path_hir_id) { - Some(id) + Some((id, typeck.node_args(*path_hir_id))) } else { None } @@ -2626,7 +2639,7 @@ pub struct ExprUseCtxt<'tcx> { /// The node which consumes a value. pub enum ExprUseNode<'tcx> { /// Assignment to, or initializer for, a local - Local(&'tcx Local<'tcx>), + LetStmt(&'tcx LetStmt<'tcx>), /// Initializer for a const or static item. ConstStatic(OwnerId), /// Implicit or explicit return from a function. @@ -2658,7 +2671,7 @@ impl<'tcx> ExprUseNode<'tcx> { /// Gets the needed type as it's defined without any type inference. pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> { match *self { - Self::Local(Local { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)), + Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)), Self::ConstStatic(id) => Some(DefinedTy::Mir( cx.param_env .and(Binder::dummy(cx.tcx.type_of(id).instantiate_identity())), @@ -2718,7 +2731,7 @@ impl<'tcx> ExprUseNode<'tcx> { let sig = cx.tcx.fn_sig(id).skip_binder(); Some(DefinedTy::Mir(cx.tcx.param_env(id).and(sig.input(i)))) }, - Self::Local(_) | Self::FieldAccess(..) | Self::Callee | Self::Expr | Self::Other => None, + Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Expr | Self::Other => None, } } } @@ -2757,7 +2770,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Optio .continue_value() .map(|(use_node, child_id)| { let node = match use_node { - Node::Local(l) => ExprUseNode::Local(l), + Node::LetStmt(l) => ExprUseNode::LetStmt(l), Node::ExprField(field) => ExprUseNode::Field(field), Node::Item(&Item { @@ -3145,7 +3158,7 @@ pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option< } } - fn visit_local(&mut self, l: &'tcx Local<'_>) { + fn visit_local(&mut self, l: &'tcx LetStmt<'_>) { if let Some(e) = l.init { self.visit_expr(e); } @@ -3222,7 +3235,7 @@ fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: rustc_ty::Array(..) | rustc_ty::Dynamic(..) | rustc_ty::Never - | rustc_ty::RawPtr(_) + | rustc_ty::RawPtr(_, _) | rustc_ty::Ref(..) | rustc_ty::Slice(_) | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)), diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 987f28192a8..456b8019e95 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -19,6 +19,8 @@ pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "B pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; +pub const CORE_ITER_ENUMERATE_METHOD: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "enumerate"]; +pub const CORE_ITER_ENUMERATE_STRUCT: [&str; 5] = ["core", "iter", "adapters", "enumerate", "Enumerate"]; pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index dadb0d662ce..cabebf89bec 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -174,7 +174,7 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_), _) + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, _) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 6e011a28bb7..97ce755adbb 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -96,7 +96,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return false; } - for (predicate, _span) in cx.tcx.explicit_item_bounds(def_id).instantiate_identity_iter_copied() { + for (predicate, _span) in cx.tcx.explicit_item_super_predicates(def_id).instantiate_identity_iter_copied() { match predicate.kind().skip_binder() { // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through // and check substitutions to find `U`. @@ -321,14 +321,14 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use), ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use), - ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { + ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => { // for the Array case we don't need to care for the len == 0 case // because we don't want to lint functions returning empty arrays is_must_use_ty(cx, *ty) }, ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { - for (predicate, _) in cx.tcx.explicit_item_bounds(def_id).skip_binder() { + for (predicate, _) in cx.tcx.explicit_item_super_predicates(def_id).skip_binder() { if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { return true; @@ -729,7 +729,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => sig_from_bounds( cx, ty, - cx.tcx.item_bounds(def_id).iter_instantiated(cx.tcx, args), + cx.tcx.item_super_predicates(def_id).iter_instantiated(cx.tcx, args), cx.tcx.opt_parent(def_id), ), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)), @@ -807,7 +807,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option for (pred, _) in cx .tcx - .explicit_item_bounds(ty.def_id) + .explicit_item_super_predicates(ty.def_id) .iter_instantiated_copied(cx.tcx, ty.args) { match pred.kind().skip_binder() { diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 7913926928f..762830ffd78 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -242,7 +242,7 @@ fn path_segment_certainty( Node::Param(..) => Certainty::Certain(None), // A local's type is certain if its type annotation is certain or it has an initializer whose // type is certain. - Node::Local(local) => { + Node::LetStmt(local) => { let lhs = local.ty.map_or(Certainty::Uncertain, |ty| type_certainty(cx, ty)); let rhs = local .init diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index ebc38e531fe..0a05ac029ea 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -5,7 +5,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; use rustc_hir::{ - AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, + AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath, Stmt, UnOp, UnsafeSource, Unsafety, }; use rustc_lint::LateContext; @@ -624,7 +624,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>( | ExprKind::Field(e, _) | ExprKind::Unary(UnOp::Deref, e) | ExprKind::Match(e, ..) - | ExprKind::Let(&Let { init: e, .. }) => { + | ExprKind::Let(&LetExpr { init: e, .. }) => { helper(typeck, false, e, f)?; }, ExprKind::Block(&Block { expr: Some(e), .. }, _) | ExprKind::Cast(e, _) | ExprKind::Unary(_, e) => { diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index 296eb8dd340..9a3a41e1d1e 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.78" +version = "0.1.79" edition = "2021" publish = false diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 070b62887d5..a63e66f3214 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-03-07" +channel = "nightly-2024-03-21" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr index ae5d6843406..cfc590bed36 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr @@ -4,6 +4,7 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` LL | cx.span_lint(lint, span, msg, |_| {}); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead (from clippy.toml) = note: `-D clippy::disallowed-methods` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` @@ -12,6 +13,8 @@ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_ | LL | tcx.node_span_lint(lint, hir_id, span, msg, |_| {}); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead (from clippy.toml) error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.fixed b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.fixed new file mode 100644 index 00000000000..d42b29ba21a --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.fixed @@ -0,0 +1,38 @@ +//@compile-flags: --test +#![warn(clippy::dbg_macro)] +#![allow(clippy::unnecessary_operation, clippy::no_effect)] + +fn foo(n: u32) -> u32 { + if let Some(n) = n.checked_sub(4) { n } else { n } +} + +fn factorial(n: u32) -> u32 { + if n <= 1 { + 1 + } else { + n * factorial(n - 1) + } +} + +fn main() { + 42; + foo(3) + factorial(4); + (1, 2, 3, 4, 5); +} + +#[test] +pub fn issue8481() { + dbg!(1); +} + +#[cfg(test)] +fn foo2() { + dbg!(1); +} + +#[cfg(test)] +mod mod1 { + fn func() { + dbg!(1); + } +} diff --git a/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.rs b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.rs index 67129e62477..bd189b1576f 100644 --- a/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.rs +++ b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.rs @@ -1,6 +1,7 @@ //@compile-flags: --test #![warn(clippy::dbg_macro)] -//@no-rustfix +#![allow(clippy::unnecessary_operation, clippy::no_effect)] + fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } } @@ -15,9 +16,7 @@ fn factorial(n: u32) -> u32 { fn main() { dbg!(42); - dbg!(dbg!(dbg!(42))); foo(3) + dbg!(factorial(4)); - dbg!(1, 2, dbg!(3, 4)); dbg!(1, 2, 3, 4, 5); } diff --git a/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr index 8ffc426be2d..129fab5ff97 100644 --- a/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr +++ b/src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.stderr @@ -1,5 +1,5 @@ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:5:22 + --> tests/ui-toml/dbg_macro/dbg_macro.rs:6:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:9:8 + --> tests/ui-toml/dbg_macro/dbg_macro.rs:10:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | if n <= 1 { | ~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:10:9 + --> tests/ui-toml/dbg_macro/dbg_macro.rs:11:9 | LL | dbg!(1) | ^^^^^^^ @@ -34,7 +34,7 @@ LL | 1 | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:12:9 + --> tests/ui-toml/dbg_macro/dbg_macro.rs:13:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | n * factorial(n - 1) | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:17:5 + --> tests/ui-toml/dbg_macro/dbg_macro.rs:18:5 | LL | dbg!(42); | ^^^^^^^^ @@ -56,17 +56,6 @@ LL | 42; | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:18:5 - | -LL | dbg!(dbg!(dbg!(42))); - | ^^^^^^^^^^^^^^^^^^^^ - | -help: remove the invocation before committing it to a version control system - | -LL | dbg!(dbg!(42)); - | ~~~~~~~~~~~~~~ - -error: the `dbg!` macro is intended as a debugging tool --> tests/ui-toml/dbg_macro/dbg_macro.rs:19:14 | LL | foo(3) + dbg!(factorial(4)); @@ -80,17 +69,6 @@ LL | foo(3) + factorial(4); error: the `dbg!` macro is intended as a debugging tool --> tests/ui-toml/dbg_macro/dbg_macro.rs:20:5 | -LL | dbg!(1, 2, dbg!(3, 4)); - | ^^^^^^^^^^^^^^^^^^^^^^ - | -help: remove the invocation before committing it to a version control system - | -LL | (1, 2, dbg!(3, 4)); - | ~~~~~~~~~~~~~~~~~~ - -error: the `dbg!` macro is intended as a debugging tool - --> tests/ui-toml/dbg_macro/dbg_macro.rs:21:5 - | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ | @@ -99,5 +77,5 @@ help: remove the invocation before committing it to a version control system LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/clippy.toml b/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/clippy.toml new file mode 100644 index 00000000000..2f3d60be3a7 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/clippy.toml @@ -0,0 +1 @@ +allow-one-hash-in-raw-strings = true diff --git a/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.fixed b/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.fixed new file mode 100644 index 00000000000..fd20bdff6e2 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.fixed @@ -0,0 +1,9 @@ +#![allow(clippy::no_effect, unused)] +#![warn(clippy::needless_raw_string_hashes)] + +fn main() { + r#"\aaa"#; + r#"\aaa"#; + r#"Hello "world"!"#; + r####" "### "## "# "####; +} diff --git a/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.rs b/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.rs new file mode 100644 index 00000000000..3c6c2463700 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.rs @@ -0,0 +1,9 @@ +#![allow(clippy::no_effect, unused)] +#![warn(clippy::needless_raw_string_hashes)] + +fn main() { + r#"\aaa"#; + r##"\aaa"##; + r##"Hello "world"!"##; + r######" "### "## "# "######; +} diff --git a/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.stderr b/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.stderr new file mode 100644 index 00000000000..421ad66e4c9 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.stderr @@ -0,0 +1,40 @@ +error: unnecessary hashes around raw string literal + --> tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.rs:6:5 + | +LL | r##"\aaa"##; + | ^^^^^^^^^^^ + | + = note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_raw_string_hashes)]` +help: remove one hash from both sides of the string literal + | +LL - r##"\aaa"##; +LL + r#"\aaa"#; + | + +error: unnecessary hashes around raw string literal + --> tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.rs:7:5 + | +LL | r##"Hello "world"!"##; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: remove one hash from both sides of the string literal + | +LL - r##"Hello "world"!"##; +LL + r#"Hello "world"!"#; + | + +error: unnecessary hashes around raw string literal + --> tests/ui-toml/needless_raw_string_hashes_one_allowed/needless_raw_string_hashes.rs:8:5 + | +LL | r######" "### "## "# "######; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove 2 hashes from both sides of the string literal + | +LL - r######" "### "## "# "######; +LL + r####" "### "## "# "####; + | + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs b/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs index 663c2eb2c37..523148d6586 100644 --- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs +++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![feature(lint_reasons)] #![deny(clippy::allow_attributes_without_reason)] -#![allow(unfulfilled_lint_expectations)] +#![allow(unfulfilled_lint_expectations, clippy::duplicated_attributes)] extern crate proc_macros; use proc_macros::{external, with_span}; diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr index 3c81233bf77..770a771ec3d 100644 --- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr +++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr @@ -1,8 +1,8 @@ error: `allow` attribute without specifying a reason --> tests/ui/allow_attributes_without_reason.rs:4:1 | -LL | #![allow(unfulfilled_lint_expectations)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![allow(unfulfilled_lint_expectations, clippy::duplicated_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: try adding a reason at the end with `, reason = ".."` note: the lint level is defined here diff --git a/src/tools/clippy/tests/ui/assigning_clones.fixed b/src/tools/clippy/tests/ui/assigning_clones.fixed index c66e0c1f602..160f3b94663 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.fixed +++ b/src/tools/clippy/tests/ui/assigning_clones.fixed @@ -128,6 +128,19 @@ fn ignore_generic_clone<T: Clone>(a: &mut T, b: &T) { *a = b.clone(); } +#[clippy::msrv = "1.62"] +fn msrv_1_62(mut a: String, b: String, c: &str) { + a.clone_from(&b); + // Should not be linted, as clone_into wasn't stabilized until 1.63 + a = c.to_owned(); +} + +#[clippy::msrv = "1.63"] +fn msrv_1_63(mut a: String, b: String, c: &str) { + a.clone_from(&b); + c.clone_into(&mut a); +} + macro_rules! clone_inside { ($a:expr, $b: expr) => { $a = $b.clone(); diff --git a/src/tools/clippy/tests/ui/assigning_clones.rs b/src/tools/clippy/tests/ui/assigning_clones.rs index b9f994d3e03..14ba1d4db9a 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.rs +++ b/src/tools/clippy/tests/ui/assigning_clones.rs @@ -128,6 +128,19 @@ fn ignore_generic_clone<T: Clone>(a: &mut T, b: &T) { *a = b.clone(); } +#[clippy::msrv = "1.62"] +fn msrv_1_62(mut a: String, b: String, c: &str) { + a = b.clone(); + // Should not be linted, as clone_into wasn't stabilized until 1.63 + a = c.to_owned(); +} + +#[clippy::msrv = "1.63"] +fn msrv_1_63(mut a: String, b: String, c: &str) { + a = b.clone(); + a = c.to_owned(); +} + macro_rules! clone_inside { ($a:expr, $b: expr) => { $a = $b.clone(); diff --git a/src/tools/clippy/tests/ui/assigning_clones.stderr b/src/tools/clippy/tests/ui/assigning_clones.stderr index b76323f3606..ba59f067431 100644 --- a/src/tools/clippy/tests/ui/assigning_clones.stderr +++ b/src/tools/clippy/tests/ui/assigning_clones.stderr @@ -67,41 +67,59 @@ error: assigning the result of `Clone::clone()` may be inefficient LL | a = b.clone(); | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:133:5 + | +LL | a = b.clone(); + | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` + +error: assigning the result of `Clone::clone()` may be inefficient + --> tests/ui/assigning_clones.rs:140:5 + | +LL | a = b.clone(); + | ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)` + +error: assigning the result of `ToOwned::to_owned()` may be inefficient + --> tests/ui/assigning_clones.rs:141:5 + | +LL | a = c.to_owned(); + | ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)` + error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:145:5 + --> tests/ui/assigning_clones.rs:158:5 | LL | *mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:149:5 + --> tests/ui/assigning_clones.rs:162:5 | LL | mut_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:170:5 + --> tests/ui/assigning_clones.rs:183:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:174:5 + --> tests/ui/assigning_clones.rs:187:5 | LL | **mut_box_string = ref_str.to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:178:5 + --> tests/ui/assigning_clones.rs:191:5 | LL | *mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)` error: assigning the result of `ToOwned::to_owned()` may be inefficient - --> tests/ui/assigning_clones.rs:182:5 + --> tests/ui/assigning_clones.rs:195:5 | LL | mut_thing = ToOwned::to_owned(ref_str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)` -error: aborting due to 17 previous errors +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs index 75f7a20f961..a6f3b164c9b 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs @@ -11,8 +11,8 @@ use quote::{quote, quote_spanned}; use syn::spanned::Spanned; use syn::token::Star; use syn::{ - parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, - Signature, TraitItem, Type, + parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemStruct, ItemTrait, Lifetime, Pat, PatIdent, + PatType, Signature, TraitItem, Type, Visibility, }; #[proc_macro_attribute] @@ -101,9 +101,7 @@ pub fn fake_main(_attr: TokenStream, item: TokenStream) -> TokenStream { let mut item = parse_macro_input!(item as ItemFn); let span = item.block.brace_token.span; - if item.sig.asyncness.is_some() { - item.sig.asyncness = None; - } + item.sig.asyncness = None; let crate_name = quote! { fake_crate }; let block = item.block; @@ -128,7 +126,7 @@ pub fn fake_main(_attr: TokenStream, item: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream { - let mut async_fn = syn::parse_macro_input!(input as syn::ItemFn); + let mut async_fn = parse_macro_input!(input as syn::ItemFn); for stmt in &mut async_fn.block.stmts { if let syn::Stmt::Expr(syn::Expr::Match(syn::ExprMatch { expr: scrutinee, .. }), _) = stmt { @@ -145,3 +143,36 @@ pub fn fake_desugar_await(_args: TokenStream, input: TokenStream) -> TokenStream quote!(#async_fn).into() } + +#[proc_macro_attribute] +pub fn rewrite_struct(_args: TokenStream, input: TokenStream) -> TokenStream { + let mut item_struct = parse_macro_input!(input as syn::ItemStruct); + // remove struct attributes including doc comments. + item_struct.attrs = vec![]; + if let Visibility::Public(token) = item_struct.vis { + // set vis to `pub(crate)` to trigger `missing_docs_in_private_items` lint. + let new_vis: Visibility = syn::parse_quote_spanned!(token.span() => pub(crate)); + item_struct.vis = new_vis; + } + if let syn::Fields::Named(fields) = &mut item_struct.fields { + for field in &mut fields.named { + // remove all attributes from fields as well. + field.attrs = vec![]; + } + } + + quote!(#item_struct).into() +} + +#[proc_macro_attribute] +pub fn with_empty_docs(_attr: TokenStream, input: TokenStream) -> TokenStream { + let item = parse_macro_input!(input as syn::Item); + let attrs: Vec<syn::Attribute> = vec![]; + let doc_comment = ""; + quote! { + #(#attrs)* + #[doc = #doc_comment] + #item + } + .into() +} diff --git a/src/tools/clippy/tests/ui/await_holding_lock.rs b/src/tools/clippy/tests/ui/await_holding_lock.rs index 27b57b64813..8e5510e6cd0 100644 --- a/src/tools/clippy/tests/ui/await_holding_lock.rs +++ b/src/tools/clippy/tests/ui/await_holding_lock.rs @@ -1,4 +1,5 @@ #![warn(clippy::await_holding_lock)] +#![allow(clippy::readonly_write_lock)] // When adding or modifying a test, please do the same for parking_lot::Mutex. mod std_mutex { diff --git a/src/tools/clippy/tests/ui/await_holding_lock.stderr b/src/tools/clippy/tests/ui/await_holding_lock.stderr index e58436345b5..0af48a36acc 100644 --- a/src/tools/clippy/tests/ui/await_holding_lock.stderr +++ b/src/tools/clippy/tests/ui/await_holding_lock.stderr @@ -1,12 +1,12 @@ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:9:13 + --> tests/ui/await_holding_lock.rs:10:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:11:15 + --> tests/ui/await_holding_lock.rs:12:15 | LL | baz().await | ^^^^^ @@ -14,40 +14,40 @@ LL | baz().await = help: to override `-D warnings` add `#[allow(clippy::await_holding_lock)]` error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:25:13 + --> tests/ui/await_holding_lock.rs:26:13 | LL | let guard = x.read().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:27:15 + --> tests/ui/await_holding_lock.rs:28:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:31:13 + --> tests/ui/await_holding_lock.rs:32:13 | LL | let mut guard = x.write().unwrap(); | ^^^^^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:33:15 + --> tests/ui/await_holding_lock.rs:34:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:53:13 + --> tests/ui/await_holding_lock.rs:54:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:56:28 + --> tests/ui/await_holding_lock.rs:57:28 | LL | let second = baz().await; | ^^^^^ @@ -56,79 +56,79 @@ LL | let third = baz().await; | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:67:17 + --> tests/ui/await_holding_lock.rs:68:17 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:69:19 + --> tests/ui/await_holding_lock.rs:70:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:80:17 + --> tests/ui/await_holding_lock.rs:81:17 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:82:19 + --> tests/ui/await_holding_lock.rs:83:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:93:13 + --> tests/ui/await_holding_lock.rs:94:13 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:95:15 + --> tests/ui/await_holding_lock.rs:96:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:109:13 + --> tests/ui/await_holding_lock.rs:110:13 | LL | let guard = x.read(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:111:15 + --> tests/ui/await_holding_lock.rs:112:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:115:13 + --> tests/ui/await_holding_lock.rs:116:13 | LL | let mut guard = x.write(); | ^^^^^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:117:15 + --> tests/ui/await_holding_lock.rs:118:15 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:137:13 + --> tests/ui/await_holding_lock.rs:138:13 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:140:28 + --> tests/ui/await_holding_lock.rs:141:28 | LL | let second = baz().await; | ^^^^^ @@ -137,40 +137,40 @@ LL | let third = baz().await; | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:151:17 + --> tests/ui/await_holding_lock.rs:152:17 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:153:19 + --> tests/ui/await_holding_lock.rs:154:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:164:17 + --> tests/ui/await_holding_lock.rs:165:17 | LL | let guard = x.lock(); | ^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:166:19 + --> tests/ui/await_holding_lock.rs:167:19 | LL | baz().await | ^^^^^ error: this `MutexGuard` is held across an `await` point - --> tests/ui/await_holding_lock.rs:185:9 + --> tests/ui/await_holding_lock.rs:186:9 | LL | let mut guard = x.lock().unwrap(); | ^^^^^^^^^ | = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await note: these are all the `await` points this lock is held through - --> tests/ui/await_holding_lock.rs:189:11 + --> tests/ui/await_holding_lock.rs:190:11 | LL | baz().await; | ^^^^^ diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed index 63b8e27e1c6..b05166a055e 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed @@ -1,4 +1,4 @@ -#![allow(unused, clippy::assertions_on_constants)] +#![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)] #![warn(clippy::bool_assert_comparison)] use std::ops::Not; diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.rs b/src/tools/clippy/tests/ui/bool_assert_comparison.rs index 58f81fedb79..dc51fcf1d36 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.rs +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.rs @@ -1,4 +1,4 @@ -#![allow(unused, clippy::assertions_on_constants)] +#![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)] #![warn(clippy::bool_assert_comparison)] use std::ops::Not; diff --git a/src/tools/clippy/tests/ui/builtin_type_shadow.stderr b/src/tools/clippy/tests/ui/builtin_type_shadow.stderr index 1e15cdee772..033204af925 100644 --- a/src/tools/clippy/tests/ui/builtin_type_shadow.stderr +++ b/src/tools/clippy/tests/ui/builtin_type_shadow.stderr @@ -19,6 +19,7 @@ LL | 42 | = note: expected type parameter `u32` found type `{integer}` + = note: the caller chooses a type for `u32` which can be different from `i32` error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.fixed b/src/tools/clippy/tests/ui/cast_lossless_bool.fixed index a4ce1c6f928..51a38a60cf6 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_bool.fixed +++ b/src/tools/clippy/tests/ui/cast_lossless_bool.fixed @@ -1,6 +1,8 @@ #![allow(dead_code)] #![warn(clippy::cast_lossless)] +type U8 = u8; + fn main() { // Test clippy::cast_lossless with casts to integer types let _ = u8::from(true); @@ -19,6 +21,8 @@ fn main() { // Test with an expression wrapped in parens let _ = u16::from(true | false); + + let _ = U8::from(true); } // The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const, diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.rs b/src/tools/clippy/tests/ui/cast_lossless_bool.rs index e5b1c30c103..cb307bd68e4 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_bool.rs +++ b/src/tools/clippy/tests/ui/cast_lossless_bool.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] #![warn(clippy::cast_lossless)] +type U8 = u8; + fn main() { // Test clippy::cast_lossless with casts to integer types let _ = true as u8; @@ -19,6 +21,8 @@ fn main() { // Test with an expression wrapped in parens let _ = (true | false) as u16; + + let _ = true as U8; } // The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const, diff --git a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr index 792b30b7a38..b47b35461f6 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_bool.stderr +++ b/src/tools/clippy/tests/ui/cast_lossless_bool.stderr @@ -1,5 +1,5 @@ error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> tests/ui/cast_lossless_bool.rs:6:13 + --> tests/ui/cast_lossless_bool.rs:8:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` @@ -8,82 +8,88 @@ LL | let _ = true as u8; = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> tests/ui/cast_lossless_bool.rs:7:13 + --> tests/ui/cast_lossless_bool.rs:9:13 | LL | let _ = true as u16; | ^^^^^^^^^^^ help: try: `u16::from(true)` error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)` - --> tests/ui/cast_lossless_bool.rs:8:13 + --> tests/ui/cast_lossless_bool.rs:10:13 | LL | let _ = true as u32; | ^^^^^^^^^^^ help: try: `u32::from(true)` error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)` - --> tests/ui/cast_lossless_bool.rs:9:13 + --> tests/ui/cast_lossless_bool.rs:11:13 | LL | let _ = true as u64; | ^^^^^^^^^^^ help: try: `u64::from(true)` error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)` - --> tests/ui/cast_lossless_bool.rs:10:13 + --> tests/ui/cast_lossless_bool.rs:12:13 | LL | let _ = true as u128; | ^^^^^^^^^^^^ help: try: `u128::from(true)` error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)` - --> tests/ui/cast_lossless_bool.rs:11:13 + --> tests/ui/cast_lossless_bool.rs:13:13 | LL | let _ = true as usize; | ^^^^^^^^^^^^^ help: try: `usize::from(true)` error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)` - --> tests/ui/cast_lossless_bool.rs:13:13 + --> tests/ui/cast_lossless_bool.rs:15:13 | LL | let _ = true as i8; | ^^^^^^^^^^ help: try: `i8::from(true)` error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)` - --> tests/ui/cast_lossless_bool.rs:14:13 + --> tests/ui/cast_lossless_bool.rs:16:13 | LL | let _ = true as i16; | ^^^^^^^^^^^ help: try: `i16::from(true)` error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)` - --> tests/ui/cast_lossless_bool.rs:15:13 + --> tests/ui/cast_lossless_bool.rs:17:13 | LL | let _ = true as i32; | ^^^^^^^^^^^ help: try: `i32::from(true)` error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)` - --> tests/ui/cast_lossless_bool.rs:16:13 + --> tests/ui/cast_lossless_bool.rs:18:13 | LL | let _ = true as i64; | ^^^^^^^^^^^ help: try: `i64::from(true)` error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)` - --> tests/ui/cast_lossless_bool.rs:17:13 + --> tests/ui/cast_lossless_bool.rs:19:13 | LL | let _ = true as i128; | ^^^^^^^^^^^^ help: try: `i128::from(true)` error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)` - --> tests/ui/cast_lossless_bool.rs:18:13 + --> tests/ui/cast_lossless_bool.rs:20:13 | LL | let _ = true as isize; | ^^^^^^^^^^^^^ help: try: `isize::from(true)` error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)` - --> tests/ui/cast_lossless_bool.rs:21:13 + --> tests/ui/cast_lossless_bool.rs:23:13 | LL | let _ = (true | false) as u16; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)` +error: casting `bool` to `U8` is more cleanly stated with `U8::from(_)` + --> tests/ui/cast_lossless_bool.rs:25:13 + | +LL | let _ = true as U8; + | ^^^^^^^^^^ help: try: `U8::from(true)` + error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)` - --> tests/ui/cast_lossless_bool.rs:49:13 + --> tests/ui/cast_lossless_bool.rs:53:13 | LL | let _ = true as u8; | ^^^^^^^^^^ help: try: `u8::from(true)` -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.fixed b/src/tools/clippy/tests/ui/cast_lossless_float.fixed index f4f2e4773a5..96a67b1945c 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_float.fixed +++ b/src/tools/clippy/tests/ui/cast_lossless_float.fixed @@ -1,11 +1,16 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] +type F32 = f32; +type F64 = f64; + fn main() { // Test clippy::cast_lossless with casts to floating-point types let x0 = 1i8; let _ = f32::from(x0); let _ = f64::from(x0); + let _ = F32::from(x0); + let _ = F64::from(x0); let x1 = 1u8; let _ = f32::from(x1); let _ = f64::from(x1); diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.rs b/src/tools/clippy/tests/ui/cast_lossless_float.rs index fdd88ed36fc..d37b2c1d920 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_float.rs +++ b/src/tools/clippy/tests/ui/cast_lossless_float.rs @@ -1,11 +1,16 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] +type F32 = f32; +type F64 = f64; + fn main() { // Test clippy::cast_lossless with casts to floating-point types let x0 = 1i8; let _ = x0 as f32; let _ = x0 as f64; + let _ = x0 as F32; + let _ = x0 as F64; let x1 = 1u8; let _ = x1 as f32; let _ = x1 as f64; diff --git a/src/tools/clippy/tests/ui/cast_lossless_float.stderr b/src/tools/clippy/tests/ui/cast_lossless_float.stderr index e70f81eb91f..ad7de760adf 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_float.stderr +++ b/src/tools/clippy/tests/ui/cast_lossless_float.stderr @@ -1,5 +1,5 @@ error: casting `i8` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:7:13 + --> tests/ui/cast_lossless_float.rs:10:13 | LL | let _ = x0 as f32; | ^^^^^^^^^ help: try: `f32::from(x0)` @@ -8,64 +8,76 @@ LL | let _ = x0 as f32; = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` error: casting `i8` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:8:13 + --> tests/ui/cast_lossless_float.rs:11:13 | LL | let _ = x0 as f64; | ^^^^^^^^^ help: try: `f64::from(x0)` +error: casting `i8` to `F32` may become silently lossy if you later change the type + --> tests/ui/cast_lossless_float.rs:12:13 + | +LL | let _ = x0 as F32; + | ^^^^^^^^^ help: try: `F32::from(x0)` + +error: casting `i8` to `F64` may become silently lossy if you later change the type + --> tests/ui/cast_lossless_float.rs:13:13 + | +LL | let _ = x0 as F64; + | ^^^^^^^^^ help: try: `F64::from(x0)` + error: casting `u8` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:10:13 + --> tests/ui/cast_lossless_float.rs:15:13 | LL | let _ = x1 as f32; | ^^^^^^^^^ help: try: `f32::from(x1)` error: casting `u8` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:11:13 + --> tests/ui/cast_lossless_float.rs:16:13 | LL | let _ = x1 as f64; | ^^^^^^^^^ help: try: `f64::from(x1)` error: casting `i16` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:13:13 + --> tests/ui/cast_lossless_float.rs:18:13 | LL | let _ = x2 as f32; | ^^^^^^^^^ help: try: `f32::from(x2)` error: casting `i16` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:14:13 + --> tests/ui/cast_lossless_float.rs:19:13 | LL | let _ = x2 as f64; | ^^^^^^^^^ help: try: `f64::from(x2)` error: casting `u16` to `f32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:16:13 + --> tests/ui/cast_lossless_float.rs:21:13 | LL | let _ = x3 as f32; | ^^^^^^^^^ help: try: `f32::from(x3)` error: casting `u16` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:17:13 + --> tests/ui/cast_lossless_float.rs:22:13 | LL | let _ = x3 as f64; | ^^^^^^^^^ help: try: `f64::from(x3)` error: casting `i32` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:19:13 + --> tests/ui/cast_lossless_float.rs:24:13 | LL | let _ = x4 as f64; | ^^^^^^^^^ help: try: `f64::from(x4)` error: casting `u32` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:21:13 + --> tests/ui/cast_lossless_float.rs:26:13 | LL | let _ = x5 as f64; | ^^^^^^^^^ help: try: `f64::from(x5)` error: casting `f32` to `f64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_float.rs:24:13 + --> tests/ui/cast_lossless_float.rs:29:13 | LL | let _ = 1.0f32 as f64; | ^^^^^^^^^^^^^ help: try: `f64::from(1.0f32)` -error: aborting due to 11 previous errors +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.fixed b/src/tools/clippy/tests/ui/cast_lossless_integer.fixed index 5e7e545e764..291556a9774 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_integer.fixed +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.fixed @@ -1,6 +1,9 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] +type I64 = i64; +type U128 = u128; + fn main() { // Test clippy::cast_lossless with casts to integer types let _ = i16::from(1i8); @@ -24,6 +27,13 @@ fn main() { // Test with an expression wrapped in parens let _ = u16::from(1u8 + 1u8); + + let _ = I64::from(1i8); + + // Do not lint if destination type is u128 + // see https://github.com/rust-lang/rust-clippy/issues/12492 + let _ = 1u8 as u128; + let _ = 1u8 as U128; } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.rs b/src/tools/clippy/tests/ui/cast_lossless_integer.rs index 0d69ddbd586..a917c7a371d 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_integer.rs +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.rs @@ -1,6 +1,9 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation, dead_code)] #![warn(clippy::cast_lossless)] +type I64 = i64; +type U128 = u128; + fn main() { // Test clippy::cast_lossless with casts to integer types let _ = 1i8 as i16; @@ -24,6 +27,13 @@ fn main() { // Test with an expression wrapped in parens let _ = (1u8 + 1u8) as u16; + + let _ = 1i8 as I64; + + // Do not lint if destination type is u128 + // see https://github.com/rust-lang/rust-clippy/issues/12492 + let _ = 1u8 as u128; + let _ = 1u8 as U128; } // The lint would suggest using `f64::from(input)` here but the `XX::from` function is not const, diff --git a/src/tools/clippy/tests/ui/cast_lossless_integer.stderr b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr index 43d4ce3ce91..aaece939285 100644 --- a/src/tools/clippy/tests/ui/cast_lossless_integer.stderr +++ b/src/tools/clippy/tests/ui/cast_lossless_integer.stderr @@ -1,5 +1,5 @@ error: casting `i8` to `i16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:6:13 + --> tests/ui/cast_lossless_integer.rs:9:13 | LL | let _ = 1i8 as i16; | ^^^^^^^^^^ help: try: `i16::from(1i8)` @@ -8,124 +8,130 @@ LL | let _ = 1i8 as i16; = help: to override `-D warnings` add `#[allow(clippy::cast_lossless)]` error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:7:13 + --> tests/ui/cast_lossless_integer.rs:10:13 | LL | let _ = 1i8 as i32; | ^^^^^^^^^^ help: try: `i32::from(1i8)` error: casting `i8` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:8:13 + --> tests/ui/cast_lossless_integer.rs:11:13 | LL | let _ = 1i8 as i64; | ^^^^^^^^^^ help: try: `i64::from(1i8)` error: casting `u8` to `i16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:9:13 + --> tests/ui/cast_lossless_integer.rs:12:13 | LL | let _ = 1u8 as i16; | ^^^^^^^^^^ help: try: `i16::from(1u8)` error: casting `u8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:10:13 + --> tests/ui/cast_lossless_integer.rs:13:13 | LL | let _ = 1u8 as i32; | ^^^^^^^^^^ help: try: `i32::from(1u8)` error: casting `u8` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:11:13 + --> tests/ui/cast_lossless_integer.rs:14:13 | LL | let _ = 1u8 as i64; | ^^^^^^^^^^ help: try: `i64::from(1u8)` error: casting `u8` to `u16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:12:13 + --> tests/ui/cast_lossless_integer.rs:15:13 | LL | let _ = 1u8 as u16; | ^^^^^^^^^^ help: try: `u16::from(1u8)` error: casting `u8` to `u32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:13:13 + --> tests/ui/cast_lossless_integer.rs:16:13 | LL | let _ = 1u8 as u32; | ^^^^^^^^^^ help: try: `u32::from(1u8)` error: casting `u8` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:14:13 + --> tests/ui/cast_lossless_integer.rs:17:13 | LL | let _ = 1u8 as u64; | ^^^^^^^^^^ help: try: `u64::from(1u8)` error: casting `i16` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:15:13 + --> tests/ui/cast_lossless_integer.rs:18:13 | LL | let _ = 1i16 as i32; | ^^^^^^^^^^^ help: try: `i32::from(1i16)` error: casting `i16` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:16:13 + --> tests/ui/cast_lossless_integer.rs:19:13 | LL | let _ = 1i16 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1i16)` error: casting `u16` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:17:13 + --> tests/ui/cast_lossless_integer.rs:20:13 | LL | let _ = 1u16 as i32; | ^^^^^^^^^^^ help: try: `i32::from(1u16)` error: casting `u16` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:18:13 + --> tests/ui/cast_lossless_integer.rs:21:13 | LL | let _ = 1u16 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1u16)` error: casting `u16` to `u32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:19:13 + --> tests/ui/cast_lossless_integer.rs:22:13 | LL | let _ = 1u16 as u32; | ^^^^^^^^^^^ help: try: `u32::from(1u16)` error: casting `u16` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:20:13 + --> tests/ui/cast_lossless_integer.rs:23:13 | LL | let _ = 1u16 as u64; | ^^^^^^^^^^^ help: try: `u64::from(1u16)` error: casting `i32` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:21:13 + --> tests/ui/cast_lossless_integer.rs:24:13 | LL | let _ = 1i32 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1i32)` error: casting `u32` to `i64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:22:13 + --> tests/ui/cast_lossless_integer.rs:25:13 | LL | let _ = 1u32 as i64; | ^^^^^^^^^^^ help: try: `i64::from(1u32)` error: casting `u32` to `u64` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:23:13 + --> tests/ui/cast_lossless_integer.rs:26:13 | LL | let _ = 1u32 as u64; | ^^^^^^^^^^^ help: try: `u64::from(1u32)` error: casting `u8` to `u16` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:26:13 + --> tests/ui/cast_lossless_integer.rs:29:13 | LL | let _ = (1u8 + 1u8) as u16; | ^^^^^^^^^^^^^^^^^^ help: try: `u16::from(1u8 + 1u8)` +error: casting `i8` to `I64` may become silently lossy if you later change the type + --> tests/ui/cast_lossless_integer.rs:31:13 + | +LL | let _ = 1i8 as I64; + | ^^^^^^^^^^ help: try: `I64::from(1i8)` + error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:60:13 + --> tests/ui/cast_lossless_integer.rs:70:13 | LL | let _ = sign_cast!(x, u8, i8) as i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8))` error: casting `i8` to `i32` may become silently lossy if you later change the type - --> tests/ui/cast_lossless_integer.rs:61:13 + --> tests/ui/cast_lossless_integer.rs:71:13 | LL | let _ = (sign_cast!(x, u8, i8) + 1) as i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::from(sign_cast!(x, u8, i8) + 1)` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/const_is_empty.rs b/src/tools/clippy/tests/ui/const_is_empty.rs new file mode 100644 index 00000000000..ae37a82e4f9 --- /dev/null +++ b/src/tools/clippy/tests/ui/const_is_empty.rs @@ -0,0 +1,174 @@ +#![feature(inline_const)] +#![warn(clippy::const_is_empty)] +#![allow(clippy::needless_late_init, unused_must_use)] + +fn test_literal() { + if "".is_empty() { + //~^ERROR: this expression always evaluates to true + } + if "foobar".is_empty() { + //~^ERROR: this expression always evaluates to false + } +} + +fn test_byte_literal() { + if b"".is_empty() { + //~^ERROR: this expression always evaluates to true + } + if b"foobar".is_empty() { + //~^ERROR: this expression always evaluates to false + } +} + +fn test_no_mut() { + let mut empty = ""; + if empty.is_empty() { + // No lint because it is mutable + } +} + +fn test_propagated() { + let empty = ""; + let non_empty = "foobar"; + let empty2 = empty; + let non_empty2 = non_empty; + if empty2.is_empty() { + //~^ERROR: this expression always evaluates to true + } + if non_empty2.is_empty() { + //~^ERROR: this expression always evaluates to false + } +} + +const EMPTY_STR: &str = ""; +const NON_EMPTY_STR: &str = "foo"; +const EMPTY_BSTR: &[u8] = b""; +const NON_EMPTY_BSTR: &[u8] = b"foo"; +const EMPTY_U8_SLICE: &[u8] = &[]; +const NON_EMPTY_U8_SLICE: &[u8] = &[1, 2]; +const EMPTY_SLICE: &[u32] = &[]; +const NON_EMPTY_SLICE: &[u32] = &[1, 2]; +const NON_EMPTY_SLICE_REPEAT: &[u32] = &[1; 2]; +const EMPTY_ARRAY: [u32; 0] = []; +const EMPTY_ARRAY_REPEAT: [u32; 0] = [1; 0]; +const NON_EMPTY_ARRAY: [u32; 2] = [1, 2]; +const NON_EMPTY_ARRAY_REPEAT: [u32; 2] = [1; 2]; +const EMPTY_REF_ARRAY: &[u32; 0] = &[]; +const NON_EMPTY_REF_ARRAY: &[u32; 3] = &[1, 2, 3]; + +fn test_from_const() { + let _ = EMPTY_STR.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_STR.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_BSTR.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_BSTR.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = EMPTY_ARRAY_REPEAT.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = EMPTY_U8_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_U8_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = NON_EMPTY_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_REF_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_REF_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); + //~^ ERROR: this expression always evaluates to false +} + +fn main() { + let value = "foobar"; + let _ = value.is_empty(); + //~^ ERROR: this expression always evaluates to false + let x = value; + let _ = x.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = "".is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = b"".is_empty(); + //~^ ERROR: this expression always evaluates to true +} + +fn str_from_arg(var: &str) { + var.is_empty(); + // Do not lint, we know nothiny about var +} + +fn update_str() { + let mut value = "duck"; + value = "penguin"; + + let _ = value.is_empty(); + // Do not lint since value is mutable +} + +fn macros() { + // Content from Macro + let file = include_str!("const_is_empty.rs"); + let _ = file.is_empty(); + // No lint because initializer comes from a macro result + + let var = env!("PATH"); + let _ = var.is_empty(); + // No lint because initializer comes from a macro result +} + +fn conditional_value() { + let value; + + if true { + value = "hey"; + } else { + value = "hej"; + } + + let _ = value.is_empty(); + // Do not lint, current constant folding is too simple to detect this +} + +fn cfg_conditioned() { + #[cfg(test)] + let val = ""; + #[cfg(not(test))] + let val = "foo"; + + let _ = val.is_empty(); + // Do not lint, value depend on a #[cfg(…)] directive +} + +fn not_cfg_conditioned() { + let val = ""; + #[cfg(not(target_os = "inexistent"))] + let _ = val.is_empty(); + //~^ ERROR: this expression always evaluates to true +} + +const fn const_rand() -> &'static str { + "17" +} + +fn const_expressions() { + let _ = const { if true { "1" } else { "2" } }.is_empty(); + // Do not lint, we do not recurse into boolean expressions + + let _ = const_rand().is_empty(); + // Do not lint, we do not recurse into functions +} + +fn constant_from_external_crate() { + let _ = std::env::consts::EXE_EXTENSION.is_empty(); + // Do not lint, `exe_ext` comes from the `std` crate +} diff --git a/src/tools/clippy/tests/ui/const_is_empty.stderr b/src/tools/clippy/tests/ui/const_is_empty.stderr new file mode 100644 index 00000000000..0e09da77bb4 --- /dev/null +++ b/src/tools/clippy/tests/ui/const_is_empty.stderr @@ -0,0 +1,161 @@ +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:6:8 + | +LL | if "".is_empty() { + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::const-is-empty` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::const_is_empty)]` + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:9:8 + | +LL | if "foobar".is_empty() { + | ^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:15:8 + | +LL | if b"".is_empty() { + | ^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:18:8 + | +LL | if b"foobar".is_empty() { + | ^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:35:8 + | +LL | if empty2.is_empty() { + | ^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:38:8 + | +LL | if non_empty2.is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:60:13 + | +LL | let _ = EMPTY_STR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:62:13 + | +LL | let _ = NON_EMPTY_STR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:64:13 + | +LL | let _ = EMPTY_BSTR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:66:13 + | +LL | let _ = NON_EMPTY_BSTR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:68:13 + | +LL | let _ = EMPTY_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:70:13 + | +LL | let _ = EMPTY_ARRAY_REPEAT.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:72:13 + | +LL | let _ = EMPTY_U8_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:74:13 + | +LL | let _ = NON_EMPTY_U8_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:76:13 + | +LL | let _ = NON_EMPTY_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:78:13 + | +LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:80:13 + | +LL | let _ = EMPTY_REF_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:82:13 + | +LL | let _ = NON_EMPTY_REF_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:84:13 + | +LL | let _ = EMPTY_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:86:13 + | +LL | let _ = NON_EMPTY_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:88:13 + | +LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:94:13 + | +LL | let _ = value.is_empty(); + | ^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:97:13 + | +LL | let _ = x.is_empty(); + | ^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:99:13 + | +LL | let _ = "".is_empty(); + | ^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:101:13 + | +LL | let _ = b"".is_empty(); + | ^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:155:13 + | +LL | let _ = val.is_empty(); + | ^^^^^^^^^^^^^^ + +error: aborting due to 26 previous errors + diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.fixed b/src/tools/clippy/tests/ui/crashes/ice-12491.fixed new file mode 100644 index 00000000000..4ea480b0663 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.fixed @@ -0,0 +1,7 @@ +#![warn(clippy::needless_return)] + +fn main() { + if (true) { + // anything一些中文 + } +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.rs b/src/tools/clippy/tests/ui/crashes/ice-12491.rs new file mode 100644 index 00000000000..60add6afa2c --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.rs @@ -0,0 +1,8 @@ +#![warn(clippy::needless_return)] + +fn main() { + if (true) { + // anything一些中文 + return; + } +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-12491.stderr b/src/tools/clippy/tests/ui/crashes/ice-12491.stderr new file mode 100644 index 00000000000..7cc418898e8 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-12491.stderr @@ -0,0 +1,19 @@ +error: unneeded `return` statement + --> tests/ui/crashes/ice-12491.rs:5:24 + | +LL | // anything一些中文 + | ____________________________^ +LL | | return; + | |______________^ + | + = note: `-D clippy::needless-return` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_return)]` +help: remove `return` + | +LL - // anything一些中文 +LL - return; +LL + // anything一些中文 + | + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/crashes/ice-6179.rs b/src/tools/clippy/tests/ui/crashes/ice-6179.rs index fffc0f7d0d4..91160eef03d 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6179.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-6179.rs @@ -1,5 +1,5 @@ //! This is a minimal reproducer for the ICE in https://github.com/rust-lang/rust-clippy/pull/6179. -//! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details. +//! The ICE is mainly caused by using `lower_ty`. See the discussion in the PR for details. #![warn(clippy::use_self)] #![allow(dead_code, clippy::let_with_type_underscore)] diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed new file mode 100644 index 00000000000..e3525191423 --- /dev/null +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed @@ -0,0 +1,111 @@ +#![warn(clippy::dbg_macro)] +#![allow(clippy::unnecessary_operation, clippy::no_effect)] + +fn foo(n: u32) -> u32 { + if let Some(n) = n.checked_sub(4) { n } else { n } + //~^ ERROR: the `dbg!` macro is intended as a debugging tool +} +fn bar(_: ()) {} + +fn factorial(n: u32) -> u32 { + if n <= 1 { + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + 1 + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + } else { + n * factorial(n - 1) + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + } +} + +fn main() { + 42; + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + foo(3) + factorial(4); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + (1, 2, 3, 4, 5); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool +} + +fn issue9914() { + macro_rules! foo { + ($x:expr) => { + $x; + }; + } + macro_rules! foo2 { + ($x:expr) => { + $x; + }; + } + macro_rules! expand_to_dbg { + () => { + + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + }; + } + + + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + #[allow(clippy::let_unit_value)] + let _ = (); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + bar(()); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + foo!(()); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + foo2!(foo!(())); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + expand_to_dbg!(); +} + +mod issue7274 { + trait Thing<'b> { + fn foo(&self); + } + + macro_rules! define_thing { + ($thing:ident, $body:expr) => { + impl<'a> Thing<'a> for $thing { + fn foo<'b>(&self) { + $body + } + } + }; + } + + struct MyThing; + define_thing!(MyThing, { + 2; + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + }); +} + +#[test] +pub fn issue8481() { + 1; + //~^ ERROR: the `dbg!` macro is intended as a debugging tool +} + +#[cfg(test)] +fn foo2() { + 1; + //~^ ERROR: the `dbg!` macro is intended as a debugging tool +} + +#[cfg(test)] +mod mod1 { + fn func() { + 1; + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + } +} + +mod issue12131 { + fn dbg_in_print(s: &str) { + println!("dbg: {:?}", s); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + print!("{}", s); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + } +} diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs index 3f4770c63d0..80606c2db05 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs @@ -1,9 +1,5 @@ -//@no-rustfix - #![warn(clippy::dbg_macro)] - -#[path = "auxiliary/submodule.rs"] -mod submodule; +#![allow(clippy::unnecessary_operation, clippy::no_effect)] fn foo(n: u32) -> u32 { if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } @@ -25,12 +21,8 @@ fn factorial(n: u32) -> u32 { fn main() { dbg!(42); //~^ ERROR: the `dbg!` macro is intended as a debugging tool - dbg!(dbg!(dbg!(42))); - //~^ ERROR: the `dbg!` macro is intended as a debugging tool foo(3) + dbg!(factorial(4)); //~^ ERROR: the `dbg!` macro is intended as a debugging tool - dbg!(1, 2, dbg!(3, 4)); - //~^ ERROR: the `dbg!` macro is intended as a debugging tool dbg!(1, 2, 3, 4, 5); //~^ ERROR: the `dbg!` macro is intended as a debugging tool } @@ -49,6 +41,7 @@ fn issue9914() { macro_rules! expand_to_dbg { () => { dbg!(); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool }; } @@ -107,3 +100,12 @@ mod mod1 { //~^ ERROR: the `dbg!` macro is intended as a debugging tool } } + +mod issue12131 { + fn dbg_in_print(s: &str) { + println!("dbg: {:?}", dbg!(s)); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + print!("{}", dbg!(s)); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + } +} diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr index 5ad0bbfed94..86667701da0 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr @@ -1,30 +1,18 @@ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/auxiliary/submodule.rs:2:5 - | -LL | dbg!(); - | ^^^^^^^ - | - = note: `-D clippy::dbg-macro` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]` -help: remove the invocation before committing it to a version control system - | -LL - dbg!(); -LL + - | - -error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:9:22 + --> tests/ui/dbg_macro/dbg_macro.rs:5:22 | LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ | + = note: `-D clippy::dbg-macro` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]` help: remove the invocation before committing it to a version control system | LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ~~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:15:8 + --> tests/ui/dbg_macro/dbg_macro.rs:11:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -35,7 +23,7 @@ LL | if n <= 1 { | ~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:17:9 + --> tests/ui/dbg_macro/dbg_macro.rs:13:9 | LL | dbg!(1) | ^^^^^^^ @@ -46,7 +34,7 @@ LL | 1 | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:20:9 + --> tests/ui/dbg_macro/dbg_macro.rs:16:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -57,7 +45,7 @@ LL | n * factorial(n - 1) | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:26:5 + --> tests/ui/dbg_macro/dbg_macro.rs:22:5 | LL | dbg!(42); | ^^^^^^^^ @@ -68,18 +56,7 @@ LL | 42; | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:28:5 - | -LL | dbg!(dbg!(dbg!(42))); - | ^^^^^^^^^^^^^^^^^^^^ - | -help: remove the invocation before committing it to a version control system - | -LL | dbg!(dbg!(42)); - | ~~~~~~~~~~~~~~ - -error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:30:14 + --> tests/ui/dbg_macro/dbg_macro.rs:24:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ @@ -90,18 +67,7 @@ LL | foo(3) + factorial(4); | ~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:32:5 - | -LL | dbg!(1, 2, dbg!(3, 4)); - | ^^^^^^^^^^^^^^^^^^^^^^ - | -help: remove the invocation before committing it to a version control system - | -LL | (1, 2, dbg!(3, 4)); - | ~~~~~~~~~~~~~~~~~~ - -error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:34:5 + --> tests/ui/dbg_macro/dbg_macro.rs:26:5 | LL | dbg!(1, 2, 3, 4, 5); | ^^^^^^^^^^^^^^^^^^^ @@ -112,7 +78,7 @@ LL | (1, 2, 3, 4, 5); | ~~~~~~~~~~~~~~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:55:5 + --> tests/ui/dbg_macro/dbg_macro.rs:48:5 | LL | dbg!(); | ^^^^^^^ @@ -124,7 +90,7 @@ LL + | error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:58:13 + --> tests/ui/dbg_macro/dbg_macro.rs:51:13 | LL | let _ = dbg!(); | ^^^^^^ @@ -135,7 +101,7 @@ LL | let _ = (); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:60:9 + --> tests/ui/dbg_macro/dbg_macro.rs:53:9 | LL | bar(dbg!()); | ^^^^^^ @@ -146,7 +112,7 @@ LL | bar(()); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:62:10 + --> tests/ui/dbg_macro/dbg_macro.rs:55:10 | LL | foo!(dbg!()); | ^^^^^^ @@ -157,7 +123,7 @@ LL | foo!(()); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:64:16 + --> tests/ui/dbg_macro/dbg_macro.rs:57:16 | LL | foo2!(foo!(dbg!())); | ^^^^^^ @@ -168,7 +134,23 @@ LL | foo2!(foo!(())); | ~~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:86:9 + --> tests/ui/dbg_macro/dbg_macro.rs:43:13 + | +LL | dbg!(); + | ^^^^^^^ +... +LL | expand_to_dbg!(); + | ---------------- in this macro invocation + | + = note: this error originates in the macro `expand_to_dbg` (in Nightly builds, run with -Z macro-backtrace for more info) +help: remove the invocation before committing it to a version control system + | +LL - dbg!(); +LL + + | + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro.rs:79:9 | LL | dbg!(2); | ^^^^^^^ @@ -179,7 +161,7 @@ LL | 2; | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:93:5 + --> tests/ui/dbg_macro/dbg_macro.rs:86:5 | LL | dbg!(1); | ^^^^^^^ @@ -190,7 +172,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:99:5 + --> tests/ui/dbg_macro/dbg_macro.rs:92:5 | LL | dbg!(1); | ^^^^^^^ @@ -201,7 +183,7 @@ LL | 1; | ~ error: the `dbg!` macro is intended as a debugging tool - --> tests/ui/dbg_macro/dbg_macro.rs:106:9 + --> tests/ui/dbg_macro/dbg_macro.rs:99:9 | LL | dbg!(1); | ^^^^^^^ @@ -211,5 +193,27 @@ help: remove the invocation before committing it to a version control system LL | 1; | ~ +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro.rs:106:31 + | +LL | println!("dbg: {:?}", dbg!(s)); + | ^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | println!("dbg: {:?}", s); + | ~ + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro.rs:108:22 + | +LL | print!("{}", dbg!(s)); + | ^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | print!("{}", s); + | ~ + error: aborting due to 19 previous errors diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs new file mode 100644 index 00000000000..0e83766ccae --- /dev/null +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.rs @@ -0,0 +1,12 @@ +//@no-rustfix +#![warn(clippy::dbg_macro)] + +#[path = "auxiliary/submodule.rs"] +mod submodule; + +fn main() { + dbg!(dbg!(dbg!(42))); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool + dbg!(1, 2, dbg!(3, 4)); + //~^ ERROR: the `dbg!` macro is intended as a debugging tool +} diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr new file mode 100644 index 00000000000..d21595c2fcd --- /dev/null +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro_unfixable.stderr @@ -0,0 +1,71 @@ +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/auxiliary/submodule.rs:2:5 + | +LL | dbg!(); + | ^^^^^^^ + | + = note: `-D clippy::dbg-macro` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]` +help: remove the invocation before committing it to a version control system + | +LL - dbg!(); +LL + + | + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:5 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | dbg!(dbg!(42)); + | ~~~~~~~~~~~~~~ + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:10 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | dbg!(dbg!(42)); + | ~~~~~~~~ + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:8:15 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | dbg!(dbg!(42)); + | ~~ + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:10:5 + | +LL | dbg!(1, 2, dbg!(3, 4)); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | (1, 2, dbg!(3, 4)); + | ~~~~~~~~~~~~~~~~~~ + +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro_unfixable.rs:10:16 + | +LL | dbg!(1, 2, dbg!(3, 4)); + | ^^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL | dbg!(1, 2, (3, 4)); + | ~~~~~~ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/doc/issue_10262.fixed b/src/tools/clippy/tests/ui/doc/issue_10262.fixed new file mode 100644 index 00000000000..5d067736d55 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/issue_10262.fixed @@ -0,0 +1,12 @@ +#![warn(clippy::doc_markdown)] + +// Should only warn for the first line! +/// `AviSynth` documentation: +//~^ ERROR: item in documentation is missing backticks +/// +/// > AvisynthPluginInit3 may be called more than once with different IScriptEnvironments. +/// +/// <blockquote>bla AvisynthPluginInit3 bla</blockquote> +/// +/// <q>bla AvisynthPluginInit3 bla</q> +pub struct Foo; diff --git a/src/tools/clippy/tests/ui/doc/issue_10262.rs b/src/tools/clippy/tests/ui/doc/issue_10262.rs new file mode 100644 index 00000000000..e2cbd938d5d --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/issue_10262.rs @@ -0,0 +1,12 @@ +#![warn(clippy::doc_markdown)] + +// Should only warn for the first line! +/// AviSynth documentation: +//~^ ERROR: item in documentation is missing backticks +/// +/// > AvisynthPluginInit3 may be called more than once with different IScriptEnvironments. +/// +/// <blockquote>bla AvisynthPluginInit3 bla</blockquote> +/// +/// <q>bla AvisynthPluginInit3 bla</q> +pub struct Foo; diff --git a/src/tools/clippy/tests/ui/doc/issue_10262.stderr b/src/tools/clippy/tests/ui/doc/issue_10262.stderr new file mode 100644 index 00000000000..f43d9551e94 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/issue_10262.stderr @@ -0,0 +1,15 @@ +error: item in documentation is missing backticks + --> tests/ui/doc/issue_10262.rs:4:5 + | +LL | /// AviSynth documentation: + | ^^^^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]` +help: try + | +LL | /// `AviSynth` documentation: + | ~~~~~~~~~~ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.rs b/src/tools/clippy/tests/ui/duplicated_attributes.rs new file mode 100644 index 00000000000..0f036c684c1 --- /dev/null +++ b/src/tools/clippy/tests/ui/duplicated_attributes.rs @@ -0,0 +1,17 @@ +#![warn(clippy::duplicated_attributes)] +#![cfg(any(unix, windows))] +#![allow(dead_code)] +#![allow(dead_code)] //~ ERROR: duplicated attribute +#![cfg(any(unix, windows))] +//~^ ERROR: duplicated attribute +//~| ERROR: duplicated attribute + +#[cfg(any(unix, windows, target_os = "linux"))] +#[allow(dead_code)] +#[allow(dead_code)] //~ ERROR: duplicated attribute +#[cfg(any(unix, windows, target_os = "linux"))] +//~^ ERROR: duplicated attribute +//~| ERROR: duplicated attribute +fn foo() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.stderr b/src/tools/clippy/tests/ui/duplicated_attributes.stderr new file mode 100644 index 00000000000..1c6578dbb43 --- /dev/null +++ b/src/tools/clippy/tests/ui/duplicated_attributes.stderr @@ -0,0 +1,123 @@ +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:4:10 + | +LL | #![allow(dead_code)] + | ^^^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:3:10 + | +LL | #![allow(dead_code)] + | ^^^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:4:10 + | +LL | #![allow(dead_code)] + | ^^^^^^^^^ + = note: `-D clippy::duplicated-attributes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::duplicated_attributes)]` + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:5:12 + | +LL | #![cfg(any(unix, windows))] + | ^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:2:12 + | +LL | #![cfg(any(unix, windows))] + | ^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:5:12 + | +LL | #![cfg(any(unix, windows))] + | ^^^^ + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:5:18 + | +LL | #![cfg(any(unix, windows))] + | ^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:2:18 + | +LL | #![cfg(any(unix, windows))] + | ^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:5:18 + | +LL | #![cfg(any(unix, windows))] + | ^^^^^^^ + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:11:9 + | +LL | #[allow(dead_code)] + | ^^^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:10:9 + | +LL | #[allow(dead_code)] + | ^^^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:11:9 + | +LL | #[allow(dead_code)] + | ^^^^^^^^^ + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:12:11 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:9:11 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:12:11 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^ + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:12:17 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:9:17 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:12:17 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^ + +error: duplicated attribute + --> tests/ui/duplicated_attributes.rs:12:26 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^^^^^^^^^^^^^ + | +note: first defined here + --> tests/ui/duplicated_attributes.rs:9:26 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^^^^^^^^^^^^^ +help: remove this attribute + --> tests/ui/duplicated_attributes.rs:12:26 + | +LL | #[cfg(any(unix, windows, target_os = "linux"))] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/else_if_without_else.rs b/src/tools/clippy/tests/ui/else_if_without_else.rs index e7786f7dd27..b04c22fa2ae 100644 --- a/src/tools/clippy/tests/ui/else_if_without_else.rs +++ b/src/tools/clippy/tests/ui/else_if_without_else.rs @@ -1,7 +1,5 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - -#![warn(clippy::all)] #![warn(clippy::else_if_without_else)] +#![allow(clippy::collapsible_else_if)] fn bla1() -> bool { unimplemented!() @@ -12,6 +10,12 @@ fn bla2() -> bool { fn bla3() -> bool { unimplemented!() } +fn bla4() -> bool { + unimplemented!() +} +fn bla5() -> bool { + unimplemented!() +} fn main() { if bla1() { @@ -57,4 +61,62 @@ fn main() { //~^ ERROR: `if` expression with an `else if`, but without a final `else` println!("else if 2"); } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else if bla3() { + println!("else if 2"); + } else if bla4() { + println!("else if 3"); + } else if bla5() { + println!("else if 4"); + } else { + println!("else"); + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else if bla3() { + println!("else if 2"); + } else if bla4() { + println!("else if 3"); + } else if bla5() { + //~^ ERROR: `if` expression with an `else if`, but without a final `else` + println!("else if 4"); + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else { + if bla3() { + println!("else if 2"); + } else if bla4() { + println!("else if 3"); + } else if bla5() { + println!("else if 4"); + } else { + println!("else"); + } + } + + if bla1() { + println!("if"); + } else if bla2() { + println!("else if 1"); + } else { + if bla3() { + println!("else if 2"); + } else if bla4() { + println!("else if 3"); + } else if bla5() { + //~^ ERROR: `if` expression with an `else if`, but without a final `else` + println!("else if 4"); + } + } } diff --git a/src/tools/clippy/tests/ui/else_if_without_else.stderr b/src/tools/clippy/tests/ui/else_if_without_else.stderr index 3bb840f39e7..bc717485229 100644 --- a/src/tools/clippy/tests/ui/else_if_without_else.stderr +++ b/src/tools/clippy/tests/ui/else_if_without_else.stderr @@ -1,5 +1,5 @@ error: `if` expression with an `else if`, but without a final `else` - --> tests/ui/else_if_without_else.rs:47:12 + --> tests/ui/else_if_without_else.rs:51:12 | LL | } else if bla2() { | ____________^ @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::else_if_without_else)]` error: `if` expression with an `else if`, but without a final `else` - --> tests/ui/else_if_without_else.rs:56:12 + --> tests/ui/else_if_without_else.rs:60:12 | LL | } else if bla3() { | ____________^ @@ -24,5 +24,29 @@ LL | | } | = help: add an `else` block here -error: aborting due to 2 previous errors +error: `if` expression with an `else if`, but without a final `else` + --> tests/ui/else_if_without_else.rs:87:12 + | +LL | } else if bla5() { + | ____________^ +LL | | +LL | | println!("else if 4"); +LL | | } + | |_____^ + | + = help: add an `else` block here + +error: `if` expression with an `else if`, but without a final `else` + --> tests/ui/else_if_without_else.rs:117:16 + | +LL | } else if bla5() { + | ________________^ +LL | | +LL | | println!("else if 4"); +LL | | } + | |_________^ + | + = help: add an `else` block here + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/empty_docs.rs b/src/tools/clippy/tests/ui/empty_docs.rs index 272fab7d5ca..00e64eebc5f 100644 --- a/src/tools/clippy/tests/ui/empty_docs.rs +++ b/src/tools/clippy/tests/ui/empty_docs.rs @@ -1,6 +1,9 @@ +//@aux-build:proc_macro_attr.rs + #![allow(unused)] #![warn(clippy::empty_docs)] #![allow(clippy::mixed_attributes_style)] +#![feature(extern_types)] mod outer { //! @@ -67,3 +70,17 @@ mod outer { y: i32, } } + +mod issue_12377 { + use proc_macro_attr::with_empty_docs; + + #[with_empty_docs] + extern "C" { + type Test; + } + + #[with_empty_docs] + struct Foo { + a: u8, + } +} diff --git a/src/tools/clippy/tests/ui/empty_docs.stderr b/src/tools/clippy/tests/ui/empty_docs.stderr index f12aead6aa7..28ebea22c5d 100644 --- a/src/tools/clippy/tests/ui/empty_docs.stderr +++ b/src/tools/clippy/tests/ui/empty_docs.stderr @@ -1,5 +1,5 @@ error: empty doc comment - --> tests/ui/empty_docs.rs:6:5 + --> tests/ui/empty_docs.rs:9:5 | LL | //! | ^^^ @@ -9,7 +9,7 @@ LL | //! = help: to override `-D warnings` add `#[allow(clippy::empty_docs)]` error: empty doc comment - --> tests/ui/empty_docs.rs:14:5 + --> tests/ui/empty_docs.rs:17:5 | LL | /// | ^^^ @@ -17,7 +17,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:16:9 + --> tests/ui/empty_docs.rs:19:9 | LL | /// | ^^^ @@ -25,7 +25,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:27:5 + --> tests/ui/empty_docs.rs:30:5 | LL | #[doc = ""] | ^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | #[doc = ""] = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:30:5 + --> tests/ui/empty_docs.rs:33:5 | LL | / #[doc = ""] LL | | #[doc = ""] @@ -42,7 +42,7 @@ LL | | #[doc = ""] = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:37:5 + --> tests/ui/empty_docs.rs:40:5 | LL | /// | ^^^ @@ -50,7 +50,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:50:13 + --> tests/ui/empty_docs.rs:53:13 | LL | /*! */ | ^^^^^^ @@ -58,7 +58,7 @@ LL | /*! */ = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:58:13 + --> tests/ui/empty_docs.rs:61:13 | LL | /// | ^^^ @@ -66,7 +66,7 @@ LL | /// = help: consider removing or filling it error: empty doc comment - --> tests/ui/empty_docs.rs:66:9 + --> tests/ui/empty_docs.rs:69:9 | LL | /// | ^^^ diff --git a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs index e843770f578..dd78491749c 100644 --- a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs +++ b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macro_attr.rs #![warn(clippy::empty_line_after_doc_comments)] -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)] #![feature(custom_inner_attributes)] #![rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs index 269e66ea0a8..f147cf2cd5d 100644 --- a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs +++ b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs @@ -1,6 +1,6 @@ //@aux-build:proc_macro_attr.rs #![warn(clippy::empty_line_after_outer_attr)] -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)] #![feature(custom_inner_attributes)] #![rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/entry.fixed b/src/tools/clippy/tests/ui/entry.fixed index 71ec13f4610..abdfae2a3e1 100644 --- a/src/tools/clippy/tests/ui/entry.fixed +++ b/src/tools/clippy/tests/ui/entry.fixed @@ -176,4 +176,14 @@ pub fn issue_11935() { } } +fn issue12489(map: &mut HashMap<u64, u64>) -> Option<()> { + if let std::collections::hash_map::Entry::Vacant(e) = map.entry(1) { + let Some(1) = Some(2) else { + return None; + }; + e.insert(42); + } + Some(()) +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/entry.rs b/src/tools/clippy/tests/ui/entry.rs index 86092b7c055..7774f99a2a2 100644 --- a/src/tools/clippy/tests/ui/entry.rs +++ b/src/tools/clippy/tests/ui/entry.rs @@ -180,4 +180,14 @@ pub fn issue_11935() { } } +fn issue12489(map: &mut HashMap<u64, u64>) -> Option<()> { + if !map.contains_key(&1) { + let Some(1) = Some(2) else { + return None; + }; + map.insert(1, 42); + } + Some(()) +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/entry.stderr b/src/tools/clippy/tests/ui/entry.stderr index ef4c36bcf54..fb467676606 100644 --- a/src/tools/clippy/tests/ui/entry.stderr +++ b/src/tools/clippy/tests/ui/entry.stderr @@ -214,5 +214,26 @@ LL + v LL + }); | -error: aborting due to 10 previous errors +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> tests/ui/entry.rs:184:5 + | +LL | / if !map.contains_key(&1) { +LL | | let Some(1) = Some(2) else { +LL | | return None; +LL | | }; +LL | | map.insert(1, 42); +LL | | } + | |_____^ + | +help: try + | +LL ~ if let std::collections::hash_map::Entry::Vacant(e) = map.entry(1) { +LL + let Some(1) = Some(2) else { +LL + return None; +LL + }; +LL + e.insert(42); +LL + } + | + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/integer_division_remainder_used.rs b/src/tools/clippy/tests/ui/integer_division_remainder_used.rs new file mode 100644 index 00000000000..5d1b02095d1 --- /dev/null +++ b/src/tools/clippy/tests/ui/integer_division_remainder_used.rs @@ -0,0 +1,41 @@ +#![warn(clippy::integer_division_remainder_used)] +#![allow(unused_variables)] +#![allow(clippy::op_ref)] + +struct CustomOps(pub i32); +impl std::ops::Div for CustomOps { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0 / rhs.0) + } +} +impl std::ops::Rem for CustomOps { + type Output = Self; + + fn rem(self, rhs: Self) -> Self::Output { + Self(self.0 % rhs.0) + } +} + +fn main() { + // should trigger + let a = 10; + let b = 5; + let c = a / b; + let d = a % b; + let e = &a / b; + let f = a % &b; + let g = &a / &b; + let h = &10 % b; + let i = a / &4; + + // should not trigger on custom Div and Rem + let w = CustomOps(3); + let x = CustomOps(4); + let y = w / x; + + let w = CustomOps(3); + let x = CustomOps(4); + let z = w % x; +} diff --git a/src/tools/clippy/tests/ui/integer_division_remainder_used.stderr b/src/tools/clippy/tests/ui/integer_division_remainder_used.stderr new file mode 100644 index 00000000000..8adfda28893 --- /dev/null +++ b/src/tools/clippy/tests/ui/integer_division_remainder_used.stderr @@ -0,0 +1,59 @@ +error: use of / has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:10:14 + | +LL | Self(self.0 / rhs.0) + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::integer-division-remainder-used` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::integer_division_remainder_used)]` + +error: use of % has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:17:14 + | +LL | Self(self.0 % rhs.0) + | ^^^^^^^^^^^^^^ + +error: use of / has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:25:13 + | +LL | let c = a / b; + | ^^^^^ + +error: use of % has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:26:13 + | +LL | let d = a % b; + | ^^^^^ + +error: use of / has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:27:13 + | +LL | let e = &a / b; + | ^^^^^^ + +error: use of % has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:28:13 + | +LL | let f = a % &b; + | ^^^^^^ + +error: use of / has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:29:13 + | +LL | let g = &a / &b; + | ^^^^^^^ + +error: use of % has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:30:13 + | +LL | let h = &10 % b; + | ^^^^^^^ + +error: use of / has been disallowed in this context + --> tests/ui/integer_division_remainder_used.rs:31:13 + | +LL | let i = a / &4; + | ^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/iter_nth.fixed b/src/tools/clippy/tests/ui/iter_nth.fixed new file mode 100644 index 00000000000..aff3731a883 --- /dev/null +++ b/src/tools/clippy/tests/ui/iter_nth.fixed @@ -0,0 +1,60 @@ +//@aux-build:option_helpers.rs + +#![warn(clippy::iter_nth)] +#![allow(clippy::useless_vec)] + +#[macro_use] +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; +use std::collections::VecDeque; + +/// Struct to generate false positives for things with `.iter()`. +#[derive(Copy, Clone)] +struct HasIter; + +impl HasIter { + fn iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn iter_mut(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } +} + +/// Checks implementation of `ITER_NTH` lint. +fn iter_nth() { + let mut some_vec = vec![0, 1, 2, 3]; + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect(); + + { + // Make sure we lint `.iter()` for relevant types. + let bad_vec = some_vec.get(3); + let bad_slice = &some_vec[..].get(3); + let bad_boxed_slice = boxed_slice.get(3); + let bad_vec_deque = some_vec_deque.get(3); + } + + { + // Make sure we lint `.iter_mut()` for relevant types. + let bad_vec = some_vec.get_mut(3); + } + { + let bad_slice = &some_vec[..].get_mut(3); + } + { + let bad_vec_deque = some_vec_deque.get_mut(3); + } + + let vec_ref = &Vec::<String>::new(); + vec_ref.get(3); + + // Make sure we don't lint for non-relevant types. + let false_positive = HasIter; + let ok = false_positive.iter().nth(3); + let ok_mut = false_positive.iter_mut().nth(3); +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/iter_nth.rs b/src/tools/clippy/tests/ui/iter_nth.rs index 7c567bb81d8..89d68044ddd 100644 --- a/src/tools/clippy/tests/ui/iter_nth.rs +++ b/src/tools/clippy/tests/ui/iter_nth.rs @@ -48,6 +48,9 @@ fn iter_nth() { let bad_vec_deque = some_vec_deque.iter_mut().nth(3); } + let vec_ref = &Vec::<String>::new(); + vec_ref.iter().nth(3); + // Make sure we don't lint for non-relevant types. let false_positive = HasIter; let ok = false_positive.iter().nth(3); diff --git a/src/tools/clippy/tests/ui/iter_nth.stderr b/src/tools/clippy/tests/ui/iter_nth.stderr index c5dd0c99727..178463f5347 100644 --- a/src/tools/clippy/tests/ui/iter_nth.stderr +++ b/src/tools/clippy/tests/ui/iter_nth.stderr @@ -4,9 +4,12 @@ error: called `.iter().nth()` on a `Vec` LL | let bad_vec = some_vec.iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable = note: `-D clippy::iter-nth` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::iter_nth)]` +help: `get` is equivalent but more concise + | +LL | let bad_vec = some_vec.get(3); + | ~~~ error: called `.iter().nth()` on a slice --> tests/ui/iter_nth.rs:35:26 @@ -14,7 +17,10 @@ error: called `.iter().nth()` on a slice LL | let bad_slice = &some_vec[..].iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable +help: `get` is equivalent but more concise + | +LL | let bad_slice = &some_vec[..].get(3); + | ~~~ error: called `.iter().nth()` on a slice --> tests/ui/iter_nth.rs:36:31 @@ -22,7 +28,10 @@ error: called `.iter().nth()` on a slice LL | let bad_boxed_slice = boxed_slice.iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable +help: `get` is equivalent but more concise + | +LL | let bad_boxed_slice = boxed_slice.get(3); + | ~~~ error: called `.iter().nth()` on a `VecDeque` --> tests/ui/iter_nth.rs:37:29 @@ -30,7 +39,10 @@ error: called `.iter().nth()` on a `VecDeque` LL | let bad_vec_deque = some_vec_deque.iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable +help: `get` is equivalent but more concise + | +LL | let bad_vec_deque = some_vec_deque.get(3); + | ~~~ error: called `.iter_mut().nth()` on a `Vec` --> tests/ui/iter_nth.rs:42:23 @@ -38,7 +50,10 @@ error: called `.iter_mut().nth()` on a `Vec` LL | let bad_vec = some_vec.iter_mut().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get_mut()` is both faster and more readable +help: `get_mut` is equivalent but more concise + | +LL | let bad_vec = some_vec.get_mut(3); + | ~~~~~~~ error: called `.iter_mut().nth()` on a slice --> tests/ui/iter_nth.rs:45:26 @@ -46,7 +61,10 @@ error: called `.iter_mut().nth()` on a slice LL | let bad_slice = &some_vec[..].iter_mut().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get_mut()` is both faster and more readable +help: `get_mut` is equivalent but more concise + | +LL | let bad_slice = &some_vec[..].get_mut(3); + | ~~~~~~~ error: called `.iter_mut().nth()` on a `VecDeque` --> tests/ui/iter_nth.rs:48:29 @@ -54,7 +72,21 @@ error: called `.iter_mut().nth()` on a `VecDeque` LL | let bad_vec_deque = some_vec_deque.iter_mut().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get_mut()` is both faster and more readable +help: `get_mut` is equivalent but more concise + | +LL | let bad_vec_deque = some_vec_deque.get_mut(3); + | ~~~~~~~ + +error: called `.iter().nth()` on a `Vec` + --> tests/ui/iter_nth.rs:52:5 + | +LL | vec_ref.iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: `get` is equivalent but more concise + | +LL | vec_ref.get(3); + | ~~~ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/len_zero.fixed b/src/tools/clippy/tests/ui/len_zero.fixed index 745fc7e1a8b..c16d7a26616 100644 --- a/src/tools/clippy/tests/ui/len_zero.fixed +++ b/src/tools/clippy/tests/ui/len_zero.fixed @@ -1,5 +1,11 @@ #![warn(clippy::len_zero)] -#![allow(dead_code, unused, clippy::needless_if, clippy::len_without_is_empty)] +#![allow( + dead_code, + unused, + clippy::needless_if, + clippy::len_without_is_empty, + clippy::const_is_empty +)] extern crate core; use core::ops::Deref; diff --git a/src/tools/clippy/tests/ui/len_zero.rs b/src/tools/clippy/tests/ui/len_zero.rs index 048ad2f4fd3..5c49a5abf81 100644 --- a/src/tools/clippy/tests/ui/len_zero.rs +++ b/src/tools/clippy/tests/ui/len_zero.rs @@ -1,5 +1,11 @@ #![warn(clippy::len_zero)] -#![allow(dead_code, unused, clippy::needless_if, clippy::len_without_is_empty)] +#![allow( + dead_code, + unused, + clippy::needless_if, + clippy::len_without_is_empty, + clippy::const_is_empty +)] extern crate core; use core::ops::Deref; diff --git a/src/tools/clippy/tests/ui/len_zero.stderr b/src/tools/clippy/tests/ui/len_zero.stderr index b1f04c94de6..dd07a85d62c 100644 --- a/src/tools/clippy/tests/ui/len_zero.stderr +++ b/src/tools/clippy/tests/ui/len_zero.stderr @@ -1,5 +1,5 @@ error: length comparison to zero - --> tests/ui/len_zero.rs:82:8 + --> tests/ui/len_zero.rs:88:8 | LL | if x.len() == 0 { | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()` @@ -8,13 +8,13 @@ LL | if x.len() == 0 { = help: to override `-D warnings` add `#[allow(clippy::len_zero)]` error: length comparison to zero - --> tests/ui/len_zero.rs:86:8 + --> tests/ui/len_zero.rs:92:8 | LL | if "".len() == 0 {} | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:95:20 + --> tests/ui/len_zero.rs:101:20 | LL | println!("{}", *s1 == ""); | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s1.is_empty()` @@ -23,121 +23,121 @@ LL | println!("{}", *s1 == ""); = help: to override `-D warnings` add `#[allow(clippy::comparison_to_empty)]` error: comparison to empty slice - --> tests/ui/len_zero.rs:96:20 + --> tests/ui/len_zero.rs:102:20 | LL | println!("{}", **s2 == ""); | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s2.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:97:20 + --> tests/ui/len_zero.rs:103:20 | LL | println!("{}", ***s3 == ""); | ^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s3.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:98:20 + --> tests/ui/len_zero.rs:104:20 | LL | println!("{}", ****s4 == ""); | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s4.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:99:20 + --> tests/ui/len_zero.rs:105:20 | LL | println!("{}", *****s5 == ""); | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s5.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:100:20 + --> tests/ui/len_zero.rs:106:20 | LL | println!("{}", ******(s6) == ""); | ^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(s6).is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:103:20 + --> tests/ui/len_zero.rs:109:20 | LL | println!("{}", &**d2s == ""); | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:118:8 + --> tests/ui/len_zero.rs:124:8 | LL | if has_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:121:8 + --> tests/ui/len_zero.rs:127:8 | LL | if has_is_empty.len() != 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:124:8 + --> tests/ui/len_zero.rs:130:8 | LL | if has_is_empty.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:127:8 + --> tests/ui/len_zero.rs:133:8 | LL | if has_is_empty.len() < 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:130:8 + --> tests/ui/len_zero.rs:136:8 | LL | if has_is_empty.len() >= 1 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:141:8 + --> tests/ui/len_zero.rs:147:8 | LL | if 0 == has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:144:8 + --> tests/ui/len_zero.rs:150:8 | LL | if 0 != has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:147:8 + --> tests/ui/len_zero.rs:153:8 | LL | if 0 < has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:150:8 + --> tests/ui/len_zero.rs:156:8 | LL | if 1 <= has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:153:8 + --> tests/ui/len_zero.rs:159:8 | LL | if 1 > has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:167:8 + --> tests/ui/len_zero.rs:173:8 | LL | if with_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:179:6 + --> tests/ui/len_zero.rs:185:6 | LL | (has_is_empty.len() > 0).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:180:6 + --> tests/ui/len_zero.rs:186:6 | LL | (has_is_empty.len() == 0).then(|| println!("Or this!")); | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:184:8 + --> tests/ui/len_zero.rs:190:8 | LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` diff --git a/src/tools/clippy/tests/ui/let_if_seq.rs b/src/tools/clippy/tests/ui/let_if_seq.rs index 9869d945299..a29d35880b8 100644 --- a/src/tools/clippy/tests/ui/let_if_seq.rs +++ b/src/tools/clippy/tests/ui/let_if_seq.rs @@ -57,6 +57,17 @@ fn early_return() -> u8 { foo } +fn allow_works() -> i32 { + #[allow(clippy::useless_let_if_seq)] + let x; + if true { + x = 1; + } else { + x = 2; + } + x +} + fn main() { early_return(); issue975(); diff --git a/src/tools/clippy/tests/ui/let_if_seq.stderr b/src/tools/clippy/tests/ui/let_if_seq.stderr index 87ad20dc27e..41930108fb1 100644 --- a/src/tools/clippy/tests/ui/let_if_seq.stderr +++ b/src/tools/clippy/tests/ui/let_if_seq.stderr @@ -1,5 +1,5 @@ error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:66:5 + --> tests/ui/let_if_seq.rs:77:5 | LL | / let mut foo = 0; LL | | @@ -14,7 +14,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::useless_let_if_seq)]` error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:73:5 + --> tests/ui/let_if_seq.rs:84:5 | LL | / let mut bar = 0; LL | | @@ -28,7 +28,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:83:5 + --> tests/ui/let_if_seq.rs:94:5 | LL | / let quz; LL | | @@ -40,7 +40,7 @@ LL | | } | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:113:5 + --> tests/ui/let_if_seq.rs:124:5 | LL | / let mut baz = 0; LL | | diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs index 1fb252e3f97..2b36c3f3c2f 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.rs +++ b/src/tools/clippy/tests/ui/manual_let_else.rs @@ -8,7 +8,8 @@ clippy::never_loop, clippy::needless_if, clippy::diverging_sub_expression, - clippy::single_match + clippy::single_match, + clippy::manual_unwrap_or_default )] #![warn(clippy::manual_let_else)] //@no-rustfix diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr index 7012c6b8891..55a410982ad 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else.stderr @@ -1,5 +1,5 @@ error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:28:5 + --> tests/ui/manual_let_else.rs:29:5 | LL | let v = if let Some(v_some) = g() { v_some } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` @@ -8,7 +8,7 @@ LL | let v = if let Some(v_some) = g() { v_some } else { return }; = help: to override `-D warnings` add `#[allow(clippy::manual_let_else)]` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:31:5 + --> tests/ui/manual_let_else.rs:32:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -26,7 +26,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:38:5 + --> tests/ui/manual_let_else.rs:39:5 | LL | / let v = if let Some(v) = g() { LL | | @@ -47,25 +47,25 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:50:9 + --> tests/ui/manual_let_else.rs:51:9 | LL | let v = if let Some(v_some) = g() { v_some } else { continue }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { continue };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:52:9 + --> tests/ui/manual_let_else.rs:53:9 | LL | let v = if let Some(v_some) = g() { v_some } else { break }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { break };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:57:5 + --> tests/ui/manual_let_else.rs:58:5 | LL | let v = if let Some(v_some) = g() { v_some } else { panic!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { panic!() };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:61:5 + --> tests/ui/manual_let_else.rs:62:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -83,7 +83,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:69:5 + --> tests/ui/manual_let_else.rs:70:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -101,7 +101,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:77:5 + --> tests/ui/manual_let_else.rs:78:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -121,7 +121,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:86:5 + --> tests/ui/manual_let_else.rs:87:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -141,7 +141,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:95:5 + --> tests/ui/manual_let_else.rs:96:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -168,7 +168,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:111:5 + --> tests/ui/manual_let_else.rs:112:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -190,7 +190,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:122:5 + --> tests/ui/manual_let_else.rs:123:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -217,7 +217,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:138:5 + --> tests/ui/manual_let_else.rs:139:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -239,7 +239,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:149:5 + --> tests/ui/manual_let_else.rs:150:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -257,7 +257,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:157:5 + --> tests/ui/manual_let_else.rs:158:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -278,7 +278,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:167:5 + --> tests/ui/manual_let_else.rs:168:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -299,7 +299,7 @@ LL + } }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:177:5 + --> tests/ui/manual_let_else.rs:178:5 | LL | / let v = if let Some(v_some) = g() { LL | | @@ -328,7 +328,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:195:5 + --> tests/ui/manual_let_else.rs:196:5 | LL | / let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) { LL | | @@ -346,7 +346,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:203:5 + --> tests/ui/manual_let_else.rs:204:5 | LL | / let (w, S { v }) = if let (Some(v_some), w_some) = (g().map(|_| S { v: 0 }), 0) { LL | | @@ -364,7 +364,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:213:13 + --> tests/ui/manual_let_else.rs:214:13 | LL | let $n = if let Some(v) = $e { v } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some($n) = g() else { return };` @@ -375,19 +375,19 @@ LL | create_binding_if_some!(w, g()); = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:222:5 + --> tests/ui/manual_let_else.rs:223:5 | LL | let v = if let Variant::A(a, 0) = e() { a } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:226:5 + --> tests/ui/manual_let_else.rs:227:5 | LL | let mut v = if let Variant::B(b) = e() { b } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:231:5 + --> tests/ui/manual_let_else.rs:232:5 | LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested { LL | | @@ -405,19 +405,19 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:238:5 + --> tests/ui/manual_let_else.rs:239:5 | LL | let v = if let Variant::A(.., a) = e() { a } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:242:5 + --> tests/ui/manual_let_else.rs:243:5 | LL | let w = if let (Some(v), ()) = (g(), ()) { v } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let (Some(w), ()) = (g(), ()) else { return };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:246:5 + --> tests/ui/manual_let_else.rs:247:5 | LL | / let w = if let Some(S { v: x }) = Some(S { v: 0 }) { LL | | @@ -435,7 +435,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:254:5 + --> tests/ui/manual_let_else.rs:255:5 | LL | / let v = if let Some(S { v: x }) = Some(S { v: 0 }) { LL | | @@ -453,7 +453,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:262:5 + --> tests/ui/manual_let_else.rs:263:5 | LL | / let (x, S { v }, w) = if let Some(U { v, w, x }) = None::<U<S<()>>> { LL | | @@ -471,7 +471,7 @@ LL + }; | error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:379:5 + --> tests/ui/manual_let_else.rs:380:5 | LL | / let _ = match ff { LL | | @@ -481,7 +481,7 @@ LL | | }; | |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };` error: this could be rewritten as `let...else` - --> tests/ui/manual_let_else.rs:456:9 + --> tests/ui/manual_let_else.rs:457:9 | LL | let v = if let Some(v_some) = g() { v_some } else { return }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };` diff --git a/src/tools/clippy/tests/ui/manual_retain.fixed b/src/tools/clippy/tests/ui/manual_retain.fixed index e359dfbb98c..5540029bf6b 100644 --- a/src/tools/clippy/tests/ui/manual_retain.fixed +++ b/src/tools/clippy/tests/ui/manual_retain.fixed @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::manual_retain)] #![allow(unused, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; diff --git a/src/tools/clippy/tests/ui/manual_retain.rs b/src/tools/clippy/tests/ui/manual_retain.rs index 931814f08b7..cee641d9d65 100644 --- a/src/tools/clippy/tests/ui/manual_retain.rs +++ b/src/tools/clippy/tests/ui/manual_retain.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::manual_retain)] #![allow(unused, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; diff --git a/src/tools/clippy/tests/ui/manual_retain.stderr b/src/tools/clippy/tests/ui/manual_retain.stderr index fdbbc53e4df..c25c804df75 100644 --- a/src/tools/clippy/tests/ui/manual_retain.stderr +++ b/src/tools/clippy/tests/ui/manual_retain.stderr @@ -1,5 +1,5 @@ error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:27:5 + --> tests/ui/manual_retain.rs:25:5 | LL | binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` @@ -8,43 +8,43 @@ LL | binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect(); = help: to override `-D warnings` add `#[allow(clippy::manual_retain)]` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:28:5 + --> tests/ui/manual_retain.rs:26:5 | LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:29:5 + --> tests/ui/manual_retain.rs:27:5 | LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:33:5 + --> tests/ui/manual_retain.rs:31:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:34:5 + --> tests/ui/manual_retain.rs:32:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:64:5 + --> tests/ui/manual_retain.rs:62:5 | LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:65:5 + --> tests/ui/manual_retain.rs:63:5 | LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:66:5 + --> tests/ui/manual_retain.rs:64:5 | LL | / btree_map = btree_map LL | | .into_iter() @@ -53,49 +53,49 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:91:5 + --> tests/ui/manual_retain.rs:89:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:92:5 + --> tests/ui/manual_retain.rs:90:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:93:5 + --> tests/ui/manual_retain.rs:91:5 | LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:97:5 + --> tests/ui/manual_retain.rs:95:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:98:5 + --> tests/ui/manual_retain.rs:96:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:128:5 + --> tests/ui/manual_retain.rs:126:5 | LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:129:5 + --> tests/ui/manual_retain.rs:127:5 | LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:130:5 + --> tests/ui/manual_retain.rs:128:5 | LL | / hash_map = hash_map LL | | .into_iter() @@ -104,133 +104,133 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:154:5 + --> tests/ui/manual_retain.rs:152:5 | LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:155:5 + --> tests/ui/manual_retain.rs:153:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:156:5 + --> tests/ui/manual_retain.rs:154:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:160:5 + --> tests/ui/manual_retain.rs:158:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:161:5 + --> tests/ui/manual_retain.rs:159:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:190:5 + --> tests/ui/manual_retain.rs:188:5 | LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:202:5 + --> tests/ui/manual_retain.rs:200:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:203:5 + --> tests/ui/manual_retain.rs:201:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:204:5 + --> tests/ui/manual_retain.rs:202:5 | LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:208:5 + --> tests/ui/manual_retain.rs:206:5 | LL | tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:209:5 + --> tests/ui/manual_retain.rs:207:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:231:5 + --> tests/ui/manual_retain.rs:229:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:232:5 + --> tests/ui/manual_retain.rs:230:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:233:5 + --> tests/ui/manual_retain.rs:231:5 | LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:290:5 + --> tests/ui/manual_retain.rs:288:5 | LL | vec = vec.into_iter().filter(|(x, y)| *x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:294:5 + --> tests/ui/manual_retain.rs:292:5 | LL | tuples = tuples.into_iter().filter(|(_, n)| *n > 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(_, n)| *n > 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:311:5 + --> tests/ui/manual_retain.rs:309:5 | LL | vec = vec.iter().filter(|&&x| x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:312:5 + --> tests/ui/manual_retain.rs:310:5 | LL | vec = vec.iter().filter(|&&x| x == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:313:5 + --> tests/ui/manual_retain.rs:311:5 | LL | vec = vec.into_iter().filter(|&x| x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:316:5 + --> tests/ui/manual_retain.rs:314:5 | LL | vec = vec.iter().filter(|&x| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:317:5 + --> tests/ui/manual_retain.rs:315:5 | LL | vec = vec.iter().filter(|&x| *x == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:318:5 + --> tests/ui/manual_retain.rs:316:5 | LL | vec = vec.into_iter().filter(|x| *x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed index 737d4c90dca..dffd44b6a7c 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed @@ -1,5 +1,10 @@ #![allow(dead_code)] -#![allow(unused_variables, clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)] +#![allow( + unused_variables, + clippy::unnecessary_wraps, + clippy::unnecessary_literal_unwrap, + clippy::manual_unwrap_or_default +)] fn option_unwrap_or() { // int case diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.rs b/src/tools/clippy/tests/ui/manual_unwrap_or.rs index f59fb87529f..67427132c1a 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs @@ -1,5 +1,10 @@ #![allow(dead_code)] -#![allow(unused_variables, clippy::unnecessary_wraps, clippy::unnecessary_literal_unwrap)] +#![allow( + unused_variables, + clippy::unnecessary_wraps, + clippy::unnecessary_literal_unwrap, + clippy::manual_unwrap_or_default +)] fn option_unwrap_or() { // int case diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr index 511b79881ac..33a099680ce 100644 --- a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr +++ b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr @@ -1,5 +1,5 @@ error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:6:5 + --> tests/ui/manual_unwrap_or.rs:11:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -11,7 +11,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::manual_unwrap_or)]` error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:12:5 + --> tests/ui/manual_unwrap_or.rs:17:5 | LL | / match Some(1) { LL | | None => 42, @@ -20,7 +20,7 @@ LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(42)` error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:18:5 + --> tests/ui/manual_unwrap_or.rs:23:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -29,7 +29,7 @@ LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:25:5 + --> tests/ui/manual_unwrap_or.rs:30:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -50,7 +50,7 @@ LL ~ }); | error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:35:5 + --> tests/ui/manual_unwrap_or.rs:40:5 | LL | / match Some("Bob") { LL | | Some(i) => i, @@ -59,7 +59,7 @@ LL | | }; | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:85:5 + --> tests/ui/manual_unwrap_or.rs:90:5 | LL | / match Ok::<i32, &str>(1) { LL | | Ok(i) => i, @@ -68,7 +68,7 @@ LL | | }; | |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:92:5 + --> tests/ui/manual_unwrap_or.rs:97:5 | LL | / match a { LL | | Ok(i) => i, @@ -77,7 +77,7 @@ LL | | }; | |_____^ help: replace with: `a.unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:98:5 + --> tests/ui/manual_unwrap_or.rs:103:5 | LL | / match Ok(1) as Result<i32, &str> { LL | | Ok(i) => i, @@ -86,7 +86,7 @@ LL | | }; | |_____^ help: replace with: `(Ok(1) as Result<i32, &str>).unwrap_or(42)` error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:111:5 + --> tests/ui/manual_unwrap_or.rs:116:5 | LL | / match s.method() { LL | | Some(i) => i, @@ -95,7 +95,7 @@ LL | | }; | |_____^ help: replace with: `s.method().unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:117:5 + --> tests/ui/manual_unwrap_or.rs:122:5 | LL | / match Ok::<i32, &str>(1) { LL | | Err(_) => 42, @@ -104,7 +104,7 @@ LL | | }; | |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:123:5 + --> tests/ui/manual_unwrap_or.rs:128:5 | LL | / match Ok::<i32, &str>(1) { LL | | Ok(i) => i, @@ -113,7 +113,7 @@ LL | | }; | |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(1 + 42)` error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:130:5 + --> tests/ui/manual_unwrap_or.rs:135:5 | LL | / match Ok::<i32, &str>(1) { LL | | Ok(i) => i, @@ -134,7 +134,7 @@ LL ~ }); | error: this pattern reimplements `Result::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:140:5 + --> tests/ui/manual_unwrap_or.rs:145:5 | LL | / match Ok::<&str, &str>("Bob") { LL | | Ok(i) => i, @@ -143,7 +143,7 @@ LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` error: this pattern reimplements `Option::unwrap_or` - --> tests/ui/manual_unwrap_or.rs:200:17 + --> tests/ui/manual_unwrap_or.rs:205:17 | LL | let _ = match some_macro!() { | _________________^ diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed new file mode 100644 index 00000000000..c8456805ee6 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.fixed @@ -0,0 +1,19 @@ +#![warn(clippy::manual_unwrap_or_default)] +#![allow(clippy::unnecessary_literal_unwrap)] + +fn main() { + let x: Option<Vec<String>> = None; + x.unwrap_or_default(); + + let x: Option<Vec<String>> = None; + x.unwrap_or_default(); + + let x: Option<String> = None; + x.unwrap_or_default(); + + let x: Option<Vec<String>> = None; + x.unwrap_or_default(); + + let x: Option<Vec<String>> = None; + x.unwrap_or_default(); +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs new file mode 100644 index 00000000000..820717be53a --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.rs @@ -0,0 +1,40 @@ +#![warn(clippy::manual_unwrap_or_default)] +#![allow(clippy::unnecessary_literal_unwrap)] + +fn main() { + let x: Option<Vec<String>> = None; + match x { + //~^ ERROR: match can be simplified with `.unwrap_or_default()` + Some(v) => v, + None => Vec::default(), + }; + + let x: Option<Vec<String>> = None; + match x { + //~^ ERROR: match can be simplified with `.unwrap_or_default()` + Some(v) => v, + _ => Vec::default(), + }; + + let x: Option<String> = None; + match x { + //~^ ERROR: match can be simplified with `.unwrap_or_default()` + Some(v) => v, + None => String::new(), + }; + + let x: Option<Vec<String>> = None; + match x { + //~^ ERROR: match can be simplified with `.unwrap_or_default()` + None => Vec::default(), + Some(v) => v, + }; + + let x: Option<Vec<String>> = None; + if let Some(v) = x { + //~^ ERROR: if let can be simplified with `.unwrap_or_default()` + v + } else { + Vec::default() + }; +} diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr new file mode 100644 index 00000000000..f4eb6583588 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_unwrap_or_default.stderr @@ -0,0 +1,56 @@ +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:6:5 + | +LL | / match x { +LL | | +LL | | Some(v) => v, +LL | | None => Vec::default(), +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + | + = note: `-D clippy::manual-unwrap-or-default` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_unwrap_or_default)]` + +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:13:5 + | +LL | / match x { +LL | | +LL | | Some(v) => v, +LL | | _ => Vec::default(), +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:20:5 + | +LL | / match x { +LL | | +LL | | Some(v) => v, +LL | | None => String::new(), +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: match can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:27:5 + | +LL | / match x { +LL | | +LL | | None => Vec::default(), +LL | | Some(v) => v, +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: if let can be simplified with `.unwrap_or_default()` + --> tests/ui/manual_unwrap_or_default.rs:34:5 + | +LL | / if let Some(v) = x { +LL | | +LL | | v +LL | | } else { +LL | | Vec::default() +LL | | }; + | |_____^ help: replace it with: `x.unwrap_or_default()` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/map_clone.fixed b/src/tools/clippy/tests/ui/map_clone.fixed index 395eea69294..e58b6b2f19e 100644 --- a/src/tools/clippy/tests/ui/map_clone.fixed +++ b/src/tools/clippy/tests/ui/map_clone.fixed @@ -6,7 +6,8 @@ clippy::redundant_clone, clippy::redundant_closure, clippy::useless_asref, - clippy::useless_vec + clippy::useless_vec, + clippy::empty_loop )] fn main() { @@ -117,4 +118,17 @@ fn main() { let y = x.as_ref().map(|x| String::clone(x)); let x: Result<String, ()> = Ok(String::new()); let y = x.as_ref().map(|x| String::clone(x)); + + // Issue #12271 + { + // Don't lint these + let x: Option<&u8> = None; + let y = x.map(|x| String::clone(loop {})); + let x: Option<&u8> = None; + let y = x.map(|x| u8::clone(loop {})); + let x: Vec<&u8> = vec![]; + let y = x.into_iter().map(|x| String::clone(loop {})); + let x: Vec<&u8> = vec![]; + let y = x.into_iter().map(|x| u8::clone(loop {})); + } } diff --git a/src/tools/clippy/tests/ui/map_clone.rs b/src/tools/clippy/tests/ui/map_clone.rs index 82a103edf5a..e642e4046f8 100644 --- a/src/tools/clippy/tests/ui/map_clone.rs +++ b/src/tools/clippy/tests/ui/map_clone.rs @@ -6,7 +6,8 @@ clippy::redundant_clone, clippy::redundant_closure, clippy::useless_asref, - clippy::useless_vec + clippy::useless_vec, + clippy::empty_loop )] fn main() { @@ -117,4 +118,17 @@ fn main() { let y = x.as_ref().map(|x| String::clone(x)); let x: Result<String, ()> = Ok(String::new()); let y = x.as_ref().map(|x| String::clone(x)); + + // Issue #12271 + { + // Don't lint these + let x: Option<&u8> = None; + let y = x.map(|x| String::clone(loop {})); + let x: Option<&u8> = None; + let y = x.map(|x| u8::clone(loop {})); + let x: Vec<&u8> = vec![]; + let y = x.into_iter().map(|x| String::clone(loop {})); + let x: Vec<&u8> = vec![]; + let y = x.into_iter().map(|x| u8::clone(loop {})); + } } diff --git a/src/tools/clippy/tests/ui/map_clone.stderr b/src/tools/clippy/tests/ui/map_clone.stderr index 1a26a26a4ca..d9e025de4ab 100644 --- a/src/tools/clippy/tests/ui/map_clone.stderr +++ b/src/tools/clippy/tests/ui/map_clone.stderr @@ -1,5 +1,5 @@ error: you are using an explicit closure for copying elements - --> tests/ui/map_clone.rs:13:22 + --> tests/ui/map_clone.rs:14:22 | LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` @@ -8,85 +8,85 @@ LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect(); = help: to override `-D warnings` add `#[allow(clippy::map_clone)]` error: you are using an explicit closure for cloning elements - --> tests/ui/map_clone.rs:14:26 + --> tests/ui/map_clone.rs:15:26 | LL | let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` error: you are using an explicit closure for copying elements - --> tests/ui/map_clone.rs:15:23 + --> tests/ui/map_clone.rs:16:23 | LL | let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` error: you are using an explicit closure for copying elements - --> tests/ui/map_clone.rs:17:26 + --> tests/ui/map_clone.rs:18:26 | LL | let _: Option<u64> = Some(&16).map(|b| *b); | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` error: you are using an explicit closure for copying elements - --> tests/ui/map_clone.rs:18:25 + --> tests/ui/map_clone.rs:19:25 | LL | let _: Option<u8> = Some(&1).map(|x| x.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` error: you are needlessly cloning iterator elements - --> tests/ui/map_clone.rs:29:29 + --> tests/ui/map_clone.rs:30:29 | LL | let _ = std::env::args().map(|v| v.clone()); | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:68:13 + --> tests/ui/map_clone.rs:69:13 | LL | let y = x.map(|x| String::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:70:13 + --> tests/ui/map_clone.rs:71:13 | LL | let y = x.map(Clone::clone); | ^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:73:13 + --> tests/ui/map_clone.rs:74:13 | LL | let y = x.map(String::clone); | ^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:79:13 + --> tests/ui/map_clone.rs:80:13 | LL | let y = x.map(|x| u32::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:82:13 + --> tests/ui/map_clone.rs:83:13 | LL | let y = x.map(|x| Clone::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:94:13 + --> tests/ui/map_clone.rs:95:13 | LL | let y = x.map(|x| String::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:97:13 + --> tests/ui/map_clone.rs:98:13 | LL | let y = x.map(|x| Clone::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `x.cloned()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:103:13 + --> tests/ui/map_clone.rs:104:13 | LL | let y = x.map(|x| u32::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` error: you are explicitly cloning with `.map()` - --> tests/ui/map_clone.rs:106:13 + --> tests/ui/map_clone.rs:107:13 | LL | let y = x.map(|x| Clone::clone(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `x.copied()` diff --git a/src/tools/clippy/tests/ui/match_result_ok.fixed b/src/tools/clippy/tests/ui/match_result_ok.fixed index 8d7cddc0ad7..76b26f5e438 100644 --- a/src/tools/clippy/tests/ui/match_result_ok.fixed +++ b/src/tools/clippy/tests/ui/match_result_ok.fixed @@ -1,6 +1,6 @@ #![warn(clippy::match_result_ok)] #![allow(dead_code)] -#![allow(clippy::boxed_local, clippy::uninlined_format_args)] +#![allow(clippy::boxed_local, clippy::uninlined_format_args, clippy::manual_unwrap_or_default)] // Checking `if` cases diff --git a/src/tools/clippy/tests/ui/match_result_ok.rs b/src/tools/clippy/tests/ui/match_result_ok.rs index 9a18b813aca..d6f2475ba79 100644 --- a/src/tools/clippy/tests/ui/match_result_ok.rs +++ b/src/tools/clippy/tests/ui/match_result_ok.rs @@ -1,6 +1,6 @@ #![warn(clippy::match_result_ok)] #![allow(dead_code)] -#![allow(clippy::boxed_local, clippy::uninlined_format_args)] +#![allow(clippy::boxed_local, clippy::uninlined_format_args, clippy::manual_unwrap_or_default)] // Checking `if` cases diff --git a/src/tools/clippy/tests/ui/missing_doc.rs b/src/tools/clippy/tests/ui/missing_doc.rs index 9bfad3b96cf..9c936d7fa23 100644 --- a/src/tools/clippy/tests/ui/missing_doc.rs +++ b/src/tools/clippy/tests/ui/missing_doc.rs @@ -1,5 +1,6 @@ //@needs-asm-support //@aux-build: proc_macros.rs +//@aux-build: proc_macro_attr.rs #![warn(clippy::missing_docs_in_private_items)] // When denying at the crate level, be sure to not get random warnings from the @@ -8,6 +9,8 @@ //! Some garbage docs for the crate here #![doc = "More garbage"] +#[macro_use] +extern crate proc_macro_attr; extern crate proc_macros; use proc_macros::with_span; @@ -112,3 +115,12 @@ with_span!(span pub enum FooPm3 { A, B(u32), C { field: u32 }}); with_span!(span pub fn foo_pm() {}); with_span!(span pub static FOO_PM: u32 = 0;); with_span!(span pub const FOO2_PM: u32 = 0;); + +// issue #12197 +// Undocumented field originated inside of spanned proc-macro attribute +/// Some dox for struct. +#[rewrite_struct] +pub struct Test { + /// Dox + a: u8, +} diff --git a/src/tools/clippy/tests/ui/missing_doc.stderr b/src/tools/clippy/tests/ui/missing_doc.stderr index 7e66e2097e9..ef0f96a5b71 100644 --- a/src/tools/clippy/tests/ui/missing_doc.stderr +++ b/src/tools/clippy/tests/ui/missing_doc.stderr @@ -1,5 +1,5 @@ error: missing documentation for a type alias - --> tests/ui/missing_doc.rs:16:1 + --> tests/ui/missing_doc.rs:19:1 | LL | type Typedef = String; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -8,19 +8,19 @@ LL | type Typedef = String; = help: to override `-D warnings` add `#[allow(clippy::missing_docs_in_private_items)]` error: missing documentation for a module - --> tests/ui/missing_doc.rs:19:1 + --> tests/ui/missing_doc.rs:22:1 | LL | mod module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> tests/ui/missing_doc.rs:25:1 + --> tests/ui/missing_doc.rs:28:1 | LL | fn foo3() {} | ^^^^^^^^^^^^ error: missing documentation for an enum - --> tests/ui/missing_doc.rs:39:1 + --> tests/ui/missing_doc.rs:42:1 | LL | / enum Baz { LL | | BazA { a: isize, b: isize }, @@ -29,43 +29,43 @@ LL | | } | |_^ error: missing documentation for a variant - --> tests/ui/missing_doc.rs:40:5 + --> tests/ui/missing_doc.rs:43:5 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> tests/ui/missing_doc.rs:40:12 + --> tests/ui/missing_doc.rs:43:12 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a struct field - --> tests/ui/missing_doc.rs:40:22 + --> tests/ui/missing_doc.rs:43:22 | LL | BazA { a: isize, b: isize }, | ^^^^^^^^ error: missing documentation for a variant - --> tests/ui/missing_doc.rs:41:5 + --> tests/ui/missing_doc.rs:44:5 | LL | BarB, | ^^^^ error: missing documentation for a constant - --> tests/ui/missing_doc.rs:65:1 + --> tests/ui/missing_doc.rs:68:1 | LL | const FOO: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> tests/ui/missing_doc.rs:74:1 + --> tests/ui/missing_doc.rs:77:1 | LL | static BAR: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a module - --> tests/ui/missing_doc.rs:83:1 + --> tests/ui/missing_doc.rs:86:1 | LL | / mod internal_impl { LL | | /// dox @@ -77,13 +77,13 @@ LL | | } | |_^ error: missing documentation for a function - --> tests/ui/missing_doc.rs:88:5 + --> tests/ui/missing_doc.rs:91:5 | LL | fn undocumented3() {} | ^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> tests/ui/missing_doc.rs:94:9 + --> tests/ui/missing_doc.rs:97:9 | LL | fn also_undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style.rs b/src/tools/clippy/tests/ui/mixed_attributes_style.rs index ad93e3019fa..4f89aa8a5e5 100644 --- a/src/tools/clippy/tests/ui/mixed_attributes_style.rs +++ b/src/tools/clippy/tests/ui/mixed_attributes_style.rs @@ -1,4 +1,5 @@ #![warn(clippy::mixed_attributes_style)] +#![allow(clippy::duplicated_attributes)] #[allow(unused)] //~ ERROR: item has both inner and outer attributes fn foo1() { diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style.stderr b/src/tools/clippy/tests/ui/mixed_attributes_style.stderr index d1d5cd3f47f..ed798073cb7 100644 --- a/src/tools/clippy/tests/ui/mixed_attributes_style.stderr +++ b/src/tools/clippy/tests/ui/mixed_attributes_style.stderr @@ -1,5 +1,5 @@ error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:3:1 + --> tests/ui/mixed_attributes_style.rs:4:1 | LL | / #[allow(unused)] LL | | fn foo1() { @@ -10,7 +10,7 @@ LL | | #![allow(unused)] = help: to override `-D warnings` add `#[allow(clippy::mixed_attributes_style)]` error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:17:1 + --> tests/ui/mixed_attributes_style.rs:18:1 | LL | / /// linux LL | | @@ -19,7 +19,7 @@ LL | | //! windows | |_______________^ error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:32:1 + --> tests/ui/mixed_attributes_style.rs:33:1 | LL | / #[allow(unused)] LL | | mod bar { diff --git a/src/tools/clippy/tests/ui/mut_mut.rs b/src/tools/clippy/tests/ui/mut_mut.rs index 288b003405d..4c45bc98026 100644 --- a/src/tools/clippy/tests/ui/mut_mut.rs +++ b/src/tools/clippy/tests/ui/mut_mut.rs @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -//@compile-flags: -Zdeduplicate-diagnostics=yes #![warn(clippy::mut_mut)] #![allow(unused)] @@ -82,3 +81,8 @@ mod issue9035 { fn bar(_: &mut impl Display) {} } + +fn allow_works() { + #[allow(clippy::mut_mut)] + let _ = &mut &mut 1; +} diff --git a/src/tools/clippy/tests/ui/mut_mut.stderr b/src/tools/clippy/tests/ui/mut_mut.stderr index 73f2410a252..42853fdc008 100644 --- a/src/tools/clippy/tests/ui/mut_mut.stderr +++ b/src/tools/clippy/tests/ui/mut_mut.stderr @@ -1,5 +1,5 @@ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:16:11 + --> tests/ui/mut_mut.rs:15:11 | LL | fn fun(x: &mut &mut u32) -> bool { | ^^^^^^^^^^^^^ @@ -8,13 +8,13 @@ LL | fn fun(x: &mut &mut u32) -> bool { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:33:17 + --> tests/ui/mut_mut.rs:32:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:48:25 + --> tests/ui/mut_mut.rs:47:25 | LL | let mut z = inline!(&mut $(&mut 3u32)); | ^ @@ -22,37 +22,37 @@ LL | let mut z = inline!(&mut $(&mut 3u32)); = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: this expression mutably borrows a mutable reference. Consider reborrowing - --> tests/ui/mut_mut.rs:35:21 + --> tests/ui/mut_mut.rs:34:21 | LL | let mut y = &mut x; | ^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:39:32 + --> tests/ui/mut_mut.rs:38:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:39:16 + --> tests/ui/mut_mut.rs:38:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:44:37 + --> tests/ui/mut_mut.rs:43:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:44:16 + --> tests/ui/mut_mut.rs:43:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ error: generally you want to avoid `&mut &mut _` if possible - --> tests/ui/mut_mut.rs:44:21 + --> tests/ui/mut_mut.rs:43:21 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed index 201f8a4c19d..a8176618c1f 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed @@ -1,4 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] +#![allow(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs index b0e5014b74b..f190eb2b76e 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs @@ -1,4 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] +#![allow(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr b/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr index f29d4492540..9f14646c3e5 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr @@ -1,5 +1,5 @@ error: use of bitwise operator instead of lazy operator between booleans - --> tests/ui/needless_bitwise_bool.rs:22:8 + --> tests/ui/needless_bitwise_bool.rs:23:8 | LL | if y & !x { | ^^^^^^ help: try: `y && !x` diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index f9eb39d4938..2575f2449e1 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -316,4 +316,11 @@ fn test_match_as_stmt() { }; } +fn allow_works() -> i32 { + #[allow(clippy::needless_return, clippy::match_single_binding)] + match () { + () => return 42, + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index 4dd2e22ea9f..04f21834d88 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -326,4 +326,11 @@ fn test_match_as_stmt() { }; } +fn allow_works() -> i32 { + #[allow(clippy::needless_return, clippy::match_single_binding)] + match () { + () => return 42, + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/no_effect_replace.rs b/src/tools/clippy/tests/ui/no_effect_replace.rs index 2a940d87fb9..e4fd5caae2a 100644 --- a/src/tools/clippy/tests/ui/no_effect_replace.rs +++ b/src/tools/clippy/tests/ui/no_effect_replace.rs @@ -1,5 +1,3 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::no_effect_replace)] fn main() { diff --git a/src/tools/clippy/tests/ui/no_effect_replace.stderr b/src/tools/clippy/tests/ui/no_effect_replace.stderr index ad2dcd2cc9b..ded86c5c5b8 100644 --- a/src/tools/clippy/tests/ui/no_effect_replace.stderr +++ b/src/tools/clippy/tests/ui/no_effect_replace.stderr @@ -1,5 +1,5 @@ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:6:13 + --> tests/ui/no_effect_replace.rs:4:13 | LL | let _ = "12345".replace('1', "1"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,43 +8,43 @@ LL | let _ = "12345".replace('1', "1"); = help: to override `-D warnings` add `#[allow(clippy::no_effect_replace)]` error: replacing text with itself - --> tests/ui/no_effect_replace.rs:9:13 + --> tests/ui/no_effect_replace.rs:7:13 | LL | let _ = "12345".replace("12", "12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:11:13 + --> tests/ui/no_effect_replace.rs:9:13 | LL | let _ = String::new().replace("12", "12"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:14:13 + --> tests/ui/no_effect_replace.rs:12:13 | LL | let _ = "12345".replacen('1', "1", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:16:13 + --> tests/ui/no_effect_replace.rs:14:13 | LL | let _ = "12345".replacen("12", "12", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:18:13 + --> tests/ui/no_effect_replace.rs:16:13 | LL | let _ = String::new().replacen("12", "12", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:25:13 + --> tests/ui/no_effect_replace.rs:23:13 | LL | let _ = "hello".replace(&x.f(), &x.f()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: replacing text with itself - --> tests/ui/no_effect_replace.rs:29:13 + --> tests/ui/no_effect_replace.rs:27:13 | LL | let _ = "hello".replace(&y(), &y()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed index d443334bb05..eeab801b7da 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.fixed +++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed @@ -3,7 +3,8 @@ clippy::ref_option_ref, clippy::equatable_if_let, clippy::let_unit_value, - clippy::redundant_locals + clippy::redundant_locals, + clippy::manual_unwrap_or_default )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs index 317c35bf842..3e5b96d7c31 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.rs +++ b/src/tools/clippy/tests/ui/option_if_let_else.rs @@ -3,7 +3,8 @@ clippy::ref_option_ref, clippy::equatable_if_let, clippy::let_unit_value, - clippy::redundant_locals + clippy::redundant_locals, + clippy::manual_unwrap_or_default )] fn bad1(string: Option<&str>) -> (bool, &str) { diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr index a794dca762f..f5359a0c34f 100644 --- a/src/tools/clippy/tests/ui/option_if_let_else.stderr +++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:10:5 + --> tests/ui/option_if_let_else.rs:11:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -12,19 +12,19 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::option_if_let_else)]` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:28:13 + --> tests/ui/option_if_let_else.rs:29:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:29:13 + --> tests/ui/option_if_let_else.rs:30:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:30:13 + --> tests/ui/option_if_let_else.rs:31:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -44,13 +44,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:36:13 + --> tests/ui/option_if_let_else.rs:37:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:37:13 + --> tests/ui/option_if_let_else.rs:38:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -70,7 +70,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:43:13 + --> tests/ui/option_if_let_else.rs:44:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -90,7 +90,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:52:5 + --> tests/ui/option_if_let_else.rs:53:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -109,7 +109,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:65:13 + --> tests/ui/option_if_let_else.rs:66:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -121,7 +121,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(side_effect, |x| x)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:74:13 + --> tests/ui/option_if_let_else.rs:75:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -144,7 +144,7 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:107:13 + --> tests/ui/option_if_let_else.rs:108:13 | LL | / if let Some(idx) = s.find('.') { LL | | vec![s[..idx].to_string(), s[idx..].to_string()] @@ -154,7 +154,7 @@ LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:118:5 + --> tests/ui/option_if_let_else.rs:119:5 | LL | / if let Ok(binding) = variable { LL | | println!("Ok {binding}"); @@ -177,13 +177,13 @@ LL + }) | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:142:13 + --> tests/ui/option_if_let_else.rs:143:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:152:13 + --> tests/ui/option_if_let_else.rs:153:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -205,13 +205,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:180:13 + --> tests/ui/option_if_let_else.rs:181:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:184:13 + --> tests/ui/option_if_let_else.rs:185:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -231,7 +231,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:223:13 + --> tests/ui/option_if_let_else.rs:224:13 | LL | let _ = match s { | _____________^ @@ -241,7 +241,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:227:13 + --> tests/ui/option_if_let_else.rs:228:13 | LL | let _ = match Some(10) { | _____________^ @@ -251,7 +251,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:233:13 + --> tests/ui/option_if_let_else.rs:234:13 | LL | let _ = match res { | _____________^ @@ -261,7 +261,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:237:13 + --> tests/ui/option_if_let_else.rs:238:13 | LL | let _ = match res { | _____________^ @@ -271,13 +271,13 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:241:13 + --> tests/ui/option_if_let_else.rs:242:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:258:17 + --> tests/ui/option_if_let_else.rs:259:17 | LL | let _ = match initial { | _________________^ @@ -287,7 +287,7 @@ LL | | }; | |_________^ help: try: `initial.as_ref().map_or(42, |value| do_something(value))` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:265:17 + --> tests/ui/option_if_let_else.rs:266:17 | LL | let _ = match initial { | _________________^ @@ -297,7 +297,7 @@ LL | | }; | |_________^ help: try: `initial.as_mut().map_or(42, |value| do_something2(value))` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:288:24 + --> tests/ui/option_if_let_else.rs:289:24 | LL | let mut _hashmap = if let Some(hm) = &opt { | ________________________^ @@ -308,7 +308,7 @@ LL | | }; | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:294:19 + --> tests/ui/option_if_let_else.rs:295:19 | LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` diff --git a/src/tools/clippy/tests/ui/option_option.rs b/src/tools/clippy/tests/ui/option_option.rs index 2f6e4d76145..42f03aae7bb 100644 --- a/src/tools/clippy/tests/ui/option_option.rs +++ b/src/tools/clippy/tests/ui/option_option.rs @@ -1,7 +1,5 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![deny(clippy::option_option)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::manual_unwrap_or_default)] const C: Option<Option<i32>> = None; //~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if diff --git a/src/tools/clippy/tests/ui/option_option.stderr b/src/tools/clippy/tests/ui/option_option.stderr index 76cb9ae944c..0cd048e400e 100644 --- a/src/tools/clippy/tests/ui/option_option.stderr +++ b/src/tools/clippy/tests/ui/option_option.stderr @@ -1,77 +1,77 @@ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:6:10 + --> tests/ui/option_option.rs:4:10 | LL | const C: Option<Option<i32>> = None; | ^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> tests/ui/option_option.rs:3:9 + --> tests/ui/option_option.rs:1:9 | LL | #![deny(clippy::option_option)] | ^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:8:11 + --> tests/ui/option_option.rs:6:11 | LL | static S: Option<Option<i32>> = None; | ^^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:11:13 + --> tests/ui/option_option.rs:9:13 | LL | fn input(_: Option<Option<u8>>) {} | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:14:16 + --> tests/ui/option_option.rs:12:16 | LL | fn output() -> Option<Option<u8>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:19:27 + --> tests/ui/option_option.rs:17:27 | LL | fn output_nested() -> Vec<Option<Option<u8>>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:25:30 + --> tests/ui/option_option.rs:23:30 | LL | fn output_nested_nested() -> Option<Option<Option<u8>>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:31:8 + --> tests/ui/option_option.rs:29:8 | LL | x: Option<Option<u8>>, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:36:23 + --> tests/ui/option_option.rs:34:23 | LL | fn struct_fn() -> Option<Option<u8>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:43:22 + --> tests/ui/option_option.rs:41:22 | LL | fn trait_fn() -> Option<Option<u8>>; | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:48:11 + --> tests/ui/option_option.rs:46:11 | LL | Tuple(Option<Option<u8>>), | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:50:17 + --> tests/ui/option_option.rs:48:17 | LL | Struct { x: Option<Option<u8>> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases - --> tests/ui/option_option.rs:92:14 + --> tests/ui/option_option.rs:90:14 | LL | foo: Option<Option<Cow<'a, str>>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/read_zero_byte_vec.rs b/src/tools/clippy/tests/ui/read_zero_byte_vec.rs index fd5a88a37a6..68acf433469 100644 --- a/src/tools/clippy/tests/ui/read_zero_byte_vec.rs +++ b/src/tools/clippy/tests/ui/read_zero_byte_vec.rs @@ -112,4 +112,10 @@ async fn test_tokio<R: TokioAsyncRead + Unpin>(r: &mut R) { //~^ ERROR: reading zero byte data to `Vec` } +fn allow_works<F: std::io::Read>(mut f: F) { + let mut data = Vec::with_capacity(100); + #[allow(clippy::read_zero_byte_vec)] + f.read(&mut data).unwrap(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_as_str.fixed b/src/tools/clippy/tests/ui/redundant_as_str.fixed index 4185b402226..708a1cc9150 100644 --- a/src/tools/clippy/tests/ui/redundant_as_str.fixed +++ b/src/tools/clippy/tests/ui/redundant_as_str.fixed @@ -1,4 +1,5 @@ #![warn(clippy::redundant_as_str)] +#![allow(clippy::const_is_empty)] fn main() { let string = "Hello, world!".to_owned(); diff --git a/src/tools/clippy/tests/ui/redundant_as_str.rs b/src/tools/clippy/tests/ui/redundant_as_str.rs index 7a74d8a55de..257af591cef 100644 --- a/src/tools/clippy/tests/ui/redundant_as_str.rs +++ b/src/tools/clippy/tests/ui/redundant_as_str.rs @@ -1,4 +1,5 @@ #![warn(clippy::redundant_as_str)] +#![allow(clippy::const_is_empty)] fn main() { let string = "Hello, world!".to_owned(); diff --git a/src/tools/clippy/tests/ui/redundant_as_str.stderr b/src/tools/clippy/tests/ui/redundant_as_str.stderr index f086de5fede..f5379d701db 100644 --- a/src/tools/clippy/tests/ui/redundant_as_str.stderr +++ b/src/tools/clippy/tests/ui/redundant_as_str.stderr @@ -1,5 +1,5 @@ error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too - --> tests/ui/redundant_as_str.rs:7:29 + --> tests/ui/redundant_as_str.rs:8:29 | LL | let _redundant = string.as_str().as_bytes(); | ^^^^^^^^^^^^^^^^^ help: try: `as_bytes` @@ -8,7 +8,7 @@ LL | let _redundant = string.as_str().as_bytes(); = help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]` error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too - --> tests/ui/redundant_as_str.rs:8:29 + --> tests/ui/redundant_as_str.rs:9:29 | LL | let _redundant = string.as_str().is_empty(); | ^^^^^^^^^^^^^^^^^ help: try: `is_empty` diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index f4ff0f0b88b..24d0f797542 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -2,6 +2,7 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. +#![allow(clippy::duplicated_attributes)] #![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 0df1098f5fb..be8da2fa1a3 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -2,6 +2,7 @@ // Use that command to update this file and do not edit by hand. // Manual edits will be overwritten. +#![allow(clippy::duplicated_attributes)] #![allow(clippy::almost_complete_range)] #![allow(clippy::disallowed_names)] #![allow(clippy::blocks_in_conditions)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index e6659b109e5..777ac20153d 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> tests/ui/rename.rs:55:9 + --> tests/ui/rename.rs:56:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -8,343 +8,343 @@ LL | #![warn(clippy::almost_complete_letter_range)] = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> tests/ui/rename.rs:56:9 + --> tests/ui/rename.rs:57:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:57:9 + --> tests/ui/rename.rs:58:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:58:9 + --> tests/ui/rename.rs:59:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions` - --> tests/ui/rename.rs:59:9 + --> tests/ui/rename.rs:60:9 | LL | #![warn(clippy::blocks_in_if_conditions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> tests/ui/rename.rs:60:9 + --> tests/ui/rename.rs:61:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> tests/ui/rename.rs:61:9 + --> tests/ui/rename.rs:62:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> tests/ui/rename.rs:62:9 + --> tests/ui/rename.rs:63:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> tests/ui/rename.rs:63:9 + --> tests/ui/rename.rs:64:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> tests/ui/rename.rs:64:9 + --> tests/ui/rename.rs:65:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> tests/ui/rename.rs:65:9 + --> tests/ui/rename.rs:66:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> tests/ui/rename.rs:66:9 + --> tests/ui/rename.rs:67:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> tests/ui/rename.rs:67:9 + --> tests/ui/rename.rs:68:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> tests/ui/rename.rs:68:9 + --> tests/ui/rename.rs:69:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl` - --> tests/ui/rename.rs:69:9 + --> tests/ui/rename.rs:70:9 | LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl` error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl` - --> tests/ui/rename.rs:70:9 + --> tests/ui/rename.rs:71:9 | LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> tests/ui/rename.rs:71:9 + --> tests/ui/rename.rs:72:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> tests/ui/rename.rs:72:9 + --> tests/ui/rename.rs:73:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> tests/ui/rename.rs:73:9 + --> tests/ui/rename.rs:74:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> tests/ui/rename.rs:74:9 + --> tests/ui/rename.rs:75:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:75:9 + --> tests/ui/rename.rs:76:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:76:9 + --> tests/ui/rename.rs:77:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:77:9 + --> tests/ui/rename.rs:78:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:78:9 + --> tests/ui/rename.rs:79:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> tests/ui/rename.rs:79:9 + --> tests/ui/rename.rs:80:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> tests/ui/rename.rs:80:9 + --> tests/ui/rename.rs:81:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> tests/ui/rename.rs:81:9 + --> tests/ui/rename.rs:82:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> tests/ui/rename.rs:82:9 + --> tests/ui/rename.rs:83:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> tests/ui/rename.rs:83:9 + --> tests/ui/rename.rs:84:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> tests/ui/rename.rs:84:9 + --> tests/ui/rename.rs:85:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> tests/ui/rename.rs:85:9 + --> tests/ui/rename.rs:86:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default` - --> tests/ui/rename.rs:86:9 + --> tests/ui/rename.rs:87:9 | LL | #![warn(clippy::unwrap_or_else_default)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> tests/ui/rename.rs:87:9 + --> tests/ui/rename.rs:88:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting` - --> tests/ui/rename.rs:88:9 + --> tests/ui/rename.rs:89:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> tests/ui/rename.rs:89:9 + --> tests/ui/rename.rs:90:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` - --> tests/ui/rename.rs:90:9 + --> tests/ui/rename.rs:91:9 | LL | #![warn(clippy::cmp_nan)] | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> tests/ui/rename.rs:91:9 + --> tests/ui/rename.rs:92:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> tests/ui/rename.rs:92:9 + --> tests/ui/rename.rs:93:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> tests/ui/rename.rs:93:9 + --> tests/ui/rename.rs:94:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks` - --> tests/ui/rename.rs:94:9 + --> tests/ui/rename.rs:95:9 | LL | #![warn(clippy::fn_null_check)] | ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:95:9 + --> tests/ui/rename.rs:96:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:96:9 + --> tests/ui/rename.rs:97:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> tests/ui/rename.rs:97:9 + --> tests/ui/rename.rs:98:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> tests/ui/rename.rs:98:9 + --> tests/ui/rename.rs:99:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> tests/ui/rename.rs:99:9 + --> tests/ui/rename.rs:100:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> tests/ui/rename.rs:100:9 + --> tests/ui/rename.rs:101:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> tests/ui/rename.rs:101:9 + --> tests/ui/rename.rs:102:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> tests/ui/rename.rs:102:9 + --> tests/ui/rename.rs:103:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> tests/ui/rename.rs:103:9 + --> tests/ui/rename.rs:104:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> tests/ui/rename.rs:104:9 + --> tests/ui/rename.rs:105:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> tests/ui/rename.rs:105:9 + --> tests/ui/rename.rs:106:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> tests/ui/rename.rs:106:9 + --> tests/ui/rename.rs:107:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> tests/ui/rename.rs:107:9 + --> tests/ui/rename.rs:108:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> tests/ui/rename.rs:108:9 + --> tests/ui/rename.rs:109:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` - --> tests/ui/rename.rs:109:9 + --> tests/ui/rename.rs:110:9 | LL | #![warn(clippy::undropped_manually_drops)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> tests/ui/rename.rs:110:9 + --> tests/ui/rename.rs:111:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> tests/ui/rename.rs:111:9 + --> tests/ui/rename.rs:112:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons` - --> tests/ui/rename.rs:112:9 + --> tests/ui/rename.rs:113:9 | LL | #![warn(clippy::vtable_address_comparisons)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons` diff --git a/src/tools/clippy/tests/ui/single_match.fixed b/src/tools/clippy/tests/ui/single_match.fixed index 6df64eb4053..acd70416d8b 100644 --- a/src/tools/clippy/tests/ui/single_match.fixed +++ b/src/tools/clippy/tests/ui/single_match.fixed @@ -1,12 +1,11 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::single_match)] #![allow( unused, clippy::uninlined_format_args, clippy::needless_if, clippy::redundant_guards, - clippy::redundant_pattern_matching + clippy::redundant_pattern_matching, + clippy::manual_unwrap_or_default )] fn dummy() {} diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs index 4f005f4e04f..bde78199810 100644 --- a/src/tools/clippy/tests/ui/single_match.rs +++ b/src/tools/clippy/tests/ui/single_match.rs @@ -1,12 +1,11 @@ -//@compile-flags: -Zdeduplicate-diagnostics=yes - #![warn(clippy::single_match)] #![allow( unused, clippy::uninlined_format_args, clippy::needless_if, clippy::redundant_guards, - clippy::redundant_pattern_matching + clippy::redundant_pattern_matching, + clippy::manual_unwrap_or_default )] fn dummy() {} diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr index 651d0b4911d..a249c120ee4 100644 --- a/src/tools/clippy/tests/ui/single_match.stderr +++ b/src/tools/clippy/tests/ui/single_match.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:16:5 + --> tests/ui/single_match.rs:15:5 | LL | / match x { LL | | Some(y) => { @@ -19,7 +19,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:24:5 + --> tests/ui/single_match.rs:23:5 | LL | / match x { LL | | // Note the missing block braces. @@ -31,7 +31,7 @@ LL | | } | |_____^ help: try: `if let Some(y) = x { println!("{:?}", y) }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:33:5 + --> tests/ui/single_match.rs:32:5 | LL | / match z { LL | | (2..=3, 7..=9) => dummy(), @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try: `if let (2..=3, 7..=9) = z { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:62:5 + --> tests/ui/single_match.rs:61:5 | LL | / match x { LL | | Some(y) => dummy(), @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try: `if let Some(y) = x { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:67:5 + --> tests/ui/single_match.rs:66:5 | LL | / match y { LL | | Ok(y) => dummy(), @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try: `if let Ok(y) = y { dummy() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:74:5 + --> tests/ui/single_match.rs:73:5 | LL | / match c { LL | | Cow::Borrowed(..) => dummy(), @@ -67,7 +67,7 @@ LL | | }; | |_____^ help: try: `if let Cow::Borrowed(..) = c { dummy() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:95:5 + --> tests/ui/single_match.rs:94:5 | LL | / match x { LL | | "test" => println!(), @@ -76,7 +76,7 @@ LL | | } | |_____^ help: try: `if x == "test" { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:108:5 + --> tests/ui/single_match.rs:107:5 | LL | / match x { LL | | Foo::A => println!(), @@ -85,7 +85,7 @@ LL | | } | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:114:5 + --> tests/ui/single_match.rs:113:5 | LL | / match x { LL | | FOO_C => println!(), @@ -94,7 +94,7 @@ LL | | } | |_____^ help: try: `if x == FOO_C { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:119:5 + --> tests/ui/single_match.rs:118:5 | LL | / match &&x { LL | | Foo::A => println!(), @@ -103,7 +103,7 @@ LL | | } | |_____^ help: try: `if x == Foo::A { println!() }` error: you seem to be trying to use `match` for an equality check. Consider using `if` - --> tests/ui/single_match.rs:125:5 + --> tests/ui/single_match.rs:124:5 | LL | / match &x { LL | | Foo::A => println!(), @@ -112,7 +112,7 @@ LL | | } | |_____^ help: try: `if x == &Foo::A { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:142:5 + --> tests/ui/single_match.rs:141:5 | LL | / match x { LL | | Bar::A => println!(), @@ -121,7 +121,7 @@ LL | | } | |_____^ help: try: `if let Bar::A = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:150:5 + --> tests/ui/single_match.rs:149:5 | LL | / match x { LL | | None => println!(), @@ -130,7 +130,7 @@ LL | | }; | |_____^ help: try: `if let None = x { println!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:172:5 + --> tests/ui/single_match.rs:171:5 | LL | / match x { LL | | (Some(_), _) => {}, @@ -139,7 +139,7 @@ LL | | } | |_____^ help: try: `if let (Some(_), _) = x {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:178:5 + --> tests/ui/single_match.rs:177:5 | LL | / match x { LL | | (Some(E::V), _) => todo!(), @@ -148,7 +148,7 @@ LL | | } | |_____^ help: try: `if let (Some(E::V), _) = x { todo!() }` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:184:5 + --> tests/ui/single_match.rs:183:5 | LL | / match (Some(42), Some(E::V), Some(42)) { LL | | (.., Some(E::V), _) => {}, @@ -157,7 +157,7 @@ LL | | } | |_____^ help: try: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:256:5 + --> tests/ui/single_match.rs:255:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -177,7 +177,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match.rs:264:5 + --> tests/ui/single_match.rs:263:5 | LL | / match bar { LL | | #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/single_match_else.fixed b/src/tools/clippy/tests/ui/single_match_else.fixed index 2970f5485fa..e840adf0fa3 100644 --- a/src/tools/clippy/tests/ui/single_match_else.fixed +++ b/src/tools/clippy/tests/ui/single_match_else.fixed @@ -1,5 +1,4 @@ //@aux-build: proc_macros.rs -//@compile-flags: -Zdeduplicate-diagnostics=yes #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs index 26974b2a48a..430c4da20f1 100644 --- a/src/tools/clippy/tests/ui/single_match_else.rs +++ b/src/tools/clippy/tests/ui/single_match_else.rs @@ -1,5 +1,4 @@ //@aux-build: proc_macros.rs -//@compile-flags: -Zdeduplicate-diagnostics=yes #![warn(clippy::single_match_else)] #![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr index 48c74c0caea..f8f88379d6d 100644 --- a/src/tools/clippy/tests/ui/single_match_else.stderr +++ b/src/tools/clippy/tests/ui/single_match_else.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:18:13 + --> tests/ui/single_match_else.rs:17:13 | LL | let _ = match ExprNode::Butterflies { | _____________^ @@ -22,7 +22,7 @@ LL ~ }; | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:83:5 + --> tests/ui/single_match_else.rs:82:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -42,7 +42,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:92:5 + --> tests/ui/single_match_else.rs:91:5 | LL | / match Some(1) { LL | | Some(a) => println!("${:?}", a), @@ -62,7 +62,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:102:5 + --> tests/ui/single_match_else.rs:101:5 | LL | / match Result::<i32, Infallible>::Ok(1) { LL | | Ok(a) => println!("${:?}", a), @@ -82,7 +82,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:111:5 + --> tests/ui/single_match_else.rs:110:5 | LL | / match Cow::from("moo") { LL | | Cow::Owned(a) => println!("${:?}", a), @@ -102,7 +102,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:121:5 + --> tests/ui/single_match_else.rs:120:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -125,7 +125,7 @@ LL + } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:132:5 + --> tests/ui/single_match_else.rs:131:5 | LL | / match bar { LL | | Some(v) => { @@ -149,7 +149,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:144:5 + --> tests/ui/single_match_else.rs:143:5 | LL | / match bar { LL | | Some(v) => unsafe { @@ -173,7 +173,7 @@ LL + } } | error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> tests/ui/single_match_else.rs:156:5 + --> tests/ui/single_match_else.rs:155:5 | LL | / match bar { LL | | #[rustfmt::skip] diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index 0a734a65d29..ec4ae2ea13c 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -17,7 +17,7 @@ fn std_instead_of_core() { use ::core::hash::Hash; //~^ ERROR: used import from `std` instead of `core` // Don't lint on `env` macro - use core::env; + use std::env; // Multiple imports use core::fmt::{Debug, Result}; diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr index ee42b474a32..8f920511cc5 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -14,12 +14,6 @@ LL | use ::std::hash::Hash; | ^^^ help: consider importing the item from `core`: `core` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core.rs:20:9 - | -LL | use std::env; - | ^^^ help: consider importing the item from `core`: `core` - -error: used import from `std` instead of `core` --> tests/ui/std_instead_of_core.rs:23:9 | LL | use std::fmt::{Debug, Result}; @@ -85,5 +79,5 @@ LL | use alloc::slice::from_ref; = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]` -error: aborting due to 13 previous errors +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/unconditional_recursion.rs b/src/tools/clippy/tests/ui/unconditional_recursion.rs index 35275e81ded..70b390b00e2 100644 --- a/src/tools/clippy/tests/ui/unconditional_recursion.rs +++ b/src/tools/clippy/tests/ui/unconditional_recursion.rs @@ -1,7 +1,11 @@ //@no-rustfix #![warn(clippy::unconditional_recursion)] -#![allow(clippy::partialeq_ne_impl, clippy::default_constructed_unit_structs)] +#![allow( + clippy::partialeq_ne_impl, + clippy::default_constructed_unit_structs, + clippy::only_used_in_recursion +)] enum Foo { A, @@ -350,4 +354,48 @@ mod issue12154 { } } +// From::from -> Into::into -> From::from +struct BadFromTy1<'a>(&'a ()); +struct BadIntoTy1<'b>(&'b ()); +impl<'a> From<BadFromTy1<'a>> for BadIntoTy1<'static> { + fn from(f: BadFromTy1<'a>) -> Self { + f.into() + } +} + +// Using UFCS syntax +struct BadFromTy2<'a>(&'a ()); +struct BadIntoTy2<'b>(&'b ()); +impl<'a> From<BadFromTy2<'a>> for BadIntoTy2<'static> { + fn from(f: BadFromTy2<'a>) -> Self { + Into::into(f) + } +} + +// Different Into impl (<i16 as Into<i32>>), so no infinite recursion +struct BadFromTy3; +impl From<BadFromTy3> for i32 { + fn from(f: BadFromTy3) -> Self { + Into::into(1i16) + } +} + +// A conditional return that ends the recursion +struct BadFromTy4; +impl From<BadFromTy4> for i32 { + fn from(f: BadFromTy4) -> Self { + if true { + return 42; + } + f.into() + } +} + +// Types differ in refs, don't lint +impl From<&BadFromTy4> for i32 { + fn from(f: &BadFromTy4) -> Self { + BadFromTy4.into() + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unconditional_recursion.stderr b/src/tools/clippy/tests/ui/unconditional_recursion.stderr index 3fd6c91000e..03c27bd8ed8 100644 --- a/src/tools/clippy/tests/ui/unconditional_recursion.stderr +++ b/src/tools/clippy/tests/ui/unconditional_recursion.stderr @@ -1,5 +1,5 @@ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:42:5 + --> tests/ui/unconditional_recursion.rs:46:5 | LL | fn ne(&self, other: &Self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -12,7 +12,7 @@ LL | self.ne(other) = help: to override `-D warnings` add `#[allow(unconditional_recursion)]` error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:46:5 + --> tests/ui/unconditional_recursion.rs:50:5 | LL | fn eq(&self, other: &Self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -23,7 +23,7 @@ LL | self.eq(other) = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:211:5 + --> tests/ui/unconditional_recursion.rs:215:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -34,7 +34,7 @@ LL | self.to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:221:5 + --> tests/ui/unconditional_recursion.rs:225:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -45,7 +45,7 @@ LL | x.to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:232:5 + --> tests/ui/unconditional_recursion.rs:236:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -56,7 +56,7 @@ LL | (self as &Self).to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:12:5 + --> tests/ui/unconditional_recursion.rs:16:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -65,7 +65,7 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:14:9 + --> tests/ui/unconditional_recursion.rs:18:9 | LL | self != other | ^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | self != other = help: to override `-D warnings` add `#[allow(clippy::unconditional_recursion)]` error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:16:5 + --> tests/ui/unconditional_recursion.rs:20:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -82,13 +82,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:18:9 + --> tests/ui/unconditional_recursion.rs:22:9 | LL | self == other | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:28:5 + --> tests/ui/unconditional_recursion.rs:32:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | self != &Foo2::B // no error here @@ -96,13 +96,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:29:9 + --> tests/ui/unconditional_recursion.rs:33:9 | LL | self != &Foo2::B // no error here | ^^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:31:5 + --> tests/ui/unconditional_recursion.rs:35:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | self == &Foo2::B // no error here @@ -110,13 +110,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:32:9 + --> tests/ui/unconditional_recursion.rs:36:9 | LL | self == &Foo2::B // no error here | ^^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:42:5 + --> tests/ui/unconditional_recursion.rs:46:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -125,27 +125,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:44:9 + --> tests/ui/unconditional_recursion.rs:48:9 | LL | self.ne(other) | ^^^^^^^^^^^^^^ -error: parameter is only used in recursion - --> tests/ui/unconditional_recursion.rs:42:18 - | -LL | fn ne(&self, other: &Self) -> bool { - | ^^^^^ help: if this is intentional, prefix it with an underscore: `_other` - | -note: parameter used here - --> tests/ui/unconditional_recursion.rs:44:17 - | -LL | self.ne(other) - | ^^^^^ - = note: `-D clippy::only-used-in-recursion` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::only_used_in_recursion)]` - error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:46:5 + --> tests/ui/unconditional_recursion.rs:50:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -154,25 +140,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:48:9 + --> tests/ui/unconditional_recursion.rs:52:9 | LL | self.eq(other) | ^^^^^^^^^^^^^^ -error: parameter is only used in recursion - --> tests/ui/unconditional_recursion.rs:46:18 - | -LL | fn eq(&self, other: &Self) -> bool { - | ^^^^^ help: if this is intentional, prefix it with an underscore: `_other` - | -note: parameter used here - --> tests/ui/unconditional_recursion.rs:48:17 - | -LL | self.eq(other) - | ^^^^^ - error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:90:5 + --> tests/ui/unconditional_recursion.rs:94:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -181,13 +155,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:92:9 + --> tests/ui/unconditional_recursion.rs:96:9 | LL | other != self | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:94:5 + --> tests/ui/unconditional_recursion.rs:98:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -196,13 +170,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:96:9 + --> tests/ui/unconditional_recursion.rs:100:9 | LL | other == self | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:104:5 + --> tests/ui/unconditional_recursion.rs:108:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -211,13 +185,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:106:9 + --> tests/ui/unconditional_recursion.rs:110:9 | LL | other != other | ^^^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> tests/ui/unconditional_recursion.rs:106:9 + --> tests/ui/unconditional_recursion.rs:110:9 | LL | other != other | ^^^^^^^^^^^^^^ @@ -225,7 +199,7 @@ LL | other != other = note: `#[deny(clippy::eq_op)]` on by default error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:108:5 + --> tests/ui/unconditional_recursion.rs:112:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -234,19 +208,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:110:9 + --> tests/ui/unconditional_recursion.rs:114:9 | LL | other == other | ^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> tests/ui/unconditional_recursion.rs:110:9 + --> tests/ui/unconditional_recursion.rs:114:9 | LL | other == other | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:117:5 + --> tests/ui/unconditional_recursion.rs:121:5 | LL | / fn ne(&self, _other: &Self) -> bool { LL | | @@ -255,19 +229,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:119:9 + --> tests/ui/unconditional_recursion.rs:123:9 | LL | self != self | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> tests/ui/unconditional_recursion.rs:119:9 + --> tests/ui/unconditional_recursion.rs:123:9 | LL | self != self | ^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:121:5 + --> tests/ui/unconditional_recursion.rs:125:5 | LL | / fn eq(&self, _other: &Self) -> bool { LL | | @@ -276,19 +250,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:123:9 + --> tests/ui/unconditional_recursion.rs:127:9 | LL | self == self | ^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> tests/ui/unconditional_recursion.rs:123:9 + --> tests/ui/unconditional_recursion.rs:127:9 | LL | self == self | ^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:149:13 + --> tests/ui/unconditional_recursion.rs:153:13 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -300,7 +274,7 @@ LL | impl_partial_eq!(S5); | -------------------- in this macro invocation | note: recursive call site - --> tests/ui/unconditional_recursion.rs:151:17 + --> tests/ui/unconditional_recursion.rs:155:17 | LL | self == other | ^^^^^^^^^^^^^ @@ -310,7 +284,7 @@ LL | impl_partial_eq!(S5); = note: this error originates in the macro `impl_partial_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:178:5 + --> tests/ui/unconditional_recursion.rs:182:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -321,13 +295,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:182:9 + --> tests/ui/unconditional_recursion.rs:186:9 | LL | mine == theirs | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:247:5 + --> tests/ui/unconditional_recursion.rs:251:5 | LL | / fn new() -> Self { LL | | @@ -336,13 +310,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:249:9 + --> tests/ui/unconditional_recursion.rs:253:9 | LL | Self::default() | ^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:286:5 + --> tests/ui/unconditional_recursion.rs:290:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -353,10 +327,38 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:290:9 + --> tests/ui/unconditional_recursion.rs:294:9 | LL | mine.eq(theirs) | ^^^^^^^^^^^^^^^ +error: function cannot return without recursing + --> tests/ui/unconditional_recursion.rs:361:5 + | +LL | / fn from(f: BadFromTy1<'a>) -> Self { +LL | | f.into() +LL | | } + | |_____^ + | +note: recursive call site + --> tests/ui/unconditional_recursion.rs:362:9 + | +LL | f.into() + | ^^^^^^^^ + +error: function cannot return without recursing + --> tests/ui/unconditional_recursion.rs:370:5 + | +LL | / fn from(f: BadFromTy2<'a>) -> Self { +LL | | Into::into(f) +LL | | } + | |_____^ + | +note: recursive call site + --> tests/ui/unconditional_recursion.rs:371:9 + | +LL | Into::into(f) + | ^^^^^^^^^^^^^ + error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/unused_enumerate_index.fixed b/src/tools/clippy/tests/ui/unused_enumerate_index.fixed index d079807ab58..cffd02b0acc 100644 --- a/src/tools/clippy/tests/ui/unused_enumerate_index.fixed +++ b/src/tools/clippy/tests/ui/unused_enumerate_index.fixed @@ -1,8 +1,12 @@ -#![allow(unused)] +#![allow(unused, clippy::map_identity)] #![warn(clippy::unused_enumerate_index)] use std::iter::Enumerate; +fn get_enumerate() -> Enumerate<std::vec::IntoIter<i32>> { + vec![1].into_iter().enumerate() +} + fn main() { let v = [1, 2, 3]; for x in v.iter() { @@ -55,4 +59,48 @@ fn main() { for x in dummy { println!("{x}"); } + + let _ = vec![1, 2, 3].into_iter().map(|x| println!("{x}")); + + let p = vec![1, 2, 3].into_iter(); + p.map(|x| println!("{x}")); + + // This shouldn't trigger the lint. `get_enumerate` may come from an external library on which we + // have no control. + let p = get_enumerate(); + p.map(|(_, x)| println!("{x}")); + + // This shouldn't trigger the lint. The `enumerate` call is in a different context. + macro_rules! mac { + () => { + [1].iter().enumerate() + }; + } + _ = mac!().map(|(_, v)| v); + + macro_rules! mac2 { + () => { + [1].iter() + }; + } + _ = mac2!().map(|_v| {}); + + // This shouldn't trigger the lint because of the `allow`. + #[allow(clippy::unused_enumerate_index)] + let v = [1].iter().enumerate(); + v.map(|(_, _x)| {}); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied(); + let x = v.map(|x: i32| x).sum::<i32>(); + assert_eq!(x, 6); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied(); + let x = v.map(|x: i32| x).sum::<i32>(); + assert_eq!(x, 6); + + let v = [1, 2, 3].iter().copied(); + let x = v.map(|x| x).sum::<i32>(); + assert_eq!(x, 6); } diff --git a/src/tools/clippy/tests/ui/unused_enumerate_index.rs b/src/tools/clippy/tests/ui/unused_enumerate_index.rs index 2d524da7632..f2b5f8b9124 100644 --- a/src/tools/clippy/tests/ui/unused_enumerate_index.rs +++ b/src/tools/clippy/tests/ui/unused_enumerate_index.rs @@ -1,8 +1,12 @@ -#![allow(unused)] +#![allow(unused, clippy::map_identity)] #![warn(clippy::unused_enumerate_index)] use std::iter::Enumerate; +fn get_enumerate() -> Enumerate<std::vec::IntoIter<i32>> { + vec![1].into_iter().enumerate() +} + fn main() { let v = [1, 2, 3]; for (_, x) in v.iter().enumerate() { @@ -55,4 +59,48 @@ fn main() { for (_, x) in dummy.enumerate() { println!("{x}"); } + + let _ = vec![1, 2, 3].into_iter().enumerate().map(|(_, x)| println!("{x}")); + + let p = vec![1, 2, 3].into_iter().enumerate(); + p.map(|(_, x)| println!("{x}")); + + // This shouldn't trigger the lint. `get_enumerate` may come from an external library on which we + // have no control. + let p = get_enumerate(); + p.map(|(_, x)| println!("{x}")); + + // This shouldn't trigger the lint. The `enumerate` call is in a different context. + macro_rules! mac { + () => { + [1].iter().enumerate() + }; + } + _ = mac!().map(|(_, v)| v); + + macro_rules! mac2 { + () => { + [1].iter() + }; + } + _ = mac2!().enumerate().map(|(_, _v)| {}); + + // This shouldn't trigger the lint because of the `allow`. + #[allow(clippy::unused_enumerate_index)] + let v = [1].iter().enumerate(); + v.map(|(_, _x)| {}); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied().enumerate(); + let x = v.map(|(_, x): (usize, i32)| x).sum::<i32>(); + assert_eq!(x, 6); + + // This should keep the explicit type of `x`. + let v = [1, 2, 3].iter().copied().enumerate(); + let x = v.map(|(_, x): (_, i32)| x).sum::<i32>(); + assert_eq!(x, 6); + + let v = [1, 2, 3].iter().copied().enumerate(); + let x = v.map(|(_, x)| x).sum::<i32>(); + assert_eq!(x, 6); } diff --git a/src/tools/clippy/tests/ui/unused_enumerate_index.stderr b/src/tools/clippy/tests/ui/unused_enumerate_index.stderr index 7bd7d29117e..6ec07dcbff0 100644 --- a/src/tools/clippy/tests/ui/unused_enumerate_index.stderr +++ b/src/tools/clippy/tests/ui/unused_enumerate_index.stderr @@ -1,5 +1,5 @@ error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:8:19 + --> tests/ui/unused_enumerate_index.rs:12:19 | LL | for (_, x) in v.iter().enumerate() { | ^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | for x in v.iter() { | ~ ~~~~~~~~ error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:55:19 + --> tests/ui/unused_enumerate_index.rs:59:19 | LL | for (_, x) in dummy.enumerate() { | ^^^^^^^^^^^^^^^^^ @@ -22,5 +22,77 @@ help: remove the `.enumerate()` call LL | for x in dummy { | ~ ~~~~~ -error: aborting due to 2 previous errors +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:63:39 + | +LL | let _ = vec![1, 2, 3].into_iter().enumerate().map(|(_, x)| println!("{x}")); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL - let _ = vec![1, 2, 3].into_iter().enumerate().map(|(_, x)| println!("{x}")); +LL + let _ = vec![1, 2, 3].into_iter().map(|x| println!("{x}")); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:65:39 + | +LL | let p = vec![1, 2, 3].into_iter().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let p = vec![1, 2, 3].into_iter(); +LL ~ p.map(|x| println!("{x}")); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:86:17 + | +LL | _ = mac2!().enumerate().map(|(_, _v)| {}); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL - _ = mac2!().enumerate().map(|(_, _v)| {}); +LL + _ = mac2!().map(|_v| {}); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:94:39 + | +LL | let v = [1, 2, 3].iter().copied().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let v = [1, 2, 3].iter().copied(); +LL ~ let x = v.map(|x: i32| x).sum::<i32>(); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:99:39 + | +LL | let v = [1, 2, 3].iter().copied().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let v = [1, 2, 3].iter().copied(); +LL ~ let x = v.map(|x: i32| x).sum::<i32>(); + | + +error: you seem to use `.enumerate()` and immediately discard the index + --> tests/ui/unused_enumerate_index.rs:103:39 + | +LL | let v = [1, 2, 3].iter().copied().enumerate(); + | ^^^^^^^^^^^ + | +help: remove the `.enumerate()` call + | +LL ~ let v = [1, 2, 3].iter().copied(); +LL ~ let x = v.map(|x| x).sum::<i32>(); + | + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs index 7e5a10c911b..f5b200d5ffe 100644 --- a/src/tools/clippy/tests/ui/unused_io_amount.rs +++ b/src/tools/clippy/tests/ui/unused_io_amount.rs @@ -271,5 +271,10 @@ pub fn wildcards(rdr: &mut dyn std::io::Read) { } } } +fn allow_works<F: std::io::Read>(mut f: F) { + let mut data = Vec::with_capacity(100); + #[allow(clippy::unused_io_amount)] + f.read(&mut data).unwrap(); +} fn main() {} diff --git a/src/tools/clippy/tests/ui/unused_peekable.rs b/src/tools/clippy/tests/ui/unused_peekable.rs index 131b51e01b6..5865bba4350 100644 --- a/src/tools/clippy/tests/ui/unused_peekable.rs +++ b/src/tools/clippy/tests/ui/unused_peekable.rs @@ -174,3 +174,9 @@ fn valid() { let mut peekable = std::iter::empty::<u32>().peekable(); takes_dyn(&mut peekable); } + +fn allow_works() { + #[allow(clippy::unused_peekable)] + let iter = [1, 2, 3].iter().peekable(); + iter; +} diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed index 787dd3ec7e6..6ea7857a238 100644 --- a/src/tools/clippy/tests/ui/use_self.fixed +++ b/src/tools/clippy/tests/ui/use_self.fixed @@ -6,7 +6,8 @@ clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into, - clippy::self_named_constructors + clippy::self_named_constructors, + clippy::needless_lifetimes )] #[macro_use] @@ -53,6 +54,7 @@ mod better { } mod lifetimes { + #[derive(Clone, Copy)] struct Foo<'a> { foo_str: &'a str, } @@ -68,11 +70,19 @@ mod lifetimes { Foo { foo_str: "foo" } } - // FIXME: the lint does not handle lifetimed struct - // `Self` should be applicable here - fn clone(&self) -> Foo<'a> { + fn clone(&self) -> Self { Foo { foo_str: self.foo_str } } + + // Cannot replace with `Self` because the lifetime is not `'a`. + fn eq<'b>(&self, other: Foo<'b>) -> bool { + let x: Foo<'_> = other; + self.foo_str == other.foo_str + } + + fn f(&self) -> Foo<'_> { + *self + } } } diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs index 39e182faea6..338cc00e45a 100644 --- a/src/tools/clippy/tests/ui/use_self.rs +++ b/src/tools/clippy/tests/ui/use_self.rs @@ -6,7 +6,8 @@ clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into, - clippy::self_named_constructors + clippy::self_named_constructors, + clippy::needless_lifetimes )] #[macro_use] @@ -53,6 +54,7 @@ mod better { } mod lifetimes { + #[derive(Clone, Copy)] struct Foo<'a> { foo_str: &'a str, } @@ -68,11 +70,19 @@ mod lifetimes { Foo { foo_str: "foo" } } - // FIXME: the lint does not handle lifetimed struct - // `Self` should be applicable here fn clone(&self) -> Foo<'a> { Foo { foo_str: self.foo_str } } + + // Cannot replace with `Self` because the lifetime is not `'a`. + fn eq<'b>(&self, other: Foo<'b>) -> bool { + let x: Foo<'_> = other; + self.foo_str == other.foo_str + } + + fn f(&self) -> Foo<'_> { + *self + } } } diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr index 8d045f05ed2..d7aa8410a47 100644 --- a/src/tools/clippy/tests/ui/use_self.stderr +++ b/src/tools/clippy/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> tests/ui/use_self.rs:21:21 + --> tests/ui/use_self.rs:22:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -8,250 +8,256 @@ LL | fn new() -> Foo { = help: to override `-D warnings` add `#[allow(clippy::use_self)]` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:22:13 + --> tests/ui/use_self.rs:23:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:24:22 + --> tests/ui/use_self.rs:25:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:25:13 + --> tests/ui/use_self.rs:26:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:30:25 + --> tests/ui/use_self.rs:31:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:31:13 + --> tests/ui/use_self.rs:32:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:96:24 + --> tests/ui/use_self.rs:73:28 + | +LL | fn clone(&self) -> Foo<'a> { + | ^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> tests/ui/use_self.rs:106:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:96:55 + --> tests/ui/use_self.rs:106:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:111:13 + --> tests/ui/use_self.rs:121:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:146:29 + --> tests/ui/use_self.rs:156:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:147:21 + --> tests/ui/use_self.rs:157:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:158:21 + --> tests/ui/use_self.rs:168:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:159:13 + --> tests/ui/use_self.rs:169:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:176:21 + --> tests/ui/use_self.rs:186:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:177:21 + --> tests/ui/use_self.rs:187:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:178:21 + --> tests/ui/use_self.rs:188:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:220:13 + --> tests/ui/use_self.rs:230:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:221:13 + --> tests/ui/use_self.rs:231:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:223:13 + --> tests/ui/use_self.rs:233:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:242:13 + --> tests/ui/use_self.rs:252:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:256:25 + --> tests/ui/use_self.rs:266:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:257:13 + --> tests/ui/use_self.rs:267:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:261:16 + --> tests/ui/use_self.rs:271:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:261:22 + --> tests/ui/use_self.rs:271:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:284:29 + --> tests/ui/use_self.rs:294:29 | LL | fn foo(value: T) -> Foo<T> { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:285:13 + --> tests/ui/use_self.rs:295:13 | LL | Foo::<T> { value } | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:457:13 + --> tests/ui/use_self.rs:467:13 | LL | A::new::<submod::B>(submod::B {}) | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:494:13 + --> tests/ui/use_self.rs:504:13 | LL | S2::new() | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:531:17 + --> tests/ui/use_self.rs:541:17 | LL | Foo::Bar => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:532:17 + --> tests/ui/use_self.rs:542:17 | LL | Foo::Baz => unimplemented!(), | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:538:20 + --> tests/ui/use_self.rs:548:20 | LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:562:17 + --> tests/ui/use_self.rs:572:17 | LL | Something::Num(n) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:563:17 + --> tests/ui/use_self.rs:573:17 | LL | Something::TupleNums(n, _m) => *n, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:564:17 + --> tests/ui/use_self.rs:574:17 | LL | Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:570:17 + --> tests/ui/use_self.rs:580:17 | LL | crate::issue8845::Something::Num(n) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:571:17 + --> tests/ui/use_self.rs:581:17 | LL | crate::issue8845::Something::TupleNums(n, _m) => *n, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:572:17 + --> tests/ui/use_self.rs:582:17 | LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:588:17 + --> tests/ui/use_self.rs:598:17 | LL | let Foo(x) = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:593:17 + --> tests/ui/use_self.rs:603:17 | LL | let crate::issue8845::Foo(x) = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:600:17 + --> tests/ui/use_self.rs:610:17 | LL | let Bar { x, .. } = self; | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:605:17 + --> tests/ui/use_self.rs:615:17 | LL | let crate::issue8845::Bar { x, .. } = self; | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> tests/ui/use_self.rs:644:17 + --> tests/ui/use_self.rs:654:17 | LL | E::A => {}, | ^ help: use the applicable keyword: `Self` -error: aborting due to 42 previous errors +error: aborting due to 43 previous errors diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed b/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed new file mode 100644 index 00000000000..6f132521926 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects.fixed @@ -0,0 +1,60 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::useless_vec)] +#![allow(clippy::needless_late_init)] + +fn f() -> i32 { + println!("side effect"); + 10 +} + +fn main() { + const N: usize = 0; + const M: usize = 1; + + // should trigger + + // on arrays + f(); let a: [i32; 0] = []; + f(); let a: [i32; 0] = []; + let mut b; + f(); b = [] as [i32; 0]; + f(); b = [] as [i32; 0]; + + // on vecs + // vecs dont support infering value of consts + f(); let c: std::vec::Vec<i32> = vec![]; + let d; + f(); d = vec![] as std::vec::Vec<i32>; + + // for macros + println!("side effect"); let e: [(); 0] = []; + + // for nested calls + { f() }; let g: [i32; 0] = []; + + // as function param + drop({ f(); vec![] as std::vec::Vec<i32> }); + + // when singled out/not part of assignment/local + { f(); vec![] as std::vec::Vec<i32> }; + { f(); [] as [i32; 0] }; + { f(); [] as [i32; 0] }; + + // should not trigger + + // on arrays with > 0 repeat + let a = [f(); 1]; + let a = [f(); M]; + let mut b; + b = [f(); 1]; + b = [f(); M]; + + // on vecs with > 0 repeat + let c = vec![f(); 1]; + let d; + d = vec![f(); 1]; + + // as function param + drop(vec![f(); 1]); +} diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs b/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs new file mode 100644 index 00000000000..9d9c367375a --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects.rs @@ -0,0 +1,60 @@ +#![warn(clippy::zero_repeat_side_effects)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::useless_vec)] +#![allow(clippy::needless_late_init)] + +fn f() -> i32 { + println!("side effect"); + 10 +} + +fn main() { + const N: usize = 0; + const M: usize = 1; + + // should trigger + + // on arrays + let a = [f(); 0]; + let a = [f(); N]; + let mut b; + b = [f(); 0]; + b = [f(); N]; + + // on vecs + // vecs dont support infering value of consts + let c = vec![f(); 0]; + let d; + d = vec![f(); 0]; + + // for macros + let e = [println!("side effect"); 0]; + + // for nested calls + let g = [{ f() }; 0]; + + // as function param + drop(vec![f(); 0]); + + // when singled out/not part of assignment/local + vec![f(); 0]; + [f(); 0]; + [f(); N]; + + // should not trigger + + // on arrays with > 0 repeat + let a = [f(); 1]; + let a = [f(); M]; + let mut b; + b = [f(); 1]; + b = [f(); M]; + + // on vecs with > 0 repeat + let c = vec![f(); 1]; + let d; + d = vec![f(); 1]; + + // as function param + drop(vec![f(); 1]); +} diff --git a/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr b/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr new file mode 100644 index 00000000000..afdc6054253 --- /dev/null +++ b/src/tools/clippy/tests/ui/zero_repeat_side_effects.stderr @@ -0,0 +1,77 @@ +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:18:5 + | +LL | let a = [f(); 0]; + | ^^^^^^^^^^^^^^^^^ help: consider using: `f(); let a: [i32; 0] = [];` + | + = note: `-D clippy::zero-repeat-side-effects` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zero_repeat_side_effects)]` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:19:5 + | +LL | let a = [f(); N]; + | ^^^^^^^^^^^^^^^^^ help: consider using: `f(); let a: [i32; 0] = [];` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:21:5 + | +LL | b = [f(); 0]; + | ^^^^^^^^^^^^ help: consider using: `f(); b = [] as [i32; 0]` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:22:5 + | +LL | b = [f(); N]; + | ^^^^^^^^^^^^ help: consider using: `f(); b = [] as [i32; 0]` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:26:5 + | +LL | let c = vec![f(); 0]; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f(); let c: std::vec::Vec<i32> = vec![];` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:28:5 + | +LL | d = vec![f(); 0]; + | ^^^^^^^^^^^^^^^^ help: consider using: `f(); d = vec![] as std::vec::Vec<i32>` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:31:5 + | +LL | let e = [println!("side effect"); 0]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `println!("side effect"); let e: [(); 0] = [];` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:34:5 + | +LL | let g = [{ f() }; 0]; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `{ f() }; let g: [i32; 0] = [];` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:37:10 + | +LL | drop(vec![f(); 0]); + | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec<i32> }` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:40:5 + | +LL | vec![f(); 0]; + | ^^^^^^^^^^^^ help: consider using: `{ f(); vec![] as std::vec::Vec<i32> }` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:41:5 + | +LL | [f(); 0]; + | ^^^^^^^^ help: consider using: `{ f(); [] as [i32; 0] }` + +error: function or method calls as the initial value in zero-sized array initializers may cause side effects + --> tests/ui/zero_repeat_side_effects.rs:42:5 + | +LL | [f(); N]; + | ^^^^^^^^ help: consider using: `{ f(); [] as [i32; 0] }` + +error: aborting due to 12 previous errors + diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 1a81394af10..d455d967e30 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -19,7 +19,7 @@ new_pr = true [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" -users_on_vacation = ["xFrednet"] +users_on_vacation = [] [assign.owners] "/.github" = ["@flip1995"] diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 0c590b0fda6..bd2f65925bb 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -198,6 +198,11 @@ pub fn compute_stamp_hash(config: &Config) -> String { format!("{:x}", hash.finish()) } +fn remove_and_create_dir_all(path: &Path) { + let _ = fs::remove_dir_all(path); + fs::create_dir_all(path).unwrap(); +} + #[derive(Copy, Clone)] struct TestCx<'test> { config: &'test Config, @@ -998,8 +1003,7 @@ impl<'test> TestCx<'test> { let mut rustc = Command::new(&self.config.rustc_path); let out_dir = self.output_base_name().with_extension("pretty-out"); - let _ = fs::remove_dir_all(&out_dir); - create_dir_all(&out_dir).unwrap(); + remove_and_create_dir_all(&out_dir); let target = if self.props.force_host { &*self.config.host } else { &*self.config.target }; @@ -2094,14 +2098,12 @@ impl<'test> TestCx<'test> { let aux_dir = self.aux_output_dir_name(); if !self.props.aux_builds.is_empty() { - let _ = fs::remove_dir_all(&aux_dir); - create_dir_all(&aux_dir).unwrap(); + remove_and_create_dir_all(&aux_dir); } if !self.props.aux_bins.is_empty() { let aux_bin_dir = self.aux_bin_output_dir_name(); - let _ = fs::remove_dir_all(&aux_bin_dir); - create_dir_all(&aux_bin_dir).unwrap(); + remove_and_create_dir_all(&aux_bin_dir); } aux_dir @@ -2469,8 +2471,7 @@ impl<'test> TestCx<'test> { } let mir_dump_dir = self.get_mir_dump_dir(); - let _ = fs::remove_dir_all(&mir_dump_dir); - create_dir_all(mir_dump_dir.as_path()).unwrap(); + remove_and_create_dir_all(&mir_dump_dir); let mut dir_opt = "-Zdump-mir-dir=".to_string(); dir_opt.push_str(mir_dump_dir.to_str().unwrap()); debug!("dir_opt: {:?}", dir_opt); @@ -2951,8 +2952,7 @@ impl<'test> TestCx<'test> { assert!(self.revision.is_none(), "revisions not relevant here"); let out_dir = self.output_base_dir(); - let _ = fs::remove_dir_all(&out_dir); - create_dir_all(&out_dir).unwrap(); + remove_and_create_dir_all(&out_dir); let proc_res = self.document(&out_dir); if !proc_res.status.success() { @@ -2986,9 +2986,7 @@ impl<'test> TestCx<'test> { let suffix = self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly"); let compare_dir = output_base_dir(self.config, self.testpaths, Some(&suffix)); - // Don't give an error if the directory didn't already exist - let _ = fs::remove_dir_all(&compare_dir); - create_dir_all(&compare_dir).unwrap(); + remove_and_create_dir_all(&compare_dir); // We need to create a new struct for the lifetimes on `config` to work. let new_rustdoc = TestCx { @@ -3137,8 +3135,7 @@ impl<'test> TestCx<'test> { assert!(self.revision.is_none(), "revisions not relevant here"); let out_dir = self.output_base_dir(); - let _ = fs::remove_dir_all(&out_dir); - create_dir_all(&out_dir).unwrap(); + remove_and_create_dir_all(&out_dir); let proc_res = self.document(&out_dir); if !proc_res.status.success() { diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index c70005c2c58..1097fc6db72 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -206,7 +206,7 @@ jobs: run: | PR=$(gh pr create -B master --title 'Automatic Rustup' --body '') ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \ - --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \ + --stream miri --subject "Miri Build Failure ($(date -u +%Y-%m))" \ --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience." env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/tools/miri/bench-cargo-miri/invalidate/Cargo.lock b/src/tools/miri/bench-cargo-miri/range-iteration/Cargo.lock index 7bf23225ea5..2a5f8f32254 100644 --- a/src/tools/miri/bench-cargo-miri/invalidate/Cargo.lock +++ b/src/tools/miri/bench-cargo-miri/range-iteration/Cargo.lock @@ -3,5 +3,5 @@ version = 3 [[package]] -name = "invalidate" +name = "range-iteration" version = "0.1.0" diff --git a/src/tools/miri/bench-cargo-miri/invalidate/Cargo.toml b/src/tools/miri/bench-cargo-miri/range-iteration/Cargo.toml index 14cf0882f0b..3f0146f6259 100644 --- a/src/tools/miri/bench-cargo-miri/invalidate/Cargo.toml +++ b/src/tools/miri/bench-cargo-miri/range-iteration/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "invalidate" +name = "range-iteration" version = "0.1.0" edition = "2021" diff --git a/src/tools/miri/bench-cargo-miri/invalidate/src/main.rs b/src/tools/miri/bench-cargo-miri/range-iteration/src/main.rs index fa8deb851c3..1ef35ee6b3c 100644 --- a/src/tools/miri/bench-cargo-miri/invalidate/src/main.rs +++ b/src/tools/miri/bench-cargo-miri/range-iteration/src/main.rs @@ -1,4 +1,5 @@ +//! This generates a lot of work for the AllocId part of the GC. fn main() { // The end of the range is just chosen to make the benchmark run for a few seconds. - for _ in 0..200_000 {} + for _ in 0..50_000 {} } diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index e32968d8178..9b89f016a77 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -ee03c286cfdca26fa5b2a4ee40957625d2c826ff +c3b05c6e5b5b59613350b8c2875b0add67ed74df diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 6955e649b4d..c12382527e7 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -179,6 +179,26 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { }); } } + + fn after_analysis<'tcx>( + &mut self, + _: &rustc_interface::interface::Compiler, + queries: &'tcx rustc_interface::Queries<'tcx>, + ) -> Compilation { + queries.global_ctxt().unwrap().enter(|tcx| { + if self.target_crate { + // cargo-miri has patched the compiler flags to make these into check-only builds, + // but we are still emulating regular rustc builds, which would perform post-mono + // const-eval during collection. So let's also do that here, even if we might be + // running with `--emit=metadata`. In particular this is needed to make + // `compile_fail` doc tests trigger post-mono errors. + // In general `collect_and_partition_mono_items` is not safe to call in check-only + // builds, but we are setting `-Zalways-encode-mir` which avoids those issues. + let _ = tcx.collect_and_partition_mono_items(()); + } + }); + Compilation::Continue + } } fn show_error(msg: &impl std::fmt::Display) -> ! { diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 7a6a85a2f79..96ff298402d 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -18,6 +18,7 @@ use crate::borrow_tracker::{ stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder}, GlobalStateInner, ProtectorKind, }; +use crate::concurrency::data_race::{NaReadType, NaWriteType}; use crate::*; use diagnostics::{RetagCause, RetagInfo}; @@ -90,7 +91,7 @@ impl NewPermission { } } } - ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Mut, .. }) => { + ty::RawPtr(_, Mutability::Mut) => { assert!(protector.is_none()); // RetagKind can't be both FnEntry and Raw. // Mutable raw pointer. No access, not protected. NewPermission::Uniform { @@ -114,7 +115,7 @@ impl NewPermission { // This fixes https://github.com/rust-lang/rust/issues/55005. } } - ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Not, .. }) => { + ty::RawPtr(_, Mutability::Not) => { assert!(protector.is_none()); // RetagKind can't be both FnEntry and Raw. // `*const T`, when freshly created, are read-only in the frozen part. NewPermission::FreezeSensitive { @@ -751,7 +752,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' assert_eq!(access, AccessKind::Write); // Make sure the data race model also knows about this. if let Some(data_race) = alloc_extra.data_race.as_mut() { - data_race.write(alloc_id, range, machine)?; + data_race.write( + alloc_id, + range, + NaWriteType::Retag, + Some(place.layout.ty), + machine, + )?; } } } @@ -794,7 +801,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' assert_eq!(access, AccessKind::Read); // Make sure the data race model also knows about this. if let Some(data_race) = alloc_extra.data_race.as_ref() { - data_race.read(alloc_id, range, &this.machine)?; + data_race.read( + alloc_id, + range, + NaReadType::Retag, + Some(place.layout.ty), + &this.machine, + )?; } } Ok(()) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 80bdcbb7559..a3d49756e4c 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -9,8 +9,11 @@ use rustc_middle::{ use rustc_span::def_id::DefId; use rustc_target::abi::{Abi, Size}; -use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind}; use crate::*; +use crate::{ + borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind}, + concurrency::data_race::NaReadType, +}; pub mod diagnostics; mod perms; @@ -312,7 +315,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' // Also inform the data race model (but only if any bytes are actually affected). if range.size.bytes() > 0 { if let Some(data_race) = alloc_extra.data_race.as_ref() { - data_race.read(alloc_id, range, &this.machine)?; + data_race.read( + alloc_id, + range, + NaReadType::Retag, + Some(place.layout.ty), + &this.machine, + )?; } } @@ -475,7 +484,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { NewPermission::from_ref_ty(pointee, mutability, self.kind, self.ecx); self.retag_ptr_inplace(place, new_perm)?; } - ty::RawPtr(_) => { + ty::RawPtr(_, _) => { // We definitely do *not* want to recurse into raw pointers -- wide raw // pointers have fields, and for dyn Trait pointees those can have reference // type! diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 127d97bd5af..d51160b2831 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -49,7 +49,7 @@ use std::{ use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir; +use rustc_middle::{mir, ty::Ty}; use rustc_span::Span; use rustc_target::abi::{Align, HasDataLayout, Size}; @@ -200,18 +200,38 @@ enum AtomicAccessType { Rmw, } -/// Type of write operation: allocating memory -/// non-atomic writes and deallocating memory -/// are all treated as writes for the purpose -/// of the data-race detector. +/// Type of a non-atomic read operation. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum NaWriteType { +pub enum NaReadType { + /// Standard unsynchronized write. + Read, + + // An implicit read generated by a retag. + Retag, +} + +impl NaReadType { + fn description(self) -> &'static str { + match self { + NaReadType::Read => "non-atomic read", + NaReadType::Retag => "retag read", + } + } +} + +/// Type of a non-atomic write operation: allocating memory, non-atomic writes, and +/// deallocating memory are all treated as writes for the purpose of the data-race detector. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum NaWriteType { /// Allocate memory. Allocate, /// Standard unsynchronized write. Write, + // An implicit write generated by a retag. + Retag, + /// Deallocate memory. /// Note that when memory is deallocated first, later non-atomic accesses /// will be reported as use-after-free, not as data races. @@ -224,6 +244,7 @@ impl NaWriteType { match self { NaWriteType::Allocate => "creating a new allocation", NaWriteType::Write => "non-atomic write", + NaWriteType::Retag => "retag write", NaWriteType::Deallocate => "deallocation", } } @@ -231,7 +252,7 @@ impl NaWriteType { #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum AccessType { - NaRead, + NaRead(NaReadType), NaWrite(NaWriteType), AtomicLoad, AtomicStore, @@ -239,29 +260,48 @@ enum AccessType { } impl AccessType { - fn description(self) -> &'static str { - match self { - AccessType::NaRead => "non-atomic read", + fn description(self, ty: Option<Ty<'_>>, size: Option<Size>) -> String { + let mut msg = String::new(); + + if let Some(size) = size { + msg.push_str(&format!("{}-byte {}", size.bytes(), msg)) + } + + msg.push_str(match self { + AccessType::NaRead(w) => w.description(), AccessType::NaWrite(w) => w.description(), AccessType::AtomicLoad => "atomic load", AccessType::AtomicStore => "atomic store", AccessType::AtomicRmw => "atomic read-modify-write", + }); + + if let Some(ty) = ty { + msg.push_str(&format!(" of type `{}`", ty)); } + + msg } fn is_atomic(self) -> bool { match self { AccessType::AtomicLoad | AccessType::AtomicStore | AccessType::AtomicRmw => true, - AccessType::NaRead | AccessType::NaWrite(_) => false, + AccessType::NaRead(_) | AccessType::NaWrite(_) => false, } } fn is_read(self) -> bool { match self { - AccessType::AtomicLoad | AccessType::NaRead => true, + AccessType::AtomicLoad | AccessType::NaRead(_) => true, AccessType::NaWrite(_) | AccessType::AtomicStore | AccessType::AtomicRmw => false, } } + + fn is_retag(self) -> bool { + matches!( + self, + AccessType::NaRead(NaReadType::Retag) | AccessType::NaWrite(NaWriteType::Retag) + ) + } } /// Memory Cell vector clock metadata @@ -502,12 +542,14 @@ impl MemoryCellClocks { &mut self, thread_clocks: &mut ThreadClockSet, index: VectorIdx, + read_type: NaReadType, current_span: Span, ) -> Result<(), DataRace> { trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks); if !current_span.is_dummy() { thread_clocks.clock[index].span = current_span; } + thread_clocks.clock[index].set_read_type(read_type); if self.write_was_before(&thread_clocks.clock) { let race_free = if let Some(atomic) = self.atomic() { // We must be ordered-after all atomic accesses, reads and writes. @@ -875,7 +917,8 @@ impl VClockAlloc { /// This finds the two racing threads and the type /// of data-race that occurred. This will also /// return info about the memory location the data-race - /// occurred in. + /// occurred in. The `ty` parameter is used for diagnostics, letting + /// the user know which type was involved in the access. #[cold] #[inline(never)] fn report_data_race<'tcx>( @@ -885,6 +928,7 @@ impl VClockAlloc { access: AccessType, access_size: Size, ptr_dbg: Pointer<AllocId>, + ty: Option<Ty<'_>>, ) -> InterpResult<'tcx> { let (current_index, current_clocks) = global.current_thread_state(thread_mgr); let mut other_size = None; // if `Some`, this was a size-mismatch race @@ -908,7 +952,7 @@ impl VClockAlloc { write_clock = mem_clocks.write(); (AccessType::NaWrite(mem_clocks.write_type), mem_clocks.write.0, &write_clock) } else if let Some(idx) = Self::find_gt_index(&mem_clocks.read, ¤t_clocks.clock) { - (AccessType::NaRead, idx, &mem_clocks.read) + (AccessType::NaRead(mem_clocks.read[idx].read_type()), idx, &mem_clocks.read) // Finally, mixed-size races. } else if access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && atomic.size != access_size { // This is only a race if we are not synchronized with all atomic accesses, so find @@ -950,37 +994,33 @@ impl VClockAlloc { Err(err_machine_stop!(TerminationInfo::DataRace { involves_non_atomic, extra, + retag_explain: access.is_retag() || other_access.is_retag(), ptr: ptr_dbg, op1: RacingOp { - action: if let Some(other_size) = other_size { - format!("{}-byte {}", other_size.bytes(), other_access.description()) - } else { - other_access.description().to_owned() - }, + action: other_access.description(None, other_size), thread_info: other_thread_info, span: other_clock.as_slice()[other_thread.index()].span_data(), }, op2: RacingOp { - action: if other_size.is_some() { - format!("{}-byte {}", access_size.bytes(), access.description()) - } else { - access.description().to_owned() - }, + action: access.description(ty, other_size.map(|_| access_size)), thread_info: current_thread_info, span: current_clocks.clock.as_slice()[current_index.index()].span_data(), }, }))? } - /// Detect data-races for an unsynchronized read operation, will not perform + /// Detect data-races for an unsynchronized read operation. It will not perform /// data-race detection if `race_detecting()` is false, either due to no threads /// being created or if it is temporarily disabled during a racy read or write /// operation for which data-race detection is handled separately, for example - /// atomic read operations. + /// atomic read operations. The `ty` parameter is used for diagnostics, letting + /// the user know which type was read. pub fn read<'tcx>( &self, alloc_id: AllocId, access_range: AllocRange, + read_type: NaReadType, + ty: Option<Ty<'_>>, machine: &MiriMachine<'_, '_>, ) -> InterpResult<'tcx> { let current_span = machine.current_span(); @@ -992,7 +1032,7 @@ impl VClockAlloc { alloc_ranges.iter_mut(access_range.start, access_range.size) { if let Err(DataRace) = - mem_clocks.read_race_detect(&mut thread_clocks, index, current_span) + mem_clocks.read_race_detect(&mut thread_clocks, index, read_type, current_span) { drop(thread_clocks); // Report data-race. @@ -1000,9 +1040,10 @@ impl VClockAlloc { global, &machine.threads, mem_clocks, - AccessType::NaRead, + AccessType::NaRead(read_type), access_range.size, Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), + ty, ); } } @@ -1012,12 +1053,17 @@ impl VClockAlloc { } } - // Shared code for detecting data-races on unique access to a section of memory - fn unique_access<'tcx>( + /// Detect data-races for an unsynchronized write operation. It will not perform + /// data-race detection if `race_detecting()` is false, either due to no threads + /// being created or if it is temporarily disabled during a racy read or write + /// operation. The `ty` parameter is used for diagnostics, letting + /// the user know which type was written. + pub fn write<'tcx>( &mut self, alloc_id: AllocId, access_range: AllocRange, write_type: NaWriteType, + ty: Option<Ty<'_>>, machine: &mut MiriMachine<'_, '_>, ) -> InterpResult<'tcx> { let current_span = machine.current_span(); @@ -1042,6 +1088,7 @@ impl VClockAlloc { AccessType::NaWrite(write_type), access_range.size, Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), + ty, ); } } @@ -1050,37 +1097,6 @@ impl VClockAlloc { Ok(()) } } - - /// Detect data-races for an unsynchronized write operation, will not perform - /// data-race threads if `race_detecting()` is false, either due to no threads - /// being created or if it is temporarily disabled during a racy read or write - /// operation - pub fn write<'tcx>( - &mut self, - alloc_id: AllocId, - range: AllocRange, - machine: &mut MiriMachine<'_, '_>, - ) -> InterpResult<'tcx> { - self.unique_access(alloc_id, range, NaWriteType::Write, machine) - } - - /// Detect data-races for an unsynchronized deallocate operation, will not perform - /// data-race threads if `race_detecting()` is false, either due to no threads - /// being created or if it is temporarily disabled during a racy read or write - /// operation - pub fn deallocate<'tcx>( - &mut self, - alloc_id: AllocId, - size: Size, - machine: &mut MiriMachine<'_, '_>, - ) -> InterpResult<'tcx> { - self.unique_access( - alloc_id, - alloc_range(Size::ZERO, size), - NaWriteType::Deallocate, - machine, - ) - } } impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {} @@ -1279,7 +1295,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap(); trace!( "Atomic op({}) with ordering {:?} on {:?} (size={})", - access.description(), + access.description(None, None), &atomic, place.ptr(), size.bytes() @@ -1307,6 +1323,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { alloc_id, Size::from_bytes(mem_clocks_range.start), ), + None, ) .map(|_| true); } diff --git a/src/tools/miri/src/concurrency/vector_clock.rs b/src/tools/miri/src/concurrency/vector_clock.rs index fa93c9e00b1..fe719943dcb 100644 --- a/src/tools/miri/src/concurrency/vector_clock.rs +++ b/src/tools/miri/src/concurrency/vector_clock.rs @@ -4,9 +4,11 @@ use smallvec::SmallVec; use std::{ cmp::Ordering, fmt::Debug, - ops::{Index, IndexMut}, + ops::{Index, IndexMut, Shr}, }; +use super::data_race::NaReadType; + /// A vector clock index, this is associated with a thread id /// but in some cases one vector index may be shared with /// multiple thread ids if it's safe to do so. @@ -50,13 +52,51 @@ const SMALL_VECTOR: usize = 4; /// so that diagnostics can report what code was responsible for an operation. #[derive(Clone, Copy, Debug)] pub struct VTimestamp { - time: u32, + /// The lowest bit indicates read type, the rest is the time. + /// `1` indicates a retag read, `0` a regular read. + time_and_read_type: u32, pub span: Span, } impl VTimestamp { - pub const ZERO: VTimestamp = VTimestamp { time: 0, span: DUMMY_SP }; + pub const ZERO: VTimestamp = VTimestamp::new(0, NaReadType::Read, DUMMY_SP); + + #[inline] + const fn encode_time_and_read_type(time: u32, read_type: NaReadType) -> u32 { + let read_type_bit = match read_type { + NaReadType::Read => 0, + NaReadType::Retag => 1, + }; + // Put the `read_type` in the lowest bit and `time` in the rest + read_type_bit | time.checked_mul(2).expect("Vector clock overflow") + } + + #[inline] + const fn new(time: u32, read_type: NaReadType, span: Span) -> Self { + Self { time_and_read_type: Self::encode_time_and_read_type(time, read_type), span } + } + + #[inline] + fn time(&self) -> u32 { + self.time_and_read_type.shr(1) + } + #[inline] + fn set_time(&mut self, time: u32) { + self.time_and_read_type = Self::encode_time_and_read_type(time, self.read_type()); + } + + #[inline] + pub fn read_type(&self) -> NaReadType { + if self.time_and_read_type & 1 == 0 { NaReadType::Read } else { NaReadType::Retag } + } + + #[inline] + pub fn set_read_type(&mut self, read_type: NaReadType) { + self.time_and_read_type = Self::encode_time_and_read_type(self.time(), read_type); + } + + #[inline] pub fn span_data(&self) -> SpanData { self.span.data() } @@ -64,7 +104,7 @@ impl VTimestamp { impl PartialEq for VTimestamp { fn eq(&self, other: &Self) -> bool { - self.time == other.time + self.time() == other.time() } } @@ -78,7 +118,7 @@ impl PartialOrd for VTimestamp { impl Ord for VTimestamp { fn cmp(&self, other: &Self) -> Ordering { - self.time.cmp(&other.time) + self.time().cmp(&other.time()) } } @@ -130,7 +170,7 @@ impl VClock { let idx = idx.index(); let mut_slice = self.get_mut_with_min_len(idx + 1); let idx_ref = &mut mut_slice[idx]; - idx_ref.time = idx_ref.time.checked_add(1).expect("Vector clock overflow"); + idx_ref.set_time(idx_ref.time().checked_add(1).expect("Vector clock overflow")); if !current_span.is_dummy() { idx_ref.span = current_span; } @@ -379,8 +419,8 @@ impl IndexMut<VectorIdx> for VClock { /// test suite #[cfg(test)] mod tests { - use super::{VClock, VTimestamp, VectorIdx}; + use crate::concurrency::data_race::NaReadType; use rustc_span::DUMMY_SP; use std::cmp::Ordering; @@ -448,7 +488,13 @@ mod tests { while let Some(0) = slice.last() { slice = &slice[..slice.len() - 1] } - VClock(slice.iter().copied().map(|time| VTimestamp { time, span: DUMMY_SP }).collect()) + VClock( + slice + .iter() + .copied() + .map(|time| VTimestamp::new(time, NaReadType::Read, DUMMY_SP)) + .collect(), + ) } fn assert_order(l: &[u32], r: &[u32], o: Option<Ordering>) { diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 03428b081c5..99d37065bac 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -46,6 +46,7 @@ pub enum TerminationInfo { op1: RacingOp, op2: RacingOp, extra: Option<&'static str>, + retag_explain: bool, }, } @@ -263,12 +264,17 @@ pub fn report_error<'tcx, 'mir>( vec![(Some(*span), format!("the `{link_name}` symbol is defined here"))], Int2PtrWithStrictProvenance => vec![(None, format!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"))], - DataRace { op1, extra, .. } => { + DataRace { op1, extra, retag_explain, .. } => { let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))]; if let Some(extra) = extra { helps.push((None, format!("{extra}"))); helps.push((None, format!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model"))); } + if *retag_explain { + helps.push((None, "retags occur on all (re)borrows and as well as when references are copied or moved".to_owned())); + helps.push((None, "retags permit optimizations that insert speculative reads or writes".to_owned())); + helps.push((None, "therefore from the perspective of data races, a retag has the same implications as a read or write".to_owned())); + } helps.push((None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"))); helps.push((None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"))); helps diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 416d0cda8f1..7821aa9efd4 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -1,5 +1,6 @@ #![feature(rustc_private)] #![feature(cell_update)] +#![feature(const_option)] #![feature(float_gamma)] #![feature(generic_nonzero)] #![feature(map_try_insert)] diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 3a4ab32e4ab..2137de6a29b 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -12,7 +12,6 @@ use rand::rngs::StdRng; use rand::Rng; use rand::SeedableRng; -use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[allow(unused)] use rustc_data_structures::static_assert_size; @@ -22,7 +21,7 @@ use rustc_middle::{ ty::{ self, layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout}, - Instance, Ty, TyCtxt, TypeAndMut, + Instance, Ty, TyCtxt, }, }; use rustc_span::def_id::{CrateNum, DefId}; @@ -36,6 +35,9 @@ use crate::{ *, }; +use self::concurrency::data_race::NaReadType; +use self::concurrency::data_race::NaWriteType; + /// First real-time signal. /// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35 /// as typical values. @@ -373,10 +375,8 @@ pub struct PrimitiveLayouts<'tcx> { impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> { fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, &'tcx LayoutError<'tcx>> { let tcx = layout_cx.tcx; - let mut_raw_ptr = - Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut }); - let const_raw_ptr = - Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not }); + let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit); + let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); Ok(Self { unit: layout_cx.layout_of(Ty::new_unit(tcx))?, i8: layout_cx.layout_of(tcx.types.i8)?, @@ -1241,7 +1241,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Read)); } if let Some(data_race) = &alloc_extra.data_race { - data_race.read(alloc_id, range, machine)?; + data_race.read(alloc_id, range, NaReadType::Read, None, machine)?; } if let Some(borrow_tracker) = &alloc_extra.borrow_tracker { borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?; @@ -1265,7 +1265,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Write)); } if let Some(data_race) = &mut alloc_extra.data_race { - data_race.write(alloc_id, range, machine)?; + data_race.write(alloc_id, range, NaWriteType::Write, None, machine)?; } if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?; @@ -1289,7 +1289,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id)); } if let Some(data_race) = &mut alloc_extra.data_race { - data_race.deallocate(alloc_id, size, machine)?; + data_race.write( + alloc_id, + alloc_range(Size::ZERO, size), + NaWriteType::Deallocate, + None, + machine, + )?; } if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?; diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index c97a052f517..6973c0e9c35 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -33,6 +33,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "round" | "trunc" | "fsqrt" + | "fsin" + | "fcos" + | "fexp" + | "fexp2" + | "flog" + | "flog2" + | "flog10" | "ctlz" | "cttz" | "bswap" @@ -45,17 +52,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(dest_len, op_len); #[derive(Copy, Clone)] - enum Op { + enum Op<'a> { MirOp(mir::UnOp), Abs, - Sqrt, Round(rustc_apfloat::Round), Numeric(Symbol), + HostOp(&'a str), } let which = match intrinsic_name { "neg" => Op::MirOp(mir::UnOp::Neg), "fabs" => Op::Abs, - "fsqrt" => Op::Sqrt, "ceil" => Op::Round(rustc_apfloat::Round::TowardPositive), "floor" => Op::Round(rustc_apfloat::Round::TowardNegative), "round" => Op::Round(rustc_apfloat::Round::NearestTiesToAway), @@ -64,7 +70,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "cttz" => Op::Numeric(sym::cttz), "bswap" => Op::Numeric(sym::bswap), "bitreverse" => Op::Numeric(sym::bitreverse), - _ => unreachable!(), + _ => Op::HostOp(intrinsic_name), }; for i in 0..dest_len { @@ -89,7 +95,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { FloatTy::F128 => unimplemented!("f16_f128"), } } - Op::Sqrt => { + Op::HostOp(host_op) => { let ty::Float(float_ty) = op.layout.ty.kind() else { span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) }; @@ -98,13 +104,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { FloatTy::F16 => unimplemented!("f16_f128"), FloatTy::F32 => { let f = op.to_scalar().to_f32()?; - let res = f.to_host().sqrt().to_soft(); + let f_host = f.to_host(); + let res = match host_op { + "fsqrt" => f_host.sqrt(), + "fsin" => f_host.sin(), + "fcos" => f_host.cos(), + "fexp" => f_host.exp(), + "fexp2" => f_host.exp2(), + "flog" => f_host.ln(), + "flog2" => f_host.log2(), + "flog10" => f_host.log10(), + _ => bug!(), + }; + let res = res.to_soft(); let res = this.adjust_nan(res, &[f]); Scalar::from(res) } FloatTy::F64 => { let f = op.to_scalar().to_f64()?; - let res = f.to_host().sqrt().to_soft(); + let f_host = f.to_host(); + let res = match host_op { + "fsqrt" => f_host.sqrt(), + "fsin" => f_host.sin(), + "fcos" => f_host.cos(), + "fexp" => f_host.exp(), + "fexp2" => f_host.exp2(), + "flog" => f_host.ln(), + "flog2" => f_host.log2(), + "flog10" => f_host.log10(), + _ => bug!(), + }; + let res = res.to_soft(); let res = this.adjust_nan(res, &[f]); Scalar::from(res) } @@ -427,7 +457,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let bitmask_len = u32::try_from(bitmask_len).unwrap(); // To read the mask, we transmute it to an integer. - // That does the right thing wrt endianess. + // That does the right thing wrt endianness. let mask_ty = this.machine.layouts.uint(mask.layout.size).unwrap(); let mask = mask.transmute(mask_ty, this)?; let mask: u64 = this.read_scalar(&mask)?.to_bits(mask_ty.size)?.try_into().unwrap(); @@ -479,7 +509,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } // We have to change the type of the place to be able to write `res` into it. This - // transmutes the integer to an array, which does the right thing wrt endianess. + // transmutes the integer to an array, which does the right thing wrt endianness. let dest = dest.transmute(this.machine.layouts.uint(dest.layout.size).unwrap(), this)?; this.write_int(res, &dest)?; diff --git a/src/tools/miri/src/shims/unix/linux/fd/epoll.rs b/src/tools/miri/src/shims/unix/linux/fd/epoll.rs index a429caaf8f4..8c5aed6def6 100644 --- a/src/tools/miri/src/shims/unix/linux/fd/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/fd/epoll.rs @@ -21,9 +21,11 @@ pub struct Epoll { /// <https://man7.org/linux/man-pages/man2/epoll_ctl.2.html> #[derive(Clone, Debug)] pub struct EpollEvent { + #[allow(dead_code)] pub events: u32, /// `Scalar<Provenance>` is used to represent the /// `epoll_data` type union. + #[allow(dead_code)] pub data: Scalar<Provenance>, } diff --git a/src/tools/miri/src/shims/unix/linux/fd/event.rs b/src/tools/miri/src/shims/unix/linux/fd/event.rs index 1f17ffb88c8..49408fda3ae 100644 --- a/src/tools/miri/src/shims/unix/linux/fd/event.rs +++ b/src/tools/miri/src/shims/unix/linux/fd/event.rs @@ -38,7 +38,7 @@ impl FileDescriptor for Event { } /// A write call adds the 8-byte integer value supplied in - /// its buffer (in native endianess) to the counter. The maximum value that may be + /// its buffer (in native endianness) to the counter. The maximum value that may be /// stored in the counter is the largest unsigned 64-bit value /// minus 1 (i.e., 0xfffffffffffffffe). If the addition would /// cause the counter's value to exceed the maximum, then the @@ -57,7 +57,7 @@ impl FileDescriptor for Event { ) -> InterpResult<'tcx, io::Result<usize>> { let v1 = self.val.get(); let bytes: [u8; 8] = bytes.try_into().unwrap(); // FIXME fail gracefully when this has the wrong size - // Convert from target endianess to host endianess. + // Convert from target endianness to host endianness. let num = match tcx.sess.target.endian { Endian::Little => u64::from_le_bytes(bytes), Endian::Big => u64::from_be_bytes(bytes), diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 7cd397625dc..7b7921219e6 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -88,6 +88,19 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.write_immediate(*sub, &this.project_field(dest, 1)?)?; } + // Used to implement the `_mm_pause` function. + // The intrinsic is used to hint the processor that the code is in a spin-loop. + // It is compiled down to a `pause` instruction. When SSE2 is not available, + // the instruction behaves like a no-op, so it is always safe to call the + // intrinsic. + "sse2.pause" => { + let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + // Only exhibit the spin-loop hint behavior when SSE2 is enabled. + if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) { + this.yield_active_thread(); + } + } + name if name.starts_with("sse.") => { return sse::EvalContextExt::emulate_x86_sse_intrinsic( this, link_name, abi, args, dest, diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index 18ff5d809e3..eb2cc9d37c8 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -580,12 +580,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } } - // Used to implement the `_mm_pause` function. - // The intrinsic is used to hint the processor that the code is in a spin-loop. - "pause" => { - let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.yield_active_thread(); - } _ => return Ok(EmulateForeignItemResult::NotSupported), } Ok(EmulateForeignItemResult::NeedsJumping) diff --git a/src/tools/miri/test-cargo-miri/src/lib.rs b/src/tools/miri/test-cargo-miri/src/lib.rs index 66c8aa2eac5..e6b8c4ef65b 100644 --- a/src/tools/miri/test-cargo-miri/src/lib.rs +++ b/src/tools/miri/test-cargo-miri/src/lib.rs @@ -1,13 +1,31 @@ /// Doc-test test +/// /// ```rust /// assert!(cargo_miri_test::make_true()); /// ``` +/// +/// `no_run` test: +/// /// ```rust,no_run /// assert!(!cargo_miri_test::make_true()); /// ``` +/// +/// `compile_fail` test: +/// /// ```rust,compile_fail /// assert!(cargo_miri_test::make_true() == 5); /// ``` +/// +/// Post-monomorphization error in `compile_fail` test: +/// +/// ```rust,compile_fail +/// struct Fail<T>(T); +/// impl<T> Fail<T> { +/// const C: () = panic!(); +/// } +/// +/// let _val = Fail::<i32>::C; +/// ``` #[no_mangle] pub fn make_true() -> bool { issue_1567::use_the_dependency(); diff --git a/src/tools/miri/test-cargo-miri/test.default.stdout.ref b/src/tools/miri/test-cargo-miri/test.default.stdout.ref index 9a17f3d61b6..922d2120bed 100644 --- a/src/tools/miri/test-cargo-miri/test.default.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.default.stdout.ref @@ -10,7 +10,7 @@ running 6 tests test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out -running 4 tests -.... -test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +running 5 tests +..... +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref index c618956656a..5c819dd5323 100644 --- a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref @@ -13,5 +13,5 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in $TIME +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs index eb1fe56df07..3edaf10f3dc 100644 --- a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs +++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs @@ -17,7 +17,7 @@ fn thread_1(p: SendPtr) { fn thread_2(p: SendPtr) { let p = p.0; unsafe { - *p = 5; //~ ERROR: /Data race detected between \(1\) non-atomic (read|write) on thread `unnamed-[0-9]+` and \(2\) non-atomic write on thread `unnamed-[0-9]+`/ + *p = 5; //~ ERROR: /Data race detected between \(1\) retag (read|write) on thread `unnamed-[0-9]+` and \(2\) non-atomic write on thread `unnamed-[0-9]+`/ } } diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr index c5b65e6f747..6f4b52fb887 100644 --- a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) retag write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_write.rs:LL:CC | LL | *p = 5; - | ^^^^^^ Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^ Data race detected between (1) retag write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_write.rs:LL:CC | LL | let _r = &mut *p; | ^^^^^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr index 62f139f6f08..fa0012f9b26 100644 --- a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_write.rs:LL:CC | LL | *p = 5; - | ^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^ Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_write.rs:LL:CC | LL | let _r = &mut *p; | ^^^^^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.rs b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.rs new file mode 100644 index 00000000000..89fdd2a01eb --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.rs @@ -0,0 +1,19 @@ +#![feature(core_intrinsics)] +#![feature(rustc_attrs)] + +use std::intrinsics::typed_swap; +use std::ptr::addr_of_mut; + +fn invalid_array() { + let mut a = [1_u8; 100]; + let mut b = [2_u8; 100]; + unsafe { + let a = addr_of_mut!(a).cast::<[bool; 100]>(); + let b = addr_of_mut!(b).cast::<[bool; 100]>(); + typed_swap(a, b); //~ERROR: constructing invalid value + } +} + +fn main() { + invalid_array(); +} diff --git a/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.stderr b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.stderr new file mode 100644 index 00000000000..15f01c1c095 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: constructing invalid value at [0]: encountered 0x02, but expected a boolean + --> $DIR/typed-swap-invalid-array.rs:LL:CC + | +LL | typed_swap(a, b); + | ^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered 0x02, but expected a boolean + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `invalid_array` at $DIR/typed-swap-invalid-array.rs:LL:CC +note: inside `main` + --> $DIR/typed-swap-invalid-array.rs:LL:CC + | +LL | invalid_array(); + | ^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.rs b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.rs new file mode 100644 index 00000000000..9d014a523f8 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.rs @@ -0,0 +1,19 @@ +#![feature(core_intrinsics)] +#![feature(rustc_attrs)] + +use std::intrinsics::typed_swap; +use std::ptr::addr_of_mut; + +fn invalid_scalar() { + let mut a = 1_u8; + let mut b = 2_u8; + unsafe { + let a = addr_of_mut!(a).cast::<bool>(); + let b = addr_of_mut!(b).cast::<bool>(); + typed_swap(a, b); //~ERROR: constructing invalid value + } +} + +fn main() { + invalid_scalar(); +} diff --git a/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.stderr b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.stderr new file mode 100644 index 00000000000..262ca202f9f --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: constructing invalid value: encountered 0x02, but expected a boolean + --> $DIR/typed-swap-invalid-scalar.rs:LL:CC + | +LL | typed_swap(a, b); + | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x02, but expected a boolean + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `invalid_scalar` at $DIR/typed-swap-invalid-scalar.rs:LL:CC +note: inside `main` + --> $DIR/typed-swap-invalid-scalar.rs:LL:CC + | +LL | invalid_scalar(); + | ^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs index 5db89c89b77..3de517055ec 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs @@ -13,7 +13,7 @@ fn main() { let ptr = ptr; // We do a protected mutable retag (but no write!) in this thread. fn retag(_x: &mut i32) {} - retag(unsafe { &mut *ptr.0 }); //~ERROR: Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-1` + retag(unsafe { &mut *ptr.0 }); //~ERROR: Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-1` }); // We do a read in the main thread. diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr index 2ce757013d5..47ae4b5d46d 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_protected_read.rs:LL:CC | LL | retag(unsafe { &mut *ptr.0 }); - | ^^^^^^^^^^^ Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^^^^^^ Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_protected_read.rs:LL:CC | LL | unsafe { ptr.0.read() }; | ^^^^^^^^^^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs index 01a2e9ac474..25c92ddf6ca 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs @@ -15,7 +15,7 @@ fn thread_1(p: SendPtr) { fn thread_2(p: SendPtr) { let p = p.0; unsafe { - *p = 5; //~ ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) non-atomic write on thread `unnamed-2` + *p = 5; //~ ERROR: Data race detected between (1) retag read on thread `unnamed-1` and (2) non-atomic write on thread `unnamed-2` } } diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr index d3c8d14e2a1..9fe9fbeda44 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_read.rs:LL:CC | LL | *p = 5; - | ^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^ Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_read.rs:LL:CC | LL | let _r = &*p; | ^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/src/tools/miri/tests/pass/intptrcast.rs b/src/tools/miri/tests/pass/intptrcast.rs index 370b09f512c..4e9fa12c181 100644 --- a/src/tools/miri/tests/pass/intptrcast.rs +++ b/src/tools/miri/tests/pass/intptrcast.rs @@ -149,6 +149,31 @@ fn functions() { } } +/// Example that should be UB but due to wildcard pointers being too permissive +/// we don't notice. +fn should_be_ub() { + let alloc1 = 1u8; + let alloc2 = 2u8; + // Expose both allocations + let addr1: usize = &alloc1 as *const u8 as usize; + let addr2: usize = &alloc2 as *const u8 as usize; + + // Cast addr1 back to a pointer. In Miri, this gives it Wildcard provenance. + let wildcard = addr1 as *const u8; + unsafe { + // Read through the wildcard + assert_eq!(*wildcard, 1); + // Offset the pointer to another allocation. + // Note that we are doing this arithmetic that does not require we stay within bounds of the allocation. + let wildcard = wildcard.wrapping_offset(addr2 as isize - addr1 as isize); + // This should report UB: + assert_eq!(*wildcard, 2); + // ... but it doesn't. A pointer's provenance specifies a single allocation that it is allowed to read from. + // And wrapping_offset only modifies the address, not the provenance. + // So which allocation is wildcard allowed to access? It cannot be both. + } +} + fn main() { cast(); cast_dangling(); @@ -162,4 +187,5 @@ fn main() { ptr_eq_integer(); zst_deref_of_dangling(); functions(); + should_be_ub(); } diff --git a/src/tools/miri/tests/pass/intrinsics-x86-pause-without-sse2.rs b/src/tools/miri/tests/pass/intrinsics-x86-pause-without-sse2.rs new file mode 100644 index 00000000000..c8b92fd5458 --- /dev/null +++ b/src/tools/miri/tests/pass/intrinsics-x86-pause-without-sse2.rs @@ -0,0 +1,25 @@ +// Ignore everything except x86 and x86_64 +// Any new targets that are added to CI should be ignored here. +// (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) +//@ignore-target-aarch64 +//@ignore-target-arm +//@ignore-target-avr +//@ignore-target-s390x +//@ignore-target-thumbv7em +//@ignore-target-wasm32 +//@compile-flags: -C target-feature=-sse2 + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; + +fn main() { + assert!(!is_x86_feature_detected!("sse2")); + + unsafe { + // This is a SSE2 intrinsic, but it behaves as a no-op when SSE2 + // is not available, so it is always safe to call. + _mm_pause(); + } +} diff --git a/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs b/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs index e636d6c8aaf..e0088b9eb24 100644 --- a/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs +++ b/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs @@ -54,6 +54,11 @@ mod tests { } } + fn test_mm_pause() { + unsafe { _mm_pause() } + } + test_mm_pause(); + #[target_feature(enable = "sse2")] unsafe fn test_mm_avg_epu8() { let (a, b) = (_mm_set1_epi8(3), _mm_set1_epi8(9)); diff --git a/src/tools/miri/tests/pass/main_fn.rs b/src/tools/miri/tests/pass/main_fn.rs deleted file mode 100644 index 4cdd034f30e..00000000000 --- a/src/tools/miri/tests/pass/main_fn.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod foo { - pub(crate) fn bar() {} -} - -use foo::bar as main; diff --git a/src/tools/miri/tests/pass/portable-simd.rs b/src/tools/miri/tests/pass/portable-simd.rs index 399913a757b..cdb441b450b 100644 --- a/src/tools/miri/tests/pass/portable-simd.rs +++ b/src/tools/miri/tests/pass/portable-simd.rs @@ -526,6 +526,23 @@ fn simd_intrinsics() { } } +fn simd_float_intrinsics() { + use intrinsics::*; + + // These are just smoke tests to ensure the intrinsics can be called. + unsafe { + let a = f32x4::splat(10.0); + simd_fsqrt(a); + simd_fsin(a); + simd_fcos(a); + simd_fexp(a); + simd_fexp2(a); + simd_flog(a); + simd_flog2(a); + simd_flog10(a); + } +} + fn simd_masked_loadstore() { // The buffer is deliberarely too short, so reading the last element would be UB. let buf = [3i32; 3]; @@ -559,5 +576,6 @@ fn main() { simd_gather_scatter(); simd_round(); simd_intrinsics(); + simd_float_intrinsics(); simd_masked_loadstore(); } diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 958aef69572..d8bb8c643d1 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -4,4 +4,5 @@ version = "0.0.0" edition = "2021" [dependencies] +object = "0.34.0" wasmparser = "0.118.2" diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 674860f8413..419b04231b5 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -2,14 +2,15 @@ use std::env; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; +pub use object; pub use wasmparser; pub fn out_dir() -> PathBuf { env::var_os("TMPDIR").unwrap().into() } -fn setup_common_build_cmd() -> Command { - let rustc = env::var("RUSTC").unwrap(); +fn setup_common_build_cmd(command: &str) -> Command { + let rustc = env::var(command).unwrap(); let mut cmd = Command::new(rustc); cmd.arg("--out-dir").arg(out_dir()).arg("-L").arg(out_dir()); cmd @@ -32,6 +33,10 @@ pub fn aux_build() -> AuxBuildInvocationBuilder { AuxBuildInvocationBuilder::new() } +pub fn rustdoc() -> Rustdoc { + Rustdoc::new() +} + #[derive(Debug)] pub struct RustcInvocationBuilder { cmd: Command, @@ -39,7 +44,7 @@ pub struct RustcInvocationBuilder { impl RustcInvocationBuilder { fn new() -> Self { - let cmd = setup_common_build_cmd(); + let cmd = setup_common_build_cmd("RUSTC"); Self { cmd } } @@ -73,7 +78,7 @@ pub struct AuxBuildInvocationBuilder { impl AuxBuildInvocationBuilder { fn new() -> Self { - let mut cmd = setup_common_build_cmd(); + let mut cmd = setup_common_build_cmd("RUSTC"); cmd.arg("--crate-type=lib"); Self { cmd } } @@ -96,6 +101,35 @@ impl AuxBuildInvocationBuilder { } } +#[derive(Debug)] +pub struct Rustdoc { + cmd: Command, +} + +impl Rustdoc { + fn new() -> Self { + let cmd = setup_common_build_cmd("RUSTDOC"); + Self { cmd } + } + + pub fn arg(&mut self, arg: &str) -> &mut Self { + self.cmd.arg(arg); + self + } + + #[track_caller] + pub fn run(&mut self) -> Output { + let caller_location = std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let output = self.cmd.output().unwrap(); + if !output.status.success() { + handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); + } + output + } +} + fn run_common(bin_name: &str) -> (Command, Output) { let target = env::var("TARGET").unwrap(); diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index c500b30b998..053afcc52d4 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -3,7 +3,7 @@ use std::cmp::min; use itertools::Itertools; use rustc_ast::token::{Delimiter, Lit, LitKind}; -use rustc_ast::{ast, ptr, token, ForLoopKind}; +use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind}; use rustc_span::{BytePos, Span}; use crate::chains::rewrite_chain; @@ -170,8 +170,8 @@ pub(crate) fn format_expr( } } } - ast::ExprKind::Match(ref cond, ref arms) => { - rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs) + ast::ExprKind::Match(ref cond, ref arms, kind) => { + rewrite_match(context, cond, arms, shape, expr.span, &expr.attrs, kind) } ast::ExprKind::Path(ref qself, ref path) => { rewrite_path(context, PathContext::Expr, qself, path, shape) @@ -625,7 +625,7 @@ pub(crate) fn rewrite_cond( shape: Shape, ) -> Option<String> { match expr.kind { - ast::ExprKind::Match(ref cond, _) => { + ast::ExprKind::Match(ref cond, _, MatchKind::Prefix) => { // `match `cond` {` let cond_shape = match context.config.indent_style() { IndentStyle::Visual => shape.shrink_left(6).and_then(|s| s.sub_width(2))?, diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs index 5a00984d4c0..e68903c8715 100644 --- a/src/tools/rustfmt/src/matches.rs +++ b/src/tools/rustfmt/src/matches.rs @@ -2,7 +2,7 @@ use std::iter::repeat; -use rustc_ast::{ast, ptr}; +use rustc_ast::{ast, ptr, MatchKind}; use rustc_span::{BytePos, Span}; use crate::comment::{combine_strs_with_missing_comments, rewrite_comment}; @@ -72,6 +72,7 @@ pub(crate) fn rewrite_match( shape: Shape, span: Span, attrs: &[ast::Attribute], + match_kind: MatchKind, ) -> Option<String> { // Do not take the rhs overhead from the upper expressions into account // when rewriting match condition. @@ -131,15 +132,27 @@ pub(crate) fn rewrite_match( } } else { let span_after_cond = mk_sp(cond.span.hi(), span.hi()); - Some(format!( - "match {}{}{{\n{}{}{}\n{}}}", - cond_str, - block_sep, - inner_attrs_str, - nested_indent_str, - rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?, - shape.indent.to_string(context.config), - )) + + match match_kind { + MatchKind::Prefix => Some(format!( + "match {}{}{{\n{}{}{}\n{}}}", + cond_str, + block_sep, + inner_attrs_str, + nested_indent_str, + rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?, + shape.indent.to_string(context.config), + )), + MatchKind::Postfix => Some(format!( + "{}.match{}{{\n{}{}{}\n{}}}", + cond_str, + block_sep, + inner_attrs_str, + nested_indent_str, + rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?, + shape.indent.to_string(context.config), + )), + } } } diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 7f576279432..47b48468a24 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -55,9 +55,10 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool { ast::PatKind::TupleStruct(_, ref path, ref subpats) => { path.segments.len() <= 1 && subpats.len() <= 1 } - ast::PatKind::Box(ref p) | ast::PatKind::Ref(ref p, _) | ast::PatKind::Paren(ref p) => { - is_short_pattern_inner(&*p) - } + ast::PatKind::Box(ref p) + | PatKind::Deref(ref p) + | ast::PatKind::Ref(ref p, _) + | ast::PatKind::Paren(ref p) => is_short_pattern_inner(&*p), PatKind::Or(ref pats) => pats.iter().all(|p| is_short_pattern_inner(p)), } } @@ -277,6 +278,7 @@ impl Rewrite for Pat { .rewrite(context, shape.offset_left(1)?.sub_width(1)?) .map(|inner_pat| format!("({})", inner_pat)), PatKind::Err(_) => None, + PatKind::Deref(_) => None, } } } diff --git a/src/tools/rustfmt/tests/source/postfix-match/pf-match.rs b/src/tools/rustfmt/tests/source/postfix-match/pf-match.rs new file mode 100644 index 00000000000..b2366723631 --- /dev/null +++ b/src/tools/rustfmt/tests/source/postfix-match/pf-match.rs @@ -0,0 +1,20 @@ +#![feature(postfix_match)] + +fn main() { + let val = Some(42); + + val.match { + Some(_) => 2, + _ => 1 + }; + + Some(2).match { + Some(_) => true, + None => false + }.match { + false => "ferris is cute", + true => "I turn cats in to petted cats", + }.match { + _ => (), + } +} \ No newline at end of file diff --git a/src/tools/rustfmt/tests/target/postfix-match/pf-match.rs b/src/tools/rustfmt/tests/target/postfix-match/pf-match.rs new file mode 100644 index 00000000000..f439f272623 --- /dev/null +++ b/src/tools/rustfmt/tests/target/postfix-match/pf-match.rs @@ -0,0 +1,20 @@ +#![feature(postfix_match)] + +fn main() { + let val = Some(42); + + val.match { + Some(_) => 2, + _ => 1, + }; + + Some(2).match { + Some(_) => true, + None => false, + }.match { + false => "ferris is cute", + true => "I turn cats in to petted cats", + }.match { + _ => (), + } +} diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 22927cffd1b..fa24447f699 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -183,9 +183,10 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) { } }); - // if an excluded file is renamed, it must be removed from this list + // if there are any file names remaining, they were moved on the fs. + // our data must remain up to date, so it must be removed from issues.txt // do this automatically on bless, otherwise issue a tidy error - if bless { + if bless && !remaining_issue_names.is_empty() { let issues_txt_header = r#" /* ============================================================ diff --git a/tests/assembly/option-nonzero-eq.rs b/tests/assembly/option-nonzero-eq.rs deleted file mode 100644 index b04cf63fd78..00000000000 --- a/tests/assembly/option-nonzero-eq.rs +++ /dev/null @@ -1,27 +0,0 @@ -//@ revisions: WIN LIN -//@ [WIN] only-windows -//@ [LIN] only-linux -//@ assembly-output: emit-asm -//@ compile-flags: --crate-type=lib -O -C llvm-args=-x86-asm-syntax=intel -//@ only-x86_64 -//@ ignore-sgx - -use std::cmp::Ordering; - -// CHECK-lABEL: ordering_eq: -#[no_mangle] -pub fn ordering_eq(l: Option<Ordering>, r: Option<Ordering>) -> bool { - // Linux (System V): first two arguments are rdi then rsi - // Windows: first two arguments are rcx then rdx - // Both use rax for the return value. - - // CHECK-NOT: mov - // CHECK-NOT: test - // CHECK-NOT: cmp - - // LIN: cmp dil, sil - // WIN: cmp cl, dl - // CHECK-NEXT: sete al - // CHECK-NEXT: ret - l == r -} diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs index bda77b5f09b..3563aec6d80 100644 --- a/tests/assembly/targets/targets-elf.rs +++ b/tests/assembly/targets/targets-elf.rs @@ -369,6 +369,9 @@ //@ revisions: riscv32im_unknown_none_elf //@ [riscv32im_unknown_none_elf] compile-flags: --target riscv32im-unknown-none-elf //@ [riscv32im_unknown_none_elf] needs-llvm-components: riscv +//@ revisions: riscv32ima_unknown_none_elf +//@ [riscv32ima_unknown_none_elf] compile-flags: --target riscv32ima-unknown-none-elf +//@ [riscv32ima_unknown_none_elf] needs-llvm-components: riscv //@ revisions: riscv32imac_esp_espidf //@ [riscv32imac_esp_espidf] compile-flags: --target riscv32imac-esp-espidf //@ [riscv32imac_esp_espidf] needs-llvm-components: riscv diff --git a/tests/assembly/x86_64-typed-swap.rs b/tests/assembly/x86_64-typed-swap.rs new file mode 100644 index 00000000000..95e87519e6c --- /dev/null +++ b/tests/assembly/x86_64-typed-swap.rs @@ -0,0 +1,53 @@ +//@ revisions: WIN LIN +//@ [WIN] only-windows +//@ [LIN] only-linux +//@ only-x86_64 +//@ assembly-output: emit-asm +//@ compile-flags: --crate-type=lib -O + +use std::arch::x86_64::__m128; +use std::mem::swap; + +// CHECK-LABEL: swap_i32: +#[no_mangle] +pub fn swap_i32(x: &mut i32, y: &mut i32) { + // CHECK: movl (%[[ARG1:.+]]), %[[T1:.+]] + // CHECK: movl (%[[ARG2:.+]]), %[[T2:.+]] + // CHECK: movl %[[T2]], (%[[ARG1]]) + // CHECK: movl %[[T1]], (%[[ARG2]]) + // CHECK: retq + swap(x, y) +} + +// CHECK-LABEL: swap_pair: +#[no_mangle] +pub fn swap_pair(x: &mut (i32, u32), y: &mut (i32, u32)) { + // CHECK: movq (%[[ARG1]]), %[[T1:.+]] + // CHECK: movq (%[[ARG2]]), %[[T2:.+]] + // CHECK: movq %[[T2]], (%[[ARG1]]) + // CHECK: movq %[[T1]], (%[[ARG2]]) + // CHECK: retq + swap(x, y) +} + +// CHECK-LABEL: swap_str: +#[no_mangle] +pub fn swap_str<'a>(x: &mut &'a str, y: &mut &'a str) { + // CHECK: movups (%[[ARG1]]), %[[T1:xmm.]] + // CHECK: movups (%[[ARG2]]), %[[T2:xmm.]] + // CHECK: movups %[[T2]], (%[[ARG1]]) + // CHECK: movups %[[T1]], (%[[ARG2]]) + // CHECK: retq + swap(x, y) +} + +// CHECK-LABEL: swap_simd: +#[no_mangle] +pub fn swap_simd(x: &mut __m128, y: &mut __m128) { + // CHECK: movaps (%[[ARG1]]), %[[T1:xmm.]] + // CHECK: movaps (%[[ARG2]]), %[[T2:xmm.]] + // CHECK: movaps %[[T2]], (%[[ARG1]]) + // CHECK: movaps %[[T1]], (%[[ARG2]]) + // CHECK: retq + swap(x, y) +} diff --git a/tests/codegen/intrinsics/typed_swap.rs b/tests/codegen/intrinsics/typed_swap.rs new file mode 100644 index 00000000000..b55fb8ee36f --- /dev/null +++ b/tests/codegen/intrinsics/typed_swap.rs @@ -0,0 +1,78 @@ +//@ revisions: OPT0 OPT3 +//@ [OPT0] compile-flags: -Copt-level=0 +//@ [OPT3] compile-flags: -Copt-level=3 +//@ compile-flags: -C no-prepopulate-passes +//@ only-64bit (so I don't need to worry about usize) +// ignore-tidy-linelength (the memcpy calls get long) + +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +use std::intrinsics::typed_swap; + +// CHECK-LABEL: @swap_unit( +#[no_mangle] +pub unsafe fn swap_unit(x: &mut (), y: &mut ()) { + // CHECK: start + // CHECK-NEXT: ret void + typed_swap(x, y) +} + +// CHECK-LABEL: @swap_i32( +#[no_mangle] +pub unsafe fn swap_i32(x: &mut i32, y: &mut i32) { + // CHECK-NOT: alloca + + // CHECK: %[[TEMP:.+]] = load i32, ptr %x, align 4 + // CHECK-SAME: !noundef + // OPT0: %[[TEMP2:.+]] = load i32, ptr %y, align 4 + // OPT0-SAME: !noundef + // OPT0: store i32 %[[TEMP2]], ptr %x, align 4 + // OPT0-NOT: memcpy + // OPT3-NOT: load + // OPT3: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x, ptr align 4 %y, i64 4, i1 false) + // CHECK: store i32 %[[TEMP]], ptr %y, align 4 + // CHECK: ret void + typed_swap(x, y) +} + +// CHECK-LABEL: @swap_pair( +#[no_mangle] +pub unsafe fn swap_pair(x: &mut (i32, u32), y: &mut (i32, u32)) { + // CHECK-NOT: alloca + + // CHECK: load i32 + // CHECK-SAME: !noundef + // CHECK: load i32 + // CHECK-SAME: !noundef + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x, ptr align 4 %y, i64 8, i1 false) + // CHECK: store i32 + // CHECK: store i32 + typed_swap(x, y) +} + +// CHECK-LABEL: @swap_str( +#[no_mangle] +pub unsafe fn swap_str<'a>(x: &mut &'a str, y: &mut &'a str) { + // CHECK-NOT: alloca + + // CHECK: load ptr + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK: load i64 + // CHECK-SAME: !noundef + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x, ptr align 8 %y, i64 16, i1 false) + // CHECK: store ptr + // CHECK: store i64 + typed_swap(x, y) +} + +// OPT0-LABEL: @swap_string( +#[no_mangle] +pub unsafe fn swap_string(x: &mut String, y: &mut String) { + // OPT0: %[[TEMP:.+]] = alloca {{.+}}, align 8 + // OPT0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[TEMP]], ptr align 8 %x, i64 24, i1 false) + // OPT0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x, ptr align 8 %y, i64 24, i1 false) + // OPT0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %y, ptr align 8 %[[TEMP]], i64 24, i1 false) + typed_swap(x, y) +} diff --git a/tests/codegen/option-nonzero-eq.rs b/tests/codegen/option-niche-eq.rs index f637b1aef97..8b8044e9b75 100644 --- a/tests/codegen/option-nonzero-eq.rs +++ b/tests/codegen/option-niche-eq.rs @@ -1,4 +1,5 @@ //@ compile-flags: -O -Zmerge-functions=disabled +//@ min-llvm-version: 18 #![crate_type = "lib"] #![feature(generic_nonzero)] @@ -7,9 +8,6 @@ use core::cmp::Ordering; use core::ptr::NonNull; use core::num::NonZero; -// See also tests/assembly/option-nonzero-eq.rs, for cases with `assume`s in the -// LLVM and thus don't optimize down clearly here, but do in assembly. - // CHECK-lABEL: @non_zero_eq #[no_mangle] pub fn non_zero_eq(l: Option<NonZero<u32>>, r: Option<NonZero<u32>>) -> bool { @@ -36,3 +34,42 @@ pub fn non_null_eq(l: Option<NonNull<u8>>, r: Option<NonNull<u8>>) -> bool { // CHECK-NEXT: ret i1 l == r } + +// CHECK-lABEL: @ordering_eq +#[no_mangle] +pub fn ordering_eq(l: Option<Ordering>, r: Option<Ordering>) -> bool { + // CHECK: start: + // CHECK-NEXT: icmp eq i8 + // CHECK-NEXT: ret i1 + l == r +} + +#[derive(PartialEq)] +pub enum EnumWithNiche { + A, + B, + C, + D, + E, + F, + G, +} + +// CHECK-lABEL: @niche_eq +#[no_mangle] +pub fn niche_eq(l: Option<EnumWithNiche>, r: Option<EnumWithNiche>) -> bool { + // CHECK: start: + // CHECK-NEXT: icmp eq i8 + // CHECK-NEXT: ret i1 + l == r +} + +// FIXME: This should work too +// // FIXME-CHECK-lABEL: @bool_eq +// #[no_mangle] +// pub fn bool_eq(l: Option<bool>, r: Option<bool>) -> bool { +// // FIXME-CHECK: start: +// // FIXME-CHECK-NEXT: icmp eq i8 +// // FIXME-CHECK-NEXT: ret i1 +// l == r +// } diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs index c5d8e0f22a2..ca781a99296 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs @@ -47,40 +47,42 @@ pub fn foo() where let _: Type4 = <Foo>::bar; } -pub fn foo1(_: Type1) { } +// Force arguments to be passed by using a reference. Otherwise, they may end up PassMode::Ignore + +pub fn foo1(_: &Type1) { } // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: Type1, _: Type1) { } +pub fn foo2(_: &Type1, _: &Type1) { } // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: Type1, _: Type1, _: Type1) { } +pub fn foo3(_: &Type1, _: &Type1, _: &Type1) { } // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: Type2) { } +pub fn foo4(_: &Type2) { } // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: Type2, _: Type2) { } +pub fn foo5(_: &Type2, _: &Type2) { } // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: Type2, _: Type2, _: Type2) { } +pub fn foo6(_: &Type2, _: &Type2, _: &Type2) { } // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: Type3) { } +pub fn foo7(_: &Type3) { } // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: Type3, _: Type3) { } +pub fn foo8(_: &Type3, _: &Type3) { } // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: Type3, _: Type3, _: Type3) { } +pub fn foo9(_: &Type3, _: &Type3, _: &Type3) { } // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: Type4) { } +pub fn foo10(_: &Type4) { } // CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: Type4, _: Type4) { } +pub fn foo11(_: &Type4, _: &Type4) { } // CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo12(_: Type4, _: Type4, _: Type4) { } +pub fn foo12(_: &Type4, _: &Type4, _: &Type4) { } // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barE"} -// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barS_E"} -// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barS_S_E"} -// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooE"} -// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooS_E"} -// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooS_S_E"} -// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooE"} -// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooS_E"} -// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooS_S_E"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barE"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barS_E"} -// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barS_S_E"} +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barEE"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barES0_E"} +// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barES0_S0_E"} +// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooEE"} +// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_E"} +// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_S0_E"} +// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooEE"} +// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_E"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_S0_E"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barEE"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_E"} +// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_S0_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs index 3a1a09150ea..38f507856bd 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs @@ -12,9 +12,9 @@ use core::ffi::*; pub fn foo1(_: ()) { } // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo2(_: (), _: c_void) { } -// CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo3(_: (), _: c_void, _: c_void) { } -// CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo4(_: *mut ()) { } // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo5(_: *mut (), _: *mut c_void) { } @@ -131,8 +131,6 @@ pub fn foo60(_: &str, _: &str, _: &str) { } // CHECK: define{{.*}}5foo60{{.*}}!type ![[TYPE60:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvvE"} -// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvvvE"} -// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvvvvE"} // CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvPvE"} // CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvPvS_E"} // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvPvS_S_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs index 0deda029c4b..6f47f5e3355 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs @@ -28,7 +28,7 @@ pub struct Type2<'a> { member3: &'a Type2<'a>, } -pub struct Bar; +pub struct Bar(i32); // repr(transparent) user-defined generic type #[repr(transparent)] diff --git a/tests/codegen/sanitizer/cfi/normalize-integers.rs b/tests/codegen/sanitizer/cfi/normalize-integers.rs index 210814eb9ae..801ed312be5 100644 --- a/tests/codegen/sanitizer/cfi/normalize-integers.rs +++ b/tests/codegen/sanitizer/cfi/normalize-integers.rs @@ -41,6 +41,6 @@ pub fn foo11(_: (), _: usize, _: usize, _: usize) { } // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64|u4i128}}E.normalized"} // CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64|u4i128}}S_E.normalized"} // CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64|u4i128}}S_S_E.normalized"} -// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvv{{u3u16|u3u32|u3u64|u4u128}}E.normalized"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvv{{u3u16|u3u32|u3u64|u4u128}}S_E.normalized"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvv{{u3u16|u3u32|u3u64|u4u128}}S_S_E.normalized"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64|u4u128}}E.normalized"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64|u4u128}}S_E.normalized"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64|u4u128}}S_S_E.normalized"} diff --git a/tests/codegen/swap-small-types.rs b/tests/codegen/swap-small-types.rs index 5fdf4a5804a..4dcfed2a53a 100644 --- a/tests/codegen/swap-small-types.rs +++ b/tests/codegen/swap-small-types.rs @@ -70,10 +70,7 @@ pub fn swap_slices<'a>(x: &mut &'a [u32], y: &mut &'a [u32]) { // CHECK-NOT: alloca // CHECK: load ptr // CHECK: load i64 - // CHECK: load ptr - // CHECK: load i64 - // CHECK: store ptr - // CHECK: store i64 + // CHECK: call void @llvm.memcpy.p0.p0.i64({{.+}}, i64 16, i1 false) // CHECK: store ptr // CHECK: store i64 swap(x, y) diff --git a/tests/coverage/let_else_loop.cov-map b/tests/coverage/let_else_loop.cov-map new file mode 100644 index 00000000000..b0cee300522 --- /dev/null +++ b/tests/coverage/let_else_loop.cov-map @@ -0,0 +1,30 @@ +Function name: let_else_loop::_if (unused) +Raw bytes (19): 0x[01, 01, 00, 03, 00, 16, 01, 01, 0c, 00, 02, 09, 00, 10, 00, 02, 09, 00, 10] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 3 +- Code(Zero) at (prev + 22, 1) to (start + 1, 12) +- Code(Zero) at (prev + 2, 9) to (start + 0, 16) +- Code(Zero) at (prev + 2, 9) to (start + 0, 16) + +Function name: let_else_loop::_loop_either_way (unused) +Raw bytes (19): 0x[01, 01, 00, 03, 00, 0f, 01, 01, 14, 00, 01, 1c, 00, 23, 00, 01, 05, 00, 0c] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 3 +- Code(Zero) at (prev + 15, 1) to (start + 1, 20) +- Code(Zero) at (prev + 1, 28) to (start + 0, 35) +- Code(Zero) at (prev + 1, 5) to (start + 0, 12) + +Function name: let_else_loop::loopy +Raw bytes (19): 0x[01, 01, 00, 03, 01, 09, 01, 01, 14, 00, 01, 1c, 00, 23, 05, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 9, 1) to (start + 1, 20) +- Code(Zero) at (prev + 1, 28) to (start + 0, 35) +- Code(Counter(1)) at (prev + 1, 1) to (start + 0, 2) + diff --git a/tests/coverage/let_else_loop.coverage b/tests/coverage/let_else_loop.coverage new file mode 100644 index 00000000000..d193c8ca1b5 --- /dev/null +++ b/tests/coverage/let_else_loop.coverage @@ -0,0 +1,35 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| | + LL| |// Regression test for <https://github.com/rust-lang/rust/issues/122738>. + LL| |// These code patterns should not trigger an ICE when allocating a physical + LL| |// counter to a node and also one of its in-edges, because that is allowed + LL| |// when the node contains a tight loop to itself. + LL| | + LL| 1|fn loopy(cond: bool) { + LL| 1| let true = cond else { loop {} }; + ^0 + LL| 1|} + LL| | + LL| |// Variant that also has `loop {}` on the success path. + LL| |// This isn't needed to catch the original ICE, but might help detect regressions. + LL| 0|fn _loop_either_way(cond: bool) { + LL| 0| let true = cond else { loop {} }; + LL| 0| loop {} + LL| |} + LL| | + LL| |// Variant using regular `if` instead of let-else. + LL| |// This doesn't trigger the original ICE, but might help detect regressions. + LL| 0|fn _if(cond: bool) { + LL| 0| if cond { + LL| 0| loop {} + LL| | } else { + LL| 0| loop {} + LL| | } + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | loopy(true); + LL| |} + diff --git a/tests/coverage/let_else_loop.rs b/tests/coverage/let_else_loop.rs new file mode 100644 index 00000000000..12e0aeabcab --- /dev/null +++ b/tests/coverage/let_else_loop.rs @@ -0,0 +1,33 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 + +// Regression test for <https://github.com/rust-lang/rust/issues/122738>. +// These code patterns should not trigger an ICE when allocating a physical +// counter to a node and also one of its in-edges, because that is allowed +// when the node contains a tight loop to itself. + +fn loopy(cond: bool) { + let true = cond else { loop {} }; +} + +// Variant that also has `loop {}` on the success path. +// This isn't needed to catch the original ICE, but might help detect regressions. +fn _loop_either_way(cond: bool) { + let true = cond else { loop {} }; + loop {} +} + +// Variant using regular `if` instead of let-else. +// This doesn't trigger the original ICE, but might help detect regressions. +fn _if(cond: bool) { + if cond { + loop {} + } else { + loop {} + } +} + +#[coverage(off)] +fn main() { + loopy(true); +} diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff index a958e5541fa..21cf745b680 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -60,7 +64,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff index b073e27729e..ee58a974480 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -60,7 +64,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb5, otherwise: bb3]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff index 0a9f339ddba..9fc9c8ed409 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -60,7 +64,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff index bbc791148af..30d93347afd 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -60,7 +64,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb5, otherwise: bb3]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff index 3a11677f6f0..3a46edbc849 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -62,7 +66,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff index 9e7e08866b9..3c71214c35f 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -62,7 +66,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb5, otherwise: bb3]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff index beadfbc07b6..4557e7b26d6 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -62,7 +66,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff index 9ea86956b83..5ab2d5e0fdc 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -62,7 +66,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb5, otherwise: bb3]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs index 9986d903501..617501217cf 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs @@ -1,6 +1,5 @@ //@ unit-test: DataflowConstProp //@ compile-flags: -Zmir-enable-passes=+GVN,+Inline -//@ ignore-debug assertions change the output MIR // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/funky_arms.rs b/tests/mir-opt/funky_arms.rs index 189cd7951fb..fc3691049eb 100644 --- a/tests/mir-opt/funky_arms.rs +++ b/tests/mir-opt/funky_arms.rs @@ -1,6 +1,5 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ compile-flags: --crate-type lib -Cdebug-assertions=no #![feature(flt2dec)] diff --git a/tests/mir-opt/inline/unchecked_shifts.rs b/tests/mir-opt/inline/unchecked_shifts.rs index 2fd18f3d5eb..12b00e76a11 100644 --- a/tests/mir-opt/inline/unchecked_shifts.rs +++ b/tests/mir-opt/inline/unchecked_shifts.rs @@ -2,7 +2,6 @@ #![crate_type = "lib"] #![feature(unchecked_shifts)] -//@ ignore-debug: the debug assertions prevent the inlining we are testing for //@ compile-flags: -Zmir-opt-level=2 -Zinline-mir // EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.diff diff --git a/tests/mir-opt/inline/unwrap_unchecked.rs b/tests/mir-opt/inline/unwrap_unchecked.rs index e44e4e23a2c..13c76c5bb53 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.rs +++ b/tests/mir-opt/inline/unwrap_unchecked.rs @@ -1,8 +1,7 @@ #![crate_type = "lib"] // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ ignore-debug: the debug assertions prevent the inlining we are testing for -//@ compile-flags: -Zmir-opt-level=2 -Zinline-mir -Cdebug-assertions=no +//@ compile-flags: -Zmir-opt-level=2 -Zinline-mir // EMIT_MIR unwrap_unchecked.unwrap_unchecked.Inline.diff // EMIT_MIR unwrap_unchecked.unwrap_unchecked.PreCodegen.after.mir diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff index 6f7853a3e97..028040edc85 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff @@ -17,6 +17,10 @@ + let _5: (); + scope 5 { + } ++ scope 6 (inlined core::ub_checks::check_language_ub) { ++ scope 7 (inlined core::ub_checks::check_language_ub::runtime) { ++ } ++ } + } + } + } @@ -37,7 +41,7 @@ + + bb2: { + StorageLive(_4); -+ _4 = UbCheck(LanguageUb); ++ _4 = UbChecks(); + assume(_4); + _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; + } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff index cac06d4af08..484fd37248c 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff @@ -17,6 +17,10 @@ + let _5: (); + scope 5 { + } ++ scope 6 (inlined core::ub_checks::check_language_ub) { ++ scope 7 (inlined core::ub_checks::check_language_ub::runtime) { ++ } ++ } + } + } + } @@ -41,7 +45,7 @@ - resume; + bb2: { + StorageLive(_4); -+ _4 = UbCheck(LanguageUb); ++ _4 = UbChecks(); + assume(_4); + _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; + } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir index 5c611650154..9cd7053871e 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir @@ -15,6 +15,10 @@ fn unwrap_unchecked(_1: Option<T>) -> T { let _4: (); scope 5 { } + scope 6 (inlined core::ub_checks::check_language_ub) { + scope 7 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -27,7 +31,7 @@ fn unwrap_unchecked(_1: Option<T>) -> T { bb1: { StorageLive(_3); - _3 = UbCheck(LanguageUb); + _3 = UbChecks(); assume(_3); _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir index 5c611650154..9cd7053871e 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir @@ -15,6 +15,10 @@ fn unwrap_unchecked(_1: Option<T>) -> T { let _4: (); scope 5 { } + scope 6 (inlined core::ub_checks::check_language_ub) { + scope 7 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -27,7 +31,7 @@ fn unwrap_unchecked(_1: Option<T>) -> T { bb1: { StorageLive(_3); - _3 = UbCheck(LanguageUb); + _3 = UbChecks(); assume(_3); _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/inline_coroutine_body.rs b/tests/mir-opt/inline_coroutine_body.rs new file mode 100644 index 00000000000..be73bc49de5 --- /dev/null +++ b/tests/mir-opt/inline_coroutine_body.rs @@ -0,0 +1,28 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +// skip-filecheck +//@ unit-test: Inline +//@ edition: 2021 +//@ compile-flags: -Zinline-mir-hint-threshold=10000 -Zinline-mir-threshold=10000 --crate-type=lib + +pub async fn run(permit: ActionPermit<'_, ()>, ctx: &mut core::task::Context<'_>) { + run2(permit, ctx); +} + +// EMIT_MIR inline_coroutine_body.run2-{closure#0}.Inline.diff +fn run2<T>(permit: ActionPermit<'_, T>, ctx: &mut core::task::Context) { + _ = || { + let mut fut = ActionPermit::perform(permit); + let fut = unsafe { core::pin::Pin::new_unchecked(&mut fut) }; + _ = core::future::Future::poll(fut, ctx); + }; +} + +pub struct ActionPermit<'a, T> { + _guard: core::cell::Ref<'a, T>, +} + +impl<'a, T> ActionPermit<'a, T> { + async fn perform(self) { + core::future::ready(()).await + } +} diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff new file mode 100644 index 00000000000..b189b4e73f4 --- /dev/null +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -0,0 +1,281 @@ +- // MIR for `run2::{closure#0}` before Inline ++ // MIR for `run2::{closure#0}` after Inline + + fn run2::{closure#0}(_1: {closure@$DIR/inline_coroutine_body.rs:13:9: 13:11}) -> () { + debug permit => (_1.0: ActionPermit<'_, T>); + debug ctx => (*(_1.1: &mut std::task::Context<'_>)); + let mut _0: (); + let mut _2: {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let mut _3: ActionPermit<'_, T>; + let mut _5: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let _6: (); + let mut _7: std::task::Poll<()>; + let mut _8: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + let mut _9: &mut std::task::Context<'_>; + let mut _10: &mut std::task::Context<'_>; + scope 1 { + debug fut => _2; + let _4: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + scope 2 { + debug fut => _4; + scope 4 { + } ++ scope 7 (inlined ActionPermit::<'_, T>::perform::{closure#0}) { ++ debug _task_context => _31; ++ debug self => ((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})).0: ActionPermit<'_, T>); ++ let _11: ActionPermit<'_, T>; ++ let mut _12: std::future::Ready<()>; ++ let mut _13: std::future::Ready<()>; ++ let mut _14: (); ++ let mut _16: (); ++ let _17: (); ++ let mut _18: std::task::Poll<()>; ++ let mut _19: std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _20: &mut std::future::Ready<()>; ++ let mut _21: &mut std::future::Ready<()>; ++ let mut _22: &mut std::task::Context<'_>; ++ let mut _23: &mut std::task::Context<'_>; ++ let mut _24: &mut std::task::Context<'_>; ++ let mut _25: isize; ++ let mut _27: !; ++ let mut _28: &mut std::task::Context<'_>; ++ let mut _29: (); ++ let mut _30: (); ++ let mut _31: &mut std::task::Context<'_>; ++ let mut _32: u32; ++ let mut _33: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _34: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _35: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _36: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _37: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _38: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _39: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _40: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ scope 8 { ++ debug self => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).0: ActionPermit<'_, T>); ++ let mut _15: std::future::Ready<()>; ++ scope 9 { ++ debug __awaitee => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).1: std::future::Ready<()>); ++ let _26: (); ++ scope 10 { ++ } ++ scope 11 { ++ debug result => _26; ++ } ++ } ++ scope 12 (inlined ready::<()>) { ++ debug t => _14; ++ let mut _41: std::option::Option<()>; ++ } ++ } ++ } + } + scope 3 { ++ scope 6 (inlined Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked) { ++ debug pointer => _5; ++ } + } + } ++ scope 5 (inlined ActionPermit::<'_, T>::perform) { ++ debug self => _3; ++ } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = move (_1.0: ActionPermit<'_, T>); +- _2 = ActionPermit::<'_, T>::perform(move _3) -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { ++ _2 = {coroutine@$DIR/inline_coroutine_body.rs:25:28: 27:6 (#0)} { self: move _3 }; + StorageDead(_3); + StorageLive(_4); + StorageLive(_5); + _5 = &mut _2; +- _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked(move _5) -> [return: bb2, unwind unreachable]; +- } +- +- bb2: { ++ _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}> { __pointer: _5 }; + StorageDead(_5); + StorageLive(_6); + StorageLive(_7); + StorageLive(_8); + _8 = move _4; + StorageLive(_9); + _10 = deref_copy (_1.1: &mut std::task::Context<'_>); + _9 = &mut (*_10); +- _7 = <{async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6} as Future>::poll(move _8, move _9) -> [return: bb3, unwind unreachable]; ++ StorageLive(_11); ++ StorageLive(_15); ++ StorageLive(_16); ++ StorageLive(_25); ++ StorageLive(_27); ++ StorageLive(_30); ++ StorageLive(_31); ++ StorageLive(_32); ++ StorageLive(_33); ++ StorageLive(_34); ++ StorageLive(_35); ++ StorageLive(_36); ++ StorageLive(_37); ++ StorageLive(_38); ++ StorageLive(_39); ++ StorageLive(_40); ++ _33 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _32 = discriminant((*_33)); ++ switchInt(move _32) -> [0: bb3, 1: bb13, 3: bb12, otherwise: bb8]; + } + +- bb3: { ++ bb1: { ++ StorageDead(_2); ++ return; ++ } ++ ++ bb2: { ++ StorageDead(_40); ++ StorageDead(_39); ++ StorageDead(_38); ++ StorageDead(_37); ++ StorageDead(_36); ++ StorageDead(_35); ++ StorageDead(_34); ++ StorageDead(_33); ++ StorageDead(_32); ++ StorageDead(_31); ++ StorageDead(_30); ++ StorageDead(_27); ++ StorageDead(_25); ++ StorageDead(_16); ++ StorageDead(_15); ++ StorageDead(_11); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + _6 = const (); + StorageDead(_6); + _0 = const (); + StorageDead(_4); +- drop(_2) -> [return: bb4, unwind unreachable]; ++ drop(_2) -> [return: bb1, unwind unreachable]; + } + ++ bb3: { ++ _31 = move _9; ++ _34 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _35 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ StorageLive(_12); ++ StorageLive(_13); ++ StorageLive(_14); ++ _14 = (); ++ StorageLive(_41); ++ _41 = Option::<()>::Some(_14); ++ _13 = std::future::Ready::<()>(move _41); ++ StorageDead(_41); ++ StorageDead(_14); ++ _12 = <std::future::Ready<()> as IntoFuture>::into_future(move _13) -> [return: bb4, unwind unreachable]; ++ } ++ + bb4: { +- StorageDead(_2); +- return; ++ StorageDead(_13); ++ _36 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ goto -> bb5; ++ } ++ ++ bb5: { ++ StorageLive(_17); ++ StorageLive(_18); ++ StorageLive(_19); ++ StorageLive(_20); ++ StorageLive(_21); ++ _37 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _20 = &mut (*_21); ++ _19 = Pin::<&mut std::future::Ready<()>>::new_unchecked(move _20) -> [return: bb6, unwind unreachable]; ++ } ++ ++ bb6: { ++ StorageDead(_20); ++ StorageLive(_22); ++ StorageLive(_23); ++ StorageLive(_24); ++ _24 = _31; ++ _23 = move _24; ++ _22 = &mut (*_23); ++ StorageDead(_24); ++ _18 = <std::future::Ready<()> as Future>::poll(move _19, move _22) -> [return: bb7, unwind unreachable]; ++ } ++ ++ bb7: { ++ StorageDead(_22); ++ StorageDead(_19); ++ _25 = discriminant(_18); ++ switchInt(move _25) -> [0: bb10, 1: bb9, otherwise: bb8]; ++ } ++ ++ bb8: { ++ unreachable; ++ } ++ ++ bb9: { ++ _17 = const (); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageLive(_28); ++ StorageLive(_29); ++ _29 = (); ++ _7 = Poll::<()>::Pending; ++ StorageDead(_12); ++ StorageDead(_28); ++ StorageDead(_29); ++ _38 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_38)) = 3; ++ goto -> bb2; ++ } ++ ++ bb10: { ++ StorageLive(_26); ++ _26 = ((_18 as Ready).0: ()); ++ _30 = _26; ++ StorageDead(_26); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageDead(_12); ++ _39 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb11, unwind unreachable]; ++ } ++ ++ bb11: { ++ _7 = Poll::<()>::Ready(move _30); ++ _40 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_40)) = 1; ++ goto -> bb2; ++ } ++ ++ bb12: { ++ StorageLive(_12); ++ StorageLive(_28); ++ StorageLive(_29); ++ _28 = move _9; ++ StorageDead(_29); ++ _31 = move _28; ++ StorageDead(_28); ++ _16 = const (); ++ goto -> bb5; ++ } ++ ++ bb13: { ++ assert(const false, "`async fn` resumed after completion") -> [success: bb13, unwind unreachable]; + } + } + diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff new file mode 100644 index 00000000000..ed18c0a3adb --- /dev/null +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -0,0 +1,341 @@ +- // MIR for `run2::{closure#0}` before Inline ++ // MIR for `run2::{closure#0}` after Inline + + fn run2::{closure#0}(_1: {closure@$DIR/inline_coroutine_body.rs:13:9: 13:11}) -> () { + debug permit => (_1.0: ActionPermit<'_, T>); + debug ctx => (*(_1.1: &mut std::task::Context<'_>)); + let mut _0: (); + let mut _2: {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let mut _3: ActionPermit<'_, T>; + let mut _5: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let _6: (); + let mut _7: std::task::Poll<()>; + let mut _8: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + let mut _9: &mut std::task::Context<'_>; + let mut _10: &mut std::task::Context<'_>; + scope 1 { + debug fut => _2; + let _4: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + scope 2 { + debug fut => _4; + scope 4 { + } ++ scope 7 (inlined ActionPermit::<'_, T>::perform::{closure#0}) { ++ debug _task_context => _31; ++ debug self => ((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})).0: ActionPermit<'_, T>); ++ let _11: ActionPermit<'_, T>; ++ let mut _12: std::future::Ready<()>; ++ let mut _13: std::future::Ready<()>; ++ let mut _14: (); ++ let mut _16: (); ++ let _17: (); ++ let mut _18: std::task::Poll<()>; ++ let mut _19: std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _20: &mut std::future::Ready<()>; ++ let mut _21: &mut std::future::Ready<()>; ++ let mut _22: &mut std::task::Context<'_>; ++ let mut _23: &mut std::task::Context<'_>; ++ let mut _24: &mut std::task::Context<'_>; ++ let mut _25: isize; ++ let mut _27: !; ++ let mut _28: &mut std::task::Context<'_>; ++ let mut _29: (); ++ let mut _30: (); ++ let mut _31: &mut std::task::Context<'_>; ++ let mut _32: u32; ++ let mut _33: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _34: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _35: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _36: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _37: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _38: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _39: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _40: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _41: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _42: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ scope 8 { ++ debug self => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).0: ActionPermit<'_, T>); ++ let mut _15: std::future::Ready<()>; ++ scope 9 { ++ debug __awaitee => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).1: std::future::Ready<()>); ++ let _26: (); ++ scope 10 { ++ } ++ scope 11 { ++ debug result => _26; ++ } ++ } ++ scope 12 (inlined ready::<()>) { ++ debug t => _14; ++ let mut _43: std::option::Option<()>; ++ } ++ } ++ } + } + scope 3 { ++ scope 6 (inlined Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked) { ++ debug pointer => _5; ++ } + } + } ++ scope 5 (inlined ActionPermit::<'_, T>::perform) { ++ debug self => _3; ++ } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = move (_1.0: ActionPermit<'_, T>); +- _2 = ActionPermit::<'_, T>::perform(move _3) -> [return: bb1, unwind: bb6]; +- } +- +- bb1: { ++ _2 = {coroutine@$DIR/inline_coroutine_body.rs:25:28: 27:6 (#0)} { self: move _3 }; + StorageDead(_3); + StorageLive(_4); + StorageLive(_5); + _5 = &mut _2; +- _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked(move _5) -> [return: bb2, unwind: bb5]; +- } +- +- bb2: { ++ _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}> { __pointer: _5 }; + StorageDead(_5); + StorageLive(_6); + StorageLive(_7); + StorageLive(_8); + _8 = move _4; + StorageLive(_9); + _10 = deref_copy (_1.1: &mut std::task::Context<'_>); + _9 = &mut (*_10); +- _7 = <{async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6} as Future>::poll(move _8, move _9) -> [return: bb3, unwind: bb5]; ++ StorageLive(_11); ++ StorageLive(_15); ++ StorageLive(_16); ++ StorageLive(_25); ++ StorageLive(_27); ++ StorageLive(_30); ++ StorageLive(_31); ++ StorageLive(_32); ++ StorageLive(_33); ++ StorageLive(_34); ++ StorageLive(_35); ++ StorageLive(_36); ++ StorageLive(_37); ++ StorageLive(_38); ++ StorageLive(_39); ++ StorageLive(_40); ++ StorageLive(_41); ++ StorageLive(_42); ++ _33 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _32 = discriminant((*_33)); ++ switchInt(move _32) -> [0: bb5, 1: bb22, 2: bb21, 3: bb20, otherwise: bb10]; + } + +- bb3: { ++ bb1: { ++ StorageDead(_2); ++ return; ++ } ++ ++ bb2 (cleanup): { ++ drop(_2) -> [return: bb3, unwind terminate(cleanup)]; ++ } ++ ++ bb3 (cleanup): { ++ resume; ++ } ++ ++ bb4: { ++ StorageDead(_42); ++ StorageDead(_41); ++ StorageDead(_40); ++ StorageDead(_39); ++ StorageDead(_38); ++ StorageDead(_37); ++ StorageDead(_36); ++ StorageDead(_35); ++ StorageDead(_34); ++ StorageDead(_33); ++ StorageDead(_32); ++ StorageDead(_31); ++ StorageDead(_30); ++ StorageDead(_27); ++ StorageDead(_25); ++ StorageDead(_16); ++ StorageDead(_15); ++ StorageDead(_11); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + _6 = const (); + StorageDead(_6); + _0 = const (); + StorageDead(_4); +- drop(_2) -> [return: bb4, unwind: bb6]; ++ drop(_2) -> [return: bb1, unwind: bb3]; + } + +- bb4: { +- StorageDead(_2); +- return; ++ bb5: { ++ _31 = move _9; ++ _34 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _35 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ StorageLive(_12); ++ StorageLive(_13); ++ StorageLive(_14); ++ _14 = (); ++ StorageLive(_43); ++ _43 = Option::<()>::Some(_14); ++ _13 = std::future::Ready::<()>(move _43); ++ StorageDead(_43); ++ StorageDead(_14); ++ _12 = <std::future::Ready<()> as IntoFuture>::into_future(move _13) -> [return: bb6, unwind: bb17]; + } + +- bb5 (cleanup): { +- drop(_2) -> [return: bb6, unwind terminate(cleanup)]; ++ bb6: { ++ StorageDead(_13); ++ _36 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ goto -> bb7; + } + +- bb6 (cleanup): { +- resume; ++ bb7: { ++ StorageLive(_17); ++ StorageLive(_18); ++ StorageLive(_19); ++ StorageLive(_20); ++ StorageLive(_21); ++ _37 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _20 = &mut (*_21); ++ _19 = Pin::<&mut std::future::Ready<()>>::new_unchecked(move _20) -> [return: bb8, unwind: bb15]; ++ } ++ ++ bb8: { ++ StorageDead(_20); ++ StorageLive(_22); ++ StorageLive(_23); ++ StorageLive(_24); ++ _24 = _31; ++ _23 = move _24; ++ _22 = &mut (*_23); ++ StorageDead(_24); ++ _18 = <std::future::Ready<()> as Future>::poll(move _19, move _22) -> [return: bb9, unwind: bb14]; ++ } ++ ++ bb9: { ++ StorageDead(_22); ++ StorageDead(_19); ++ _25 = discriminant(_18); ++ switchInt(move _25) -> [0: bb12, 1: bb11, otherwise: bb10]; ++ } ++ ++ bb10: { ++ unreachable; ++ } ++ ++ bb11: { ++ _17 = const (); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageLive(_28); ++ StorageLive(_29); ++ _29 = (); ++ _7 = Poll::<()>::Pending; ++ StorageDead(_12); ++ StorageDead(_28); ++ StorageDead(_29); ++ _38 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_38)) = 3; ++ goto -> bb4; ++ } ++ ++ bb12: { ++ StorageLive(_26); ++ _26 = ((_18 as Ready).0: ()); ++ _30 = _26; ++ StorageDead(_26); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageDead(_12); ++ _39 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb13, unwind: bb19]; ++ } ++ ++ bb13: { ++ _7 = Poll::<()>::Ready(move _30); ++ _40 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_40)) = 1; ++ goto -> bb4; ++ } ++ ++ bb14 (cleanup): { ++ StorageDead(_22); ++ StorageDead(_19); ++ StorageDead(_23); ++ goto -> bb16; ++ } ++ ++ bb15 (cleanup): { ++ StorageDead(_20); ++ StorageDead(_19); ++ goto -> bb16; ++ } ++ ++ bb16 (cleanup): { ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ goto -> bb18; ++ } ++ ++ bb17 (cleanup): { ++ StorageDead(_13); ++ goto -> bb18; ++ } ++ ++ bb18 (cleanup): { ++ StorageDead(_12); ++ _41 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ drop((((*_41) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb19, unwind terminate(cleanup)]; ++ } ++ ++ bb19 (cleanup): { ++ _42 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_42)) = 2; ++ goto -> bb2; ++ } ++ ++ bb20: { ++ StorageLive(_12); ++ StorageLive(_28); ++ StorageLive(_29); ++ _28 = move _9; ++ StorageDead(_29); ++ _31 = move _28; ++ StorageDead(_28); ++ _16 = const (); ++ goto -> bb7; ++ } ++ ++ bb21: { ++ assert(const false, "`async fn` resumed after panicking") -> [success: bb21, unwind: bb2]; ++ } ++ ++ bb22: { ++ assert(const false, "`async fn` resumed after completion") -> [success: bb22, unwind: bb2]; + } + } + diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff b/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff new file mode 100644 index 00000000000..ff65ca77039 --- /dev/null +++ b/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff @@ -0,0 +1,56 @@ +- // MIR for `main` before CleanupPostBorrowck ++ // MIR for `main` after CleanupPostBorrowck + + fn main() -> () { + let mut _0: (); + let mut _1: bool; + + coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => /the/src/instrument_coverage_cleanup.rs:15:8: 15:36 (#0) + + coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) }; + coverage ExpressionId(1) => Expression { lhs: Counter(1), op: Add, rhs: Expression(0) }; + coverage Code(Counter(0)) => /the/src/instrument_coverage_cleanup.rs:14:1 - 15:36; + coverage Code(Expression(0)) => /the/src/instrument_coverage_cleanup.rs:15:37 - 15:39; + coverage Code(Counter(1)) => /the/src/instrument_coverage_cleanup.rs:15:39 - 15:40; + coverage Code(Expression(1)) => /the/src/instrument_coverage_cleanup.rs:16:1 - 16:2; + coverage Branch { true_term: Expression(0), false_term: Counter(1) } => /the/src/instrument_coverage_cleanup.rs:15:8 - 15:36; + + bb0: { + Coverage::CounterIncrement(0); +- Coverage::SpanMarker; ++ nop; + StorageLive(_1); + _1 = std::hint::black_box::<bool>(const true) -> [return: bb1, unwind: bb5]; + } + + bb1: { + switchInt(move _1) -> [0: bb3, otherwise: bb2]; + } + + bb2: { + Coverage::CounterIncrement(1); +- Coverage::BlockMarker(1); ++ nop; + _0 = const (); + goto -> bb4; + } + + bb3: { + Coverage::ExpressionUsed(0); +- Coverage::BlockMarker(0); ++ nop; + _0 = const (); + goto -> bb4; + } + + bb4: { + Coverage::ExpressionUsed(1); + StorageDead(_1); + return; + } + + bb5 (cleanup): { + resume; + } + } + diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff b/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff new file mode 100644 index 00000000000..8757559149a --- /dev/null +++ b/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff @@ -0,0 +1,53 @@ +- // MIR for `main` before InstrumentCoverage ++ // MIR for `main` after InstrumentCoverage + + fn main() -> () { + let mut _0: (); + let mut _1: bool; + + coverage branch { true: BlockMarkerId(0), false: BlockMarkerId(1) } => /the/src/instrument_coverage_cleanup.rs:15:8: 15:36 (#0) + ++ coverage ExpressionId(0) => Expression { lhs: Counter(0), op: Subtract, rhs: Counter(1) }; ++ coverage ExpressionId(1) => Expression { lhs: Counter(1), op: Add, rhs: Expression(0) }; ++ coverage Code(Counter(0)) => /the/src/instrument_coverage_cleanup.rs:14:1 - 15:36; ++ coverage Code(Expression(0)) => /the/src/instrument_coverage_cleanup.rs:15:37 - 15:39; ++ coverage Code(Counter(1)) => /the/src/instrument_coverage_cleanup.rs:15:39 - 15:40; ++ coverage Code(Expression(1)) => /the/src/instrument_coverage_cleanup.rs:16:1 - 16:2; ++ coverage Branch { true_term: Expression(0), false_term: Counter(1) } => /the/src/instrument_coverage_cleanup.rs:15:8 - 15:36; ++ + bb0: { ++ Coverage::CounterIncrement(0); + Coverage::SpanMarker; + StorageLive(_1); + _1 = std::hint::black_box::<bool>(const true) -> [return: bb1, unwind: bb5]; + } + + bb1: { + switchInt(move _1) -> [0: bb3, otherwise: bb2]; + } + + bb2: { ++ Coverage::CounterIncrement(1); + Coverage::BlockMarker(1); + _0 = const (); + goto -> bb4; + } + + bb3: { ++ Coverage::ExpressionUsed(0); + Coverage::BlockMarker(0); + _0 = const (); + goto -> bb4; + } + + bb4: { ++ Coverage::ExpressionUsed(1); + StorageDead(_1); + return; + } + + bb5 (cleanup): { + resume; + } + } + diff --git a/tests/mir-opt/instrument_coverage_cleanup.rs b/tests/mir-opt/instrument_coverage_cleanup.rs new file mode 100644 index 00000000000..8a2fd67139b --- /dev/null +++ b/tests/mir-opt/instrument_coverage_cleanup.rs @@ -0,0 +1,22 @@ +// Test that CleanupPostBorrowck cleans up the marker statements that are +// inserted during MIR building (after InstrumentCoverage is done with them), +// but leaves the statements that were added by InstrumentCoverage. +// +// Removed statement kinds: BlockMarker, SpanMarker +// Retained statement kinds: CounterIncrement, ExpressionUsed + +//@ unit-test: InstrumentCoverage +//@ compile-flags: -Cinstrument-coverage -Zcoverage-options=branch -Zno-profiler-runtime +//@ compile-flags: --remap-path-prefix={{src-base}}=/the/src + +// EMIT_MIR instrument_coverage_cleanup.main.InstrumentCoverage.diff +// EMIT_MIR instrument_coverage_cleanup.main.CleanupPostBorrowck.diff +fn main() { + if !core::hint::black_box(true) {} +} + +// CHECK-NOT: Coverage::BlockMarker +// CHECK-NOT: Coverage::SpanMarker +// CHECK: Coverage::CounterIncrement +// CHECK-NOT: Coverage::BlockMarker +// CHECK-NOT: Coverage::SpanMarker diff --git a/tests/mir-opt/pre-codegen/duplicate_switch_targets.rs b/tests/mir-opt/pre-codegen/duplicate_switch_targets.rs index 67540676f4a..561bafa9651 100644 --- a/tests/mir-opt/pre-codegen/duplicate_switch_targets.rs +++ b/tests/mir-opt/pre-codegen/duplicate_switch_targets.rs @@ -1,6 +1,5 @@ // skip-filecheck //@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=0 -//@ ignore-debug: standard library debug assertions add a panic that breaks this optimization #![crate_type = "lib"] diff --git a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir index 0597e453e22..455e4ba7244 100644 --- a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir @@ -9,6 +9,10 @@ fn ub_if_b(_1: Thing) -> Thing { let _4: (); scope 2 { } + scope 3 (inlined core::ub_checks::check_language_ub) { + scope 4 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } bb0: { @@ -23,7 +27,7 @@ fn ub_if_b(_1: Thing) -> Thing { bb2: { StorageLive(_3); - _3 = UbCheck(LanguageUb); + _3 = UbChecks(); assume(_3); _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/pre-codegen/mem_replace.rs b/tests/mir-opt/pre-codegen/mem_replace.rs index 9cb3a839956..a68fe31f609 100644 --- a/tests/mir-opt/pre-codegen/mem_replace.rs +++ b/tests/mir-opt/pre-codegen/mem_replace.rs @@ -1,6 +1,6 @@ // skip-filecheck //@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -Zinline-mir -//@ ignore-debug the standard library debug assertions leak into this test +//@ ignore-debug: precondition checks on ptr::read/write are under cfg(debug_assertions) // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![crate_type = "lib"] diff --git a/tests/mir-opt/pre-codegen/slice_index.rs b/tests/mir-opt/pre-codegen/slice_index.rs index 1d977ee9214..c9dd72d8be2 100644 --- a/tests/mir-opt/pre-codegen/slice_index.rs +++ b/tests/mir-opt/pre-codegen/slice_index.rs @@ -1,6 +1,5 @@ // skip-filecheck //@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -//@ ignore-debug the standard library debug assertions leak into this test // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![crate_type = "lib"] diff --git a/tests/mir-opt/pre-codegen/slice_iter.rs b/tests/mir-opt/pre-codegen/slice_iter.rs index 0fbd3706544..86f37ca4d13 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.rs +++ b/tests/mir-opt/pre-codegen/slice_iter.rs @@ -1,6 +1,5 @@ // skip-filecheck //@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -//@ ignore-debug the standard library debug assertions leak into this test // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![crate_type = "lib"] diff --git a/tests/pretty/postfix-match.rs b/tests/pretty/postfix-match.rs new file mode 100644 index 00000000000..5bb54e15275 --- /dev/null +++ b/tests/pretty/postfix-match.rs @@ -0,0 +1,21 @@ +#![feature(postfix_match)] + +fn main() { + let val = Some(42); + + val.match { + Some(_) => 2, + _ => 1 + }; + + + Some(2).match { + Some(_) => true, + None => false + }.match { + false => "ferris is cute", + true => "I turn cats in to petted cats", + }.match { + _ => (), + } +} diff --git a/tests/run-make-fulldeps/obtain-borrowck/driver.rs b/tests/run-make-fulldeps/obtain-borrowck/driver.rs index 2e3bf70e144..e67ec8690f8 100644 --- a/tests/run-make-fulldeps/obtain-borrowck/driver.rs +++ b/tests/run-make-fulldeps/obtain-borrowck/driver.rs @@ -68,7 +68,7 @@ impl rustc_driver::Callbacks for CompilerCalls { let mut bodies = Vec::new(); let crate_items = tcx.hir_crate_items(()); - for id in crate_items.items() { + for id in crate_items.free_items() { if matches!(tcx.def_kind(id.owner_id), DefKind::Fn) { bodies.push(id.owner_id); } diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs new file mode 100644 index 00000000000..e7a5e8addbe --- /dev/null +++ b/tests/run-make/compiler-builtins/rmake.rs @@ -0,0 +1,142 @@ +//! The compiler_builtins library is special. It can call functions in core, but it must not +//! require linkage against a build of core. If it ever does, building the standard library *may* +//! result in linker errors, depending on whether the linker in use applies optimizations first or +//! resolves symbols first. So the portable and safe approach is to forbid such a linkage +//! requirement entirely. +//! +//! In addition, whether compiler_builtins requires linkage against core can depend on optimization +//! settings. Turning off optimizations and enabling debug assertions tends to produce the most +//! dependence on core that is possible, so that is the configuration we test here. + +#![deny(warnings)] + +extern crate run_make_support; + +use run_make_support::object; +use run_make_support::object::read::archive::ArchiveFile; +use run_make_support::object::read::Object; +use run_make_support::object::ObjectSection; +use run_make_support::object::ObjectSymbol; +use run_make_support::object::RelocationTarget; +use run_make_support::out_dir; +use std::collections::HashSet; + +const MANIFEST: &str = r#" +[package] +name = "scratch" +version = "0.1.0" +edition = "2021" + +[lib] +path = "lib.rs""#; + +fn main() { + let target_dir = out_dir().join("target"); + let target = std::env::var("TARGET").unwrap(); + if target.starts_with("wasm") || target.starts_with("nvptx") { + // wasm and nvptx targets don't produce rlib files that object can parse. + return; + } + + println!("Testing compiler_builtins for {}", target); + + // Set up the tiniest Cargo project: An empty no_std library. Just enough to run -Zbuild-std. + let manifest_path = out_dir().join("Cargo.toml"); + std::fs::write(&manifest_path, MANIFEST.as_bytes()).unwrap(); + std::fs::write(out_dir().join("lib.rs"), b"#![no_std]").unwrap(); + + let path = std::env::var("PATH").unwrap(); + let rustc = std::env::var("RUSTC").unwrap(); + let bootstrap_cargo = std::env::var("BOOTSTRAP_CARGO").unwrap(); + let status = std::process::Command::new(bootstrap_cargo) + .args([ + "build", + "--manifest-path", + manifest_path.to_str().unwrap(), + "-Zbuild-std=core", + "--target", + &target, + ]) + .env_clear() + .env("PATH", path) + .env("RUSTC", rustc) + .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes") + .env("CARGO_TARGET_DIR", &target_dir) + .env("RUSTC_BOOTSTRAP", "1") + .status() + .unwrap(); + + assert!(status.success()); + + let rlibs_path = target_dir.join(target).join("debug").join("deps"); + let compiler_builtins_rlib = std::fs::read_dir(rlibs_path) + .unwrap() + .find_map(|e| { + let path = e.unwrap().path(); + let file_name = path.file_name().unwrap().to_str().unwrap(); + if file_name.starts_with("libcompiler_builtins") && file_name.ends_with(".rlib") { + Some(path) + } else { + None + } + }) + .unwrap(); + + // rlib files are archives, where the archive members each a CGU, and we also have one called + // lib.rmeta which is the encoded metadata. Each of the CGUs is an object file. + let data = std::fs::read(compiler_builtins_rlib).unwrap(); + + let mut defined_symbols = HashSet::new(); + let mut undefined_relocations = HashSet::new(); + + let archive = ArchiveFile::parse(&*data).unwrap(); + for member in archive.members() { + let member = member.unwrap(); + if member.name() == b"lib.rmeta" { + continue; + } + let data = member.data(&*data).unwrap(); + let object = object::File::parse(&*data).unwrap(); + + // Record all defined symbols in this CGU. + for symbol in object.symbols() { + if !symbol.is_undefined() { + let name = symbol.name().unwrap(); + defined_symbols.insert(name); + } + } + + // Find any relocations against undefined symbols. Calls within this CGU are relocations + // against a defined symbol. + for (_offset, relocation) in object.sections().flat_map(|section| section.relocations()) { + let RelocationTarget::Symbol(symbol_index) = relocation.target() else { + continue; + }; + let symbol = object.symbol_by_index(symbol_index).unwrap(); + if symbol.is_undefined() { + let name = symbol.name().unwrap(); + undefined_relocations.insert(name); + } + } + } + + // We can have symbols in the compiler_builtins rlib that are actually from core, if they were + // monomorphized in the compiler_builtins crate. This is totally fine, because though the call + // is to a function in core, it's resolved internally. + // + // It is normal to have relocations against symbols not defined in the rlib for things like + // unwinding, or math functions provided the target's platform libraries. Finding these is not + // a problem, we want to specifically ban relocations against core which are not resolved + // internally. + undefined_relocations + .retain(|symbol| !defined_symbols.contains(symbol) && symbol.contains("core")); + + if !undefined_relocations.is_empty() { + panic!( + "compiler_builtins must not link against core, but it does. \n\ + These symbols may be undefined in a debug build of compiler_builtins:\n\ + {:?}", + undefined_relocations + ); + } +} diff --git a/tests/run-make/rustdoc-test-args/foo.rs b/tests/run-make/rustdoc-test-args/foo.rs new file mode 100644 index 00000000000..51d17849fd7 --- /dev/null +++ b/tests/run-make/rustdoc-test-args/foo.rs @@ -0,0 +1,3 @@ +//! ``` +//! let x = 12; +//! ``` diff --git a/tests/run-make/rustdoc-test-args/rmake.rs b/tests/run-make/rustdoc-test-args/rmake.rs new file mode 100644 index 00000000000..808d13928eb --- /dev/null +++ b/tests/run-make/rustdoc-test-args/rmake.rs @@ -0,0 +1,18 @@ +extern crate run_make_support; + +use run_make_support::{out_dir, rustdoc}; +use std::{fs, iter}; +use std::path::Path; + +fn generate_a_lot_of_cfgs(path: &Path) { + let content = iter::repeat("--cfg=a\n").take(100_000).collect::<String>(); + fs::write(path, content.as_bytes()).expect("failed to create args file"); +} + +fn main() { + let arg_file = out_dir().join("args"); + generate_a_lot_of_cfgs(&arg_file); + + let arg_file = format!("@{}", arg_file.display()); + rustdoc().arg("--test").arg(&arg_file).arg("foo.rs").run(); +} diff --git a/tests/rustdoc-ui/invalid_associated_const.rs b/tests/rustdoc-ui/invalid_associated_const.rs index 6ab8c36f740..6f211a383a6 100644 --- a/tests/rustdoc-ui/invalid_associated_const.rs +++ b/tests/rustdoc-ui/invalid_associated_const.rs @@ -3,6 +3,7 @@ trait T { type A: S<C<X = 0i32> = 34>; //~^ ERROR associated type bindings are not allowed here + //~| ERROR associated type bindings are not allowed here } trait S { diff --git a/tests/rustdoc-ui/invalid_associated_const.stderr b/tests/rustdoc-ui/invalid_associated_const.stderr index 9c6ae0f76c6..1eb6d2714e3 100644 --- a/tests/rustdoc-ui/invalid_associated_const.stderr +++ b/tests/rustdoc-ui/invalid_associated_const.stderr @@ -4,6 +4,14 @@ error[E0229]: associated type bindings are not allowed here LL | type A: S<C<X = 0i32> = 34>; | ^^^^^^^^ associated type not allowed here -error: aborting due to 1 previous error +error[E0229]: associated type bindings are not allowed here + --> $DIR/invalid_associated_const.rs:4:17 + | +LL | type A: S<C<X = 0i32> = 34>; + | ^^^^^^^^ associated type not allowed here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0229`. diff --git a/tests/rustdoc-ui/issue-102467.rs b/tests/rustdoc-ui/issue-102467.rs index bff876e41d6..a27e6156979 100644 --- a/tests/rustdoc-ui/issue-102467.rs +++ b/tests/rustdoc-ui/issue-102467.rs @@ -6,6 +6,7 @@ trait T { type A: S<C<X = 0i32> = 34>; //~^ ERROR associated type bindings are not allowed here + //~| ERROR associated type bindings are not allowed here } trait S { diff --git a/tests/rustdoc-ui/issue-102467.stderr b/tests/rustdoc-ui/issue-102467.stderr index 4a769f94cf2..f54a50a4e19 100644 --- a/tests/rustdoc-ui/issue-102467.stderr +++ b/tests/rustdoc-ui/issue-102467.stderr @@ -4,6 +4,14 @@ error[E0229]: associated type bindings are not allowed here LL | type A: S<C<X = 0i32> = 34>; | ^^^^^^^^ associated type not allowed here -error: aborting due to 1 previous error +error[E0229]: associated type bindings are not allowed here + --> $DIR/issue-102467.rs:7:17 + | +LL | type A: S<C<X = 0i32> = 34>; + | ^^^^^^^^ associated type not allowed here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0229`. diff --git a/tests/rustdoc-ui/issues/issue-96287.rs b/tests/rustdoc-ui/issues/issue-96287.rs index 08cc7ef4c90..b490c2fc03f 100644 --- a/tests/rustdoc-ui/issues/issue-96287.rs +++ b/tests/rustdoc-ui/issues/issue-96287.rs @@ -6,6 +6,7 @@ pub trait TraitWithAssoc { pub type Foo<V> = impl Trait<V::Assoc>; //~^ ERROR +//~| ERROR pub trait Trait<U> {} diff --git a/tests/rustdoc-ui/issues/issue-96287.stderr b/tests/rustdoc-ui/issues/issue-96287.stderr index 62d81534a98..9aba0332164 100644 --- a/tests/rustdoc-ui/issues/issue-96287.stderr +++ b/tests/rustdoc-ui/issues/issue-96287.stderr @@ -9,6 +9,18 @@ help: consider restricting type parameter `V` LL | pub type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>; | ++++++++++++++++ -error: aborting due to 1 previous error +error[E0220]: associated type `Assoc` not found for `V` + --> $DIR/issue-96287.rs:7:33 + | +LL | pub type Foo<V> = impl Trait<V::Assoc>; + | ^^^^^ there is an associated type `Assoc` in the trait `TraitWithAssoc` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider restricting type parameter `V` + | +LL | pub type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>; + | ++++++++++++++++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/asm/aarch64/type-check-3.stderr b/tests/ui/asm/aarch64/type-check-3.stderr index f710df2dcde..9e37bb4c203 100644 --- a/tests/ui/asm/aarch64/type-check-3.stderr +++ b/tests/ui/asm/aarch64/type-check-3.stderr @@ -4,8 +4,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0u8); | ^^ --- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default warning: formatting may not be suitable for sub-register argument @@ -14,8 +14,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0u16); | ^^ ---- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:52:15 @@ -23,8 +23,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0i32); | ^^ ---- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:54:15 @@ -32,8 +32,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0f32); | ^^ ---- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:57:15 @@ -41,8 +41,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(vreg) 0i16); | ^^ ---- for this argument | - = help: use `{0:h}` to have the register formatted as `h0` - = help: or use `{0:v}` to keep the default formatting of `v0` + = help: use `{0:h}` to have the register formatted as `h0` (for 16-bit values) + = help: or use `{0:v}` to keep the default formatting of `v0` (for 128-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:59:15 @@ -50,8 +50,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(vreg) 0f32); | ^^ ---- for this argument | - = help: use `{0:s}` to have the register formatted as `s0` - = help: or use `{0:v}` to keep the default formatting of `v0` + = help: use `{0:s}` to have the register formatted as `s0` (for 32-bit values) + = help: or use `{0:v}` to keep the default formatting of `v0` (for 128-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:61:15 @@ -59,8 +59,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(vreg) 0f64); | ^^ ---- for this argument | - = help: use `{0:d}` to have the register formatted as `d0` - = help: or use `{0:v}` to keep the default formatting of `v0` + = help: use `{0:d}` to have the register formatted as `d0` (for 64-bit values) + = help: or use `{0:v}` to keep the default formatting of `v0` (for 128-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:63:15 @@ -68,8 +68,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(vreg_low16) 0f64); | ^^ ---- for this argument | - = help: use `{0:d}` to have the register formatted as `d0` - = help: or use `{0:v}` to keep the default formatting of `v0` + = help: use `{0:d}` to have the register formatted as `d0` (for 64-bit values) + = help: or use `{0:v}` to keep the default formatting of `v0` (for 128-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:66:15 @@ -77,8 +77,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{0} {0}", in(reg) 0i16); | ^^^ ^^^ ---- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:68:15 @@ -86,8 +86,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{0} {0:x}", in(reg) 0i16); | ^^^ ---- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) error: type `i128` cannot be used with this register class --> $DIR/type-check-3.rs:73:28 diff --git a/tests/ui/asm/bad-template.aarch64.stderr b/tests/ui/asm/bad-template.aarch64.stderr index b18946d7c6d..5023cf317d7 100644 --- a/tests/ui/asm/bad-template.aarch64.stderr +++ b/tests/ui/asm/bad-template.aarch64.stderr @@ -194,8 +194,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{:foo}", in(reg) foo); | ^^^^^^ --- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default error: aborting due to 21 previous errors; 1 warning emitted diff --git a/tests/ui/asm/bad-template.x86_64.stderr b/tests/ui/asm/bad-template.x86_64.stderr index 2f584c30a32..1b9775636f5 100644 --- a/tests/ui/asm/bad-template.x86_64.stderr +++ b/tests/ui/asm/bad-template.x86_64.stderr @@ -194,8 +194,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{:foo}", in(reg) foo); | ^^^^^^ --- for this argument | - = help: use `{0:e}` to have the register formatted as `eax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:e}` to have the register formatted as `eax` (for 32-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default error: aborting due to 21 previous errors; 1 warning emitted diff --git a/tests/ui/asm/x86_64/type-check-3.stderr b/tests/ui/asm/x86_64/type-check-3.stderr index 1baf50ff6e0..34bfcd71cac 100644 --- a/tests/ui/asm/x86_64/type-check-3.stderr +++ b/tests/ui/asm/x86_64/type-check-3.stderr @@ -44,8 +44,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{0} {0}", in(reg) 0i16); | ^^^ ^^^ ---- for this argument | - = help: use `{0:x}` to have the register formatted as `ax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:x}` to have the register formatted as `ax` (for 16-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default warning: formatting may not be suitable for sub-register argument @@ -54,8 +54,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{0} {0:x}", in(reg) 0i16); | ^^^ ---- for this argument | - = help: use `{0:x}` to have the register formatted as `ax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:x}` to have the register formatted as `ax` (for 16-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:38:15 @@ -63,8 +63,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0i32); | ^^ ---- for this argument | - = help: use `{0:e}` to have the register formatted as `eax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:e}` to have the register formatted as `eax` (for 32-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:41:15 @@ -72,8 +72,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(ymm_reg) 0i64); | ^^ ---- for this argument | - = help: use `{0:x}` to have the register formatted as `xmm0` - = help: or use `{0:y}` to keep the default formatting of `ymm0` + = help: use `{0:x}` to have the register formatted as `xmm0` (for 128-bit values) + = help: or use `{0:y}` to keep the default formatting of `ymm0` (for 256-bit values) error: type `i8` cannot be used with this register class --> $DIR/type-check-3.rs:52:28 diff --git a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs index aaf16181030..06fd0a024f0 100644 --- a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs +++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.rs @@ -37,8 +37,19 @@ fn take2<P: Project<SELF = {}>>(_: P) {} trait Iface<'r> { //~^ NOTE the lifetime parameter `'r` is defined here + //~| NOTE the lifetime parameter `'r` is defined here type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }> //~^ ERROR the type of the associated constant `K` must not depend on generic parameters + //~| ERROR the type of the associated constant `K` must not depend on generic parameters + //~| NOTE its type must not depend on the lifetime parameter `'r` + //~| NOTE `K` has type `&'r [Self; Q]` + //~| ERROR the type of the associated constant `K` must not depend on `Self` + //~| NOTE its type must not depend on `Self` + //~| NOTE `K` has type `&'r [Self; Q]` + //~| ERROR the type of the associated constant `K` must not depend on generic parameters + //~| NOTE its type must not depend on the const parameter `Q` + //~| NOTE the const parameter `Q` is defined here + //~| NOTE `K` has type `&'r [Self; Q]` //~| NOTE its type must not depend on the lifetime parameter `'r` //~| NOTE `K` has type `&'r [Self; Q]` //~| ERROR the type of the associated constant `K` must not depend on `Self` @@ -48,6 +59,9 @@ trait Iface<'r> { //~| NOTE its type must not depend on the const parameter `Q` //~| NOTE the const parameter `Q` is defined here //~| NOTE `K` has type `&'r [Self; Q]` + //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` where Self: Sized + 'r; } diff --git a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr index 077ac6e7f93..6b7b714fff1 100644 --- a/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr +++ b/tests/ui/associated-consts/assoc-const-eq-param-in-ty.stderr @@ -44,18 +44,18 @@ LL | fn take2<P: Project<SELF = {}>>(_: P) {} = note: `SELF` has type `P` error: the type of the associated constant `K` must not depend on generic parameters - --> $DIR/assoc-const-eq-param-in-ty.rs:40:52 + --> $DIR/assoc-const-eq-param-in-ty.rs:41:52 | LL | trait Iface<'r> { | -- the lifetime parameter `'r` is defined here -LL | +... LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }> | ^ its type must not depend on the lifetime parameter `'r` | = note: `K` has type `&'r [Self; Q]` error: the type of the associated constant `K` must not depend on `Self` - --> $DIR/assoc-const-eq-param-in-ty.rs:40:52 + --> $DIR/assoc-const-eq-param-in-ty.rs:41:52 | LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }> | ^ its type must not depend on `Self` @@ -63,7 +63,7 @@ LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }> = note: `K` has type `&'r [Self; Q]` error: the type of the associated constant `K` must not depend on generic parameters - --> $DIR/assoc-const-eq-param-in-ty.rs:40:52 + --> $DIR/assoc-const-eq-param-in-ty.rs:41:52 | LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }> | - ^ its type must not depend on the const parameter `Q` @@ -72,5 +72,37 @@ LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }> | = note: `K` has type `&'r [Self; Q]` -error: aborting due to 8 previous errors +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:41:52 + | +LL | trait Iface<'r> { + | -- the lifetime parameter `'r` is defined here +... +LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }> + | ^ its type must not depend on the lifetime parameter `'r` + | + = note: `K` has type `&'r [Self; Q]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: the type of the associated constant `K` must not depend on `Self` + --> $DIR/assoc-const-eq-param-in-ty.rs:41:52 + | +LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }> + | ^ its type must not depend on `Self` + | + = note: `K` has type `&'r [Self; Q]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: the type of the associated constant `K` must not depend on generic parameters + --> $DIR/assoc-const-eq-param-in-ty.rs:41:52 + | +LL | type Assoc<const Q: usize>: Trait<'r, Self, Q, K = { loop {} }> + | - ^ its type must not depend on the const parameter `Q` + | | + | the const parameter `Q` is defined here + | + = note: `K` has type `&'r [Self; Q]` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 11 previous errors diff --git a/tests/ui/associated-consts/issue-102335-const.rs b/tests/ui/associated-consts/issue-102335-const.rs index f60cb92da7f..969c2c43b71 100644 --- a/tests/ui/associated-consts/issue-102335-const.rs +++ b/tests/ui/associated-consts/issue-102335-const.rs @@ -3,6 +3,7 @@ trait T { type A: S<C<X = 0i32> = 34>; //~^ ERROR associated type bindings are not allowed here + //~| ERROR associated type bindings are not allowed here } trait S { diff --git a/tests/ui/associated-consts/issue-102335-const.stderr b/tests/ui/associated-consts/issue-102335-const.stderr index b69dfd51ea8..2a70425a3cc 100644 --- a/tests/ui/associated-consts/issue-102335-const.stderr +++ b/tests/ui/associated-consts/issue-102335-const.stderr @@ -4,6 +4,14 @@ error[E0229]: associated type bindings are not allowed here LL | type A: S<C<X = 0i32> = 34>; | ^^^^^^^^ associated type not allowed here -error: aborting due to 1 previous error +error[E0229]: associated type bindings are not allowed here + --> $DIR/issue-102335-const.rs:4:17 + | +LL | type A: S<C<X = 0i32> = 34>; + | ^^^^^^^^ associated type not allowed here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0229`. diff --git a/tests/ui/associated-inherent-types/issue-109299-1.rs b/tests/ui/associated-inherent-types/issue-109299-1.rs index b86e2e31e06..4546785f0b1 100644 --- a/tests/ui/associated-inherent-types/issue-109299-1.rs +++ b/tests/ui/associated-inherent-types/issue-109299-1.rs @@ -7,7 +7,9 @@ impl Lexer<i32> { type Cursor = (); } -type X = impl for<T> Fn() -> Lexer<T>::Cursor; //~ ERROR associated type `Cursor` not found for `Lexer<T>` in the current scope -//~^ ERROR: unconstrained opaque type +type X = impl for<T> Fn() -> Lexer<T>::Cursor; +//~^ ERROR associated type `Cursor` not found for `Lexer<T>` in the current scope +//~| ERROR associated type `Cursor` not found for `Lexer<T>` in the current scope +//~| ERROR: unconstrained opaque type fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-109299-1.stderr b/tests/ui/associated-inherent-types/issue-109299-1.stderr index 5848fa4087d..07a00b6b9a9 100644 --- a/tests/ui/associated-inherent-types/issue-109299-1.stderr +++ b/tests/ui/associated-inherent-types/issue-109299-1.stderr @@ -10,6 +10,19 @@ LL | type X = impl for<T> Fn() -> Lexer<T>::Cursor; = note: the associated type was found for - `Lexer<i32>` +error[E0220]: associated type `Cursor` not found for `Lexer<T>` in the current scope + --> $DIR/issue-109299-1.rs:10:40 + | +LL | struct Lexer<T>(T); + | --------------- associated item `Cursor` not found for this struct +... +LL | type X = impl for<T> Fn() -> Lexer<T>::Cursor; + | ^^^^^^ associated item not found in `Lexer<T>` + | + = note: the associated type was found for + - `Lexer<i32>` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error: unconstrained opaque type --> $DIR/issue-109299-1.rs:10:10 | @@ -18,6 +31,6 @@ LL | type X = impl for<T> Fn() -> Lexer<T>::Cursor; | = note: `X` must be used in combination with a concrete type within the same module -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.rs b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.rs new file mode 100644 index 00000000000..1768b006622 --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.rs @@ -0,0 +1,18 @@ +trait Id { + type This: ?Sized; +} +impl<T: ?Sized> Id for T { + type This = T; +} + +trait Trait { + type Assoc: Id<This: Copy>; +} + +// We can't see use the `T::Assoc::This: Copy` bound to prove `T::Assoc: Copy` +fn foo<T: Trait>(x: T::Assoc) -> (T::Assoc, T::Assoc) { + (x, x) + //~^ ERROR use of moved value +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.stderr new file mode 100644 index 00000000000..cd0e026905c --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid-2.stderr @@ -0,0 +1,13 @@ +error[E0382]: use of moved value: `x` + --> $DIR/cant-see-copy-bound-from-child-rigid-2.rs:14:9 + | +LL | fn foo<T: Trait>(x: T::Assoc) -> (T::Assoc, T::Assoc) { + | - move occurs because `x` has type `<T as Trait>::Assoc`, which does not implement the `Copy` trait +LL | (x, x) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs new file mode 100644 index 00000000000..6b3fd7e898d --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs @@ -0,0 +1,18 @@ +trait Id { + type This: ?Sized; +} + +trait Trait { + type Assoc: Id<This: Copy>; +} + +// We can't see use the `T::Assoc::This: Copy` bound to prove `T::Assoc: Copy` +fn foo<T: Trait>(x: T::Assoc) -> (T::Assoc, T::Assoc) +where + T::Assoc: Id<This = T::Assoc>, +{ + (x, x) + //~^ ERROR use of moved value +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr new file mode 100644 index 00000000000..3ed73918de3 --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr @@ -0,0 +1,14 @@ +error[E0382]: use of moved value: `x` + --> $DIR/cant-see-copy-bound-from-child-rigid.rs:14:9 + | +LL | fn foo<T: Trait>(x: T::Assoc) -> (T::Assoc, T::Assoc) + | - move occurs because `x` has type `<T as Trait>::Assoc`, which does not implement the `Copy` trait +... +LL | (x, x) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/associated-type-bounds/dont-imply-atb-in-closure-inference.rs b/tests/ui/associated-type-bounds/dont-imply-atb-in-closure-inference.rs new file mode 100644 index 00000000000..fecb3b15338 --- /dev/null +++ b/tests/ui/associated-type-bounds/dont-imply-atb-in-closure-inference.rs @@ -0,0 +1,21 @@ +//@ check-pass + +#![feature(type_alias_impl_trait)] + +trait IsPtr { + type Assoc; +} +impl<T> IsPtr for T { + type Assoc = fn(i32); +} + +type Tait = impl IsPtr<Assoc: Fn(i32)> + Fn(u32); + +fn hello() +where + Tait:, +{ + let _: Tait = |x| {}; +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/duplicate.rs b/tests/ui/associated-type-bounds/duplicate.rs index 06a1993da72..2b4a01376d7 100644 --- a/tests/ui/associated-type-bounds/duplicate.rs +++ b/tests/ui/associated-type-bounds/duplicate.rs @@ -132,16 +132,19 @@ where fn FRPIT1() -> impl Iterator<Item: Copy, Item: Send> { //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] iter::empty() //~^ ERROR type annotations needed } fn FRPIT2() -> impl Iterator<Item: Copy, Item: Copy> { //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] iter::empty() //~^ ERROR type annotations needed } fn FRPIT3() -> impl Iterator<Item: 'static, Item: 'static> { //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] iter::empty() //~^ ERROR type annotations needed } @@ -182,10 +185,13 @@ type ETAI3<T: Iterator<Item: 'static, Item: 'static>> = impl Copy; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] type ETAI4 = impl Iterator<Item: Copy, Item: Send>; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] type ETAI5 = impl Iterator<Item: Copy, Item: Copy>; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] type ETAI6 = impl Iterator<Item: 'static, Item: 'static>; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] +//~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] trait TRI1<T: Iterator<Item: Copy, Item: Send>> {} //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] @@ -250,14 +256,17 @@ where trait TRA1 { type A: Iterator<Item: Copy, Item: Send>; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] } trait TRA2 { type A: Iterator<Item: Copy, Item: Copy>; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] } trait TRA3 { type A: Iterator<Item: 'static, Item: 'static>; //~^ ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] + //~| ERROR the value of the associated type `Item` in trait `Iterator` is already specified [E0719] } fn main() {} diff --git a/tests/ui/associated-type-bounds/duplicate.stderr b/tests/ui/associated-type-bounds/duplicate.stderr index 2d298f0a013..cf4809991c3 100644 --- a/tests/ui/associated-type-bounds/duplicate.stderr +++ b/tests/ui/associated-type-bounds/duplicate.stderr @@ -198,8 +198,18 @@ LL | fn FRPIT1() -> impl Iterator<Item: Copy, Item: Send> { | | | `Item` bound here first +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:133:42 + | +LL | fn FRPIT1() -> impl Iterator<Item: Copy, Item: Send> { + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0282]: type annotations needed - --> $DIR/duplicate.rs:135:5 + --> $DIR/duplicate.rs:136:5 | LL | iter::empty() | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` @@ -210,15 +220,25 @@ LL | iter::empty::<T>() | +++++ error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:138:42 + --> $DIR/duplicate.rs:139:42 + | +LL | fn FRPIT2() -> impl Iterator<Item: Copy, Item: Copy> { + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:139:42 | LL | fn FRPIT2() -> impl Iterator<Item: Copy, Item: Copy> { | ---------- ^^^^^^^^^^ re-bound here | | | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0282]: type annotations needed - --> $DIR/duplicate.rs:140:5 + --> $DIR/duplicate.rs:142:5 | LL | iter::empty() | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` @@ -229,15 +249,25 @@ LL | iter::empty::<T>() | +++++ error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:143:45 + --> $DIR/duplicate.rs:145:45 + | +LL | fn FRPIT3() -> impl Iterator<Item: 'static, Item: 'static> { + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:145:45 | LL | fn FRPIT3() -> impl Iterator<Item: 'static, Item: 'static> { | ------------- ^^^^^^^^^^^^^ re-bound here | | | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0282]: type annotations needed - --> $DIR/duplicate.rs:145:5 + --> $DIR/duplicate.rs:148:5 | LL | iter::empty() | ^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `empty` @@ -248,7 +278,7 @@ LL | iter::empty::<T>() | +++++ error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:148:40 + --> $DIR/duplicate.rs:151:40 | LL | fn FAPIT1(_: impl Iterator<Item: Copy, Item: Send>) {} | ---------- ^^^^^^^^^^ re-bound here @@ -256,7 +286,7 @@ LL | fn FAPIT1(_: impl Iterator<Item: Copy, Item: Send>) {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:150:40 + --> $DIR/duplicate.rs:153:40 | LL | fn FAPIT2(_: impl Iterator<Item: Copy, Item: Copy>) {} | ---------- ^^^^^^^^^^ re-bound here @@ -264,7 +294,7 @@ LL | fn FAPIT2(_: impl Iterator<Item: Copy, Item: Copy>) {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:152:43 + --> $DIR/duplicate.rs:155:43 | LL | fn FAPIT3(_: impl Iterator<Item: 'static, Item: 'static>) {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -272,7 +302,7 @@ LL | fn FAPIT3(_: impl Iterator<Item: 'static, Item: 'static>) {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:155:35 + --> $DIR/duplicate.rs:158:35 | LL | type TAI1<T: Iterator<Item: Copy, Item: Send>> = T; | ---------- ^^^^^^^^^^ re-bound here @@ -280,7 +310,7 @@ LL | type TAI1<T: Iterator<Item: Copy, Item: Send>> = T; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:157:35 + --> $DIR/duplicate.rs:160:35 | LL | type TAI2<T: Iterator<Item: Copy, Item: Copy>> = T; | ---------- ^^^^^^^^^^ re-bound here @@ -288,7 +318,7 @@ LL | type TAI2<T: Iterator<Item: Copy, Item: Copy>> = T; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:159:38 + --> $DIR/duplicate.rs:162:38 | LL | type TAI3<T: Iterator<Item: 'static, Item: 'static>> = T; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -296,7 +326,7 @@ LL | type TAI3<T: Iterator<Item: 'static, Item: 'static>> = T; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:163:29 + --> $DIR/duplicate.rs:166:29 | LL | T: Iterator<Item: Copy, Item: Send>, | ---------- ^^^^^^^^^^ re-bound here @@ -304,7 +334,7 @@ LL | T: Iterator<Item: Copy, Item: Send>, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:168:29 + --> $DIR/duplicate.rs:171:29 | LL | T: Iterator<Item: Copy, Item: Copy>, | ---------- ^^^^^^^^^^ re-bound here @@ -312,7 +342,7 @@ LL | T: Iterator<Item: Copy, Item: Copy>, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:173:32 + --> $DIR/duplicate.rs:176:32 | LL | T: Iterator<Item: 'static, Item: 'static>, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -320,7 +350,7 @@ LL | T: Iterator<Item: 'static, Item: 'static>, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:177:36 + --> $DIR/duplicate.rs:180:36 | LL | type ETAI1<T: Iterator<Item: Copy, Item: Send>> = impl Copy; | ---------- ^^^^^^^^^^ re-bound here @@ -328,7 +358,7 @@ LL | type ETAI1<T: Iterator<Item: Copy, Item: Send>> = impl Copy; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:179:36 + --> $DIR/duplicate.rs:182:36 | LL | type ETAI2<T: Iterator<Item: Copy, Item: Copy>> = impl Copy; | ---------- ^^^^^^^^^^ re-bound here @@ -336,7 +366,7 @@ LL | type ETAI2<T: Iterator<Item: Copy, Item: Copy>> = impl Copy; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:181:39 + --> $DIR/duplicate.rs:184:39 | LL | type ETAI3<T: Iterator<Item: 'static, Item: 'static>> = impl Copy; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -344,23 +374,43 @@ LL | type ETAI3<T: Iterator<Item: 'static, Item: 'static>> = impl Copy; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:183:40 + --> $DIR/duplicate.rs:186:40 + | +LL | type ETAI4 = impl Iterator<Item: Copy, Item: Send>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:186:40 | LL | type ETAI4 = impl Iterator<Item: Copy, Item: Send>; | ---------- ^^^^^^^^^^ re-bound here | | | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:189:40 + | +LL | type ETAI5 = impl Iterator<Item: Copy, Item: Copy>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:185:40 + --> $DIR/duplicate.rs:189:40 | LL | type ETAI5 = impl Iterator<Item: Copy, Item: Copy>; | ---------- ^^^^^^^^^^ re-bound here | | | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:187:43 + --> $DIR/duplicate.rs:192:43 | LL | type ETAI6 = impl Iterator<Item: 'static, Item: 'static>; | ------------- ^^^^^^^^^^^^^ re-bound here @@ -368,7 +418,17 @@ LL | type ETAI6 = impl Iterator<Item: 'static, Item: 'static>; | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:190:36 + --> $DIR/duplicate.rs:192:43 + | +LL | type ETAI6 = impl Iterator<Item: 'static, Item: 'static>; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:196:36 | LL | trait TRI1<T: Iterator<Item: Copy, Item: Send>> {} | ---------- ^^^^^^^^^^ re-bound here @@ -376,7 +436,7 @@ LL | trait TRI1<T: Iterator<Item: Copy, Item: Send>> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:192:36 + --> $DIR/duplicate.rs:198:36 | LL | trait TRI2<T: Iterator<Item: Copy, Item: Copy>> {} | ---------- ^^^^^^^^^^ re-bound here @@ -384,7 +444,7 @@ LL | trait TRI2<T: Iterator<Item: Copy, Item: Copy>> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:194:39 + --> $DIR/duplicate.rs:200:39 | LL | trait TRI3<T: Iterator<Item: 'static, Item: 'static>> {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -392,7 +452,7 @@ LL | trait TRI3<T: Iterator<Item: 'static, Item: 'static>> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:196:34 + --> $DIR/duplicate.rs:202:34 | LL | trait TRS1: Iterator<Item: Copy, Item: Send> {} | ---------- ^^^^^^^^^^ re-bound here @@ -400,7 +460,7 @@ LL | trait TRS1: Iterator<Item: Copy, Item: Send> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:196:34 + --> $DIR/duplicate.rs:202:34 | LL | trait TRS1: Iterator<Item: Copy, Item: Send> {} | ---------- ^^^^^^^^^^ re-bound here @@ -410,7 +470,7 @@ LL | trait TRS1: Iterator<Item: Copy, Item: Send> {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:196:34 + --> $DIR/duplicate.rs:202:34 | LL | trait TRS1: Iterator<Item: Copy, Item: Send> {} | ---------- ^^^^^^^^^^ re-bound here @@ -420,7 +480,7 @@ LL | trait TRS1: Iterator<Item: Copy, Item: Send> {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:200:34 + --> $DIR/duplicate.rs:206:34 | LL | trait TRS2: Iterator<Item: Copy, Item: Copy> {} | ---------- ^^^^^^^^^^ re-bound here @@ -428,7 +488,7 @@ LL | trait TRS2: Iterator<Item: Copy, Item: Copy> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:200:34 + --> $DIR/duplicate.rs:206:34 | LL | trait TRS2: Iterator<Item: Copy, Item: Copy> {} | ---------- ^^^^^^^^^^ re-bound here @@ -438,7 +498,7 @@ LL | trait TRS2: Iterator<Item: Copy, Item: Copy> {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:200:34 + --> $DIR/duplicate.rs:206:34 | LL | trait TRS2: Iterator<Item: Copy, Item: Copy> {} | ---------- ^^^^^^^^^^ re-bound here @@ -448,7 +508,7 @@ LL | trait TRS2: Iterator<Item: Copy, Item: Copy> {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:204:37 + --> $DIR/duplicate.rs:210:37 | LL | trait TRS3: Iterator<Item: 'static, Item: 'static> {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -456,7 +516,7 @@ LL | trait TRS3: Iterator<Item: 'static, Item: 'static> {} | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:204:37 + --> $DIR/duplicate.rs:210:37 | LL | trait TRS3: Iterator<Item: 'static, Item: 'static> {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -466,7 +526,7 @@ LL | trait TRS3: Iterator<Item: 'static, Item: 'static> {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:204:37 + --> $DIR/duplicate.rs:210:37 | LL | trait TRS3: Iterator<Item: 'static, Item: 'static> {} | ------------- ^^^^^^^^^^^^^ re-bound here @@ -476,7 +536,7 @@ LL | trait TRS3: Iterator<Item: 'static, Item: 'static> {} = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:210:29 + --> $DIR/duplicate.rs:216:29 | LL | T: Iterator<Item: Copy, Item: Send>, | ---------- ^^^^^^^^^^ re-bound here @@ -484,7 +544,7 @@ LL | T: Iterator<Item: Copy, Item: Send>, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:216:29 + --> $DIR/duplicate.rs:222:29 | LL | T: Iterator<Item: Copy, Item: Copy>, | ---------- ^^^^^^^^^^ re-bound here @@ -492,7 +552,7 @@ LL | T: Iterator<Item: Copy, Item: Copy>, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:222:32 + --> $DIR/duplicate.rs:228:32 | LL | T: Iterator<Item: 'static, Item: 'static>, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -500,7 +560,7 @@ LL | T: Iterator<Item: 'static, Item: 'static>, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:228:32 + --> $DIR/duplicate.rs:234:32 | LL | Self: Iterator<Item: Copy, Item: Send>, | ---------- ^^^^^^^^^^ re-bound here @@ -508,7 +568,7 @@ LL | Self: Iterator<Item: Copy, Item: Send>, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:228:32 + --> $DIR/duplicate.rs:234:32 | LL | Self: Iterator<Item: Copy, Item: Send>, | ---------- ^^^^^^^^^^ re-bound here @@ -518,7 +578,7 @@ LL | Self: Iterator<Item: Copy, Item: Send>, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:228:32 + --> $DIR/duplicate.rs:234:32 | LL | Self: Iterator<Item: Copy, Item: Send>, | ---------- ^^^^^^^^^^ re-bound here @@ -528,7 +588,7 @@ LL | Self: Iterator<Item: Copy, Item: Send>, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:236:32 + --> $DIR/duplicate.rs:242:32 | LL | Self: Iterator<Item: Copy, Item: Copy>, | ---------- ^^^^^^^^^^ re-bound here @@ -536,7 +596,7 @@ LL | Self: Iterator<Item: Copy, Item: Copy>, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:236:32 + --> $DIR/duplicate.rs:242:32 | LL | Self: Iterator<Item: Copy, Item: Copy>, | ---------- ^^^^^^^^^^ re-bound here @@ -546,7 +606,7 @@ LL | Self: Iterator<Item: Copy, Item: Copy>, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:236:32 + --> $DIR/duplicate.rs:242:32 | LL | Self: Iterator<Item: Copy, Item: Copy>, | ---------- ^^^^^^^^^^ re-bound here @@ -556,7 +616,7 @@ LL | Self: Iterator<Item: Copy, Item: Copy>, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:244:35 + --> $DIR/duplicate.rs:250:35 | LL | Self: Iterator<Item: 'static, Item: 'static>, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -564,7 +624,7 @@ LL | Self: Iterator<Item: 'static, Item: 'static>, | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:244:35 + --> $DIR/duplicate.rs:250:35 | LL | Self: Iterator<Item: 'static, Item: 'static>, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -574,7 +634,7 @@ LL | Self: Iterator<Item: 'static, Item: 'static>, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:244:35 + --> $DIR/duplicate.rs:250:35 | LL | Self: Iterator<Item: 'static, Item: 'static>, | ------------- ^^^^^^^^^^^^^ re-bound here @@ -584,30 +644,60 @@ LL | Self: Iterator<Item: 'static, Item: 'static>, = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:251:34 + --> $DIR/duplicate.rs:257:34 + | +LL | type A: Iterator<Item: Copy, Item: Send>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:257:34 | LL | type A: Iterator<Item: Copy, Item: Send>; | ---------- ^^^^^^^^^^ re-bound here | | | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:262:34 + | +LL | type A: Iterator<Item: Copy, Item: Copy>; + | ---------- ^^^^^^^^^^ re-bound here + | | + | `Item` bound here first error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:255:34 + --> $DIR/duplicate.rs:262:34 | LL | type A: Iterator<Item: Copy, Item: Copy>; | ---------- ^^^^^^^^^^ re-bound here | | | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified - --> $DIR/duplicate.rs:259:37 + --> $DIR/duplicate.rs:267:37 | LL | type A: Iterator<Item: 'static, Item: 'static>; | ------------- ^^^^^^^^^^^^^ re-bound here | | | `Item` bound here first -error: aborting due to 72 previous errors +error[E0719]: the value of the associated type `Item` in trait `Iterator` is already specified + --> $DIR/duplicate.rs:267:37 + | +LL | type A: Iterator<Item: 'static, Item: 'static>; + | ------------- ^^^^^^^^^^^^^ re-bound here + | | + | `Item` bound here first + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 81 previous errors Some errors have detailed explanations: E0282, E0719. For more information about an error, try `rustc --explain E0282`. diff --git a/tests/ui/associated-type-bounds/issue-102335-ty.rs b/tests/ui/associated-type-bounds/issue-102335-ty.rs index 363df73c1ff..5fd8b71e679 100644 --- a/tests/ui/associated-type-bounds/issue-102335-ty.rs +++ b/tests/ui/associated-type-bounds/issue-102335-ty.rs @@ -1,6 +1,7 @@ trait T { type A: S<C<i32 = u32> = ()>; //~^ ERROR associated type bindings are not allowed here + //~| ERROR associated type bindings are not allowed here } trait Q {} diff --git a/tests/ui/associated-type-bounds/issue-102335-ty.stderr b/tests/ui/associated-type-bounds/issue-102335-ty.stderr index 561ca15ab0d..3bd7566ad1e 100644 --- a/tests/ui/associated-type-bounds/issue-102335-ty.stderr +++ b/tests/ui/associated-type-bounds/issue-102335-ty.stderr @@ -4,6 +4,14 @@ error[E0229]: associated type bindings are not allowed here LL | type A: S<C<i32 = u32> = ()>; | ^^^^^^^^^ associated type not allowed here -error: aborting due to 1 previous error +error[E0229]: associated type bindings are not allowed here + --> $DIR/issue-102335-ty.rs:2:17 + | +LL | type A: S<C<i32 = u32> = ()>; + | ^^^^^^^^^ associated type not allowed here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0229`. diff --git a/tests/ui/associated-type-bounds/issue-99828.rs b/tests/ui/associated-type-bounds/issue-99828.rs index 67ba50f3cbc..ab3654131f1 100644 --- a/tests/ui/associated-type-bounds/issue-99828.rs +++ b/tests/ui/associated-type-bounds/issue-99828.rs @@ -1,5 +1,6 @@ fn get_iter(vec: &[i32]) -> impl Iterator<Item = {}> + '_ { //~^ ERROR expected type, found constant + //~| ERROR expected type, found constant //~| ERROR associated const equality is incomplete vec.iter() } diff --git a/tests/ui/associated-type-bounds/issue-99828.stderr b/tests/ui/associated-type-bounds/issue-99828.stderr index 911f3ff0f5e..132d5251987 100644 --- a/tests/ui/associated-type-bounds/issue-99828.stderr +++ b/tests/ui/associated-type-bounds/issue-99828.stderr @@ -19,6 +19,18 @@ LL | fn get_iter(vec: &[i32]) -> impl Iterator<Item = {}> + '_ { note: the associated type is defined here --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error: aborting due to 2 previous errors +error: expected type, found constant + --> $DIR/issue-99828.rs:1:50 + | +LL | fn get_iter(vec: &[i32]) -> impl Iterator<Item = {}> + '_ { + | ---- ^^ unexpected constant + | | + | expected a type because of this associated type + | +note: the associated type is defined here + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout index 47b39e5246d..d6fb643702c 100644 --- a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout +++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout @@ -2,7 +2,7 @@ print-type-size type: `{async fn body@$DIR/async-awaiting-fut.rs:21:21: 24:2}`: print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes print-type-size variant `Suspend0`: 3077 bytes -print-type-size local `.__awaitee`: 3077 bytes +print-type-size local `.__awaitee`: 3077 bytes, type: {async fn body@$DIR/async-awaiting-fut.rs:10:64: 19:2} print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body@$DIR/async-awaiting-fut.rs:10:64: 19:2}>`: 3077 bytes, alignment: 1 bytes @@ -19,19 +19,19 @@ print-type-size variant `Suspend0`: 2052 bytes print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes print-type-size padding: 1 bytes print-type-size local `.fut`: 1025 bytes, alignment: 1 bytes -print-type-size local `..coroutine_field4`: 1 bytes -print-type-size local `.__awaitee`: 1 bytes +print-type-size local `..coroutine_field4`: 1 bytes, type: bool +print-type-size local `.__awaitee`: 1 bytes, type: {async fn body@$DIR/async-awaiting-fut.rs:6:17: 6:19} print-type-size variant `Suspend1`: 3076 bytes print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes print-type-size padding: 1026 bytes -print-type-size local `..coroutine_field4`: 1 bytes, alignment: 1 bytes -print-type-size local `.__awaitee`: 1025 bytes +print-type-size local `..coroutine_field4`: 1 bytes, alignment: 1 bytes, type: bool +print-type-size local `.__awaitee`: 1025 bytes, type: {async fn body@$DIR/async-awaiting-fut.rs:8:35: 8:37} print-type-size variant `Suspend2`: 2052 bytes print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes print-type-size padding: 1 bytes print-type-size local `.fut`: 1025 bytes, alignment: 1 bytes -print-type-size local `..coroutine_field4`: 1 bytes -print-type-size local `.__awaitee`: 1 bytes +print-type-size local `..coroutine_field4`: 1 bytes, type: bool +print-type-size local `.__awaitee`: 1 bytes, type: {async fn body@$DIR/async-awaiting-fut.rs:6:17: 6:19} print-type-size variant `Returned`: 1025 bytes print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes print-type-size variant `Panicked`: 1025 bytes diff --git a/tests/ui/async-await/future-sizes/large-arg.stdout b/tests/ui/async-await/future-sizes/large-arg.stdout index 005460df626..589df102af4 100644 --- a/tests/ui/async-await/future-sizes/large-arg.stdout +++ b/tests/ui/async-await/future-sizes/large-arg.stdout @@ -2,7 +2,7 @@ print-type-size type: `{async fn body@$DIR/large-arg.rs:6:21: 8:2}`: 3076 bytes, print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes print-type-size variant `Suspend0`: 3075 bytes -print-type-size local `.__awaitee`: 3075 bytes +print-type-size local `.__awaitee`: 3075 bytes, type: {async fn body@$DIR/large-arg.rs:10:30: 12:2} print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body@$DIR/large-arg.rs:10:30: 12:2}>`: 3075 bytes, alignment: 1 bytes @@ -17,7 +17,7 @@ print-type-size variant `Unresumed`: 1024 bytes print-type-size upvar `.t`: 1024 bytes print-type-size variant `Suspend0`: 3074 bytes print-type-size upvar `.t`: 1024 bytes -print-type-size local `.__awaitee`: 2050 bytes +print-type-size local `.__awaitee`: 2050 bytes, type: {async fn body@$DIR/large-arg.rs:13:26: 15:2} print-type-size variant `Returned`: 1024 bytes print-type-size upvar `.t`: 1024 bytes print-type-size variant `Panicked`: 1024 bytes @@ -34,7 +34,7 @@ print-type-size variant `Unresumed`: 1024 bytes print-type-size upvar `.t`: 1024 bytes print-type-size variant `Suspend0`: 2049 bytes print-type-size upvar `.t`: 1024 bytes -print-type-size local `.__awaitee`: 1025 bytes +print-type-size local `.__awaitee`: 1025 bytes, type: {async fn body@$DIR/large-arg.rs:16:26: 18:2} print-type-size variant `Returned`: 1024 bytes print-type-size upvar `.t`: 1024 bytes print-type-size variant `Panicked`: 1024 bytes diff --git a/tests/ui/async-await/in-trait/return-not-existing-pair.rs b/tests/ui/async-await/in-trait/return-not-existing-pair.rs index 68be1358f81..3889efe1f2a 100644 --- a/tests/ui/async-await/in-trait/return-not-existing-pair.rs +++ b/tests/ui/async-await/in-trait/return-not-existing-pair.rs @@ -9,8 +9,7 @@ trait MyTrait<'a, 'b, T> { impl<'a, 'b, T, U> MyTrait<T> for U { //~^ ERROR: implicit elided lifetime not allowed here [E0726] async fn foo(_: T) -> (&'a U, &'b T) {} - //~^ ERROR: method `foo` has a `&self` declaration in the trait, but not in the impl [E0186] - //~| ERROR: mismatched types [E0308] + //~^ ERROR: mismatched types [E0308] } fn main() {} diff --git a/tests/ui/async-await/in-trait/return-not-existing-pair.stderr b/tests/ui/async-await/in-trait/return-not-existing-pair.stderr index 4694e608097..13d3606abba 100644 --- a/tests/ui/async-await/in-trait/return-not-existing-pair.stderr +++ b/tests/ui/async-await/in-trait/return-not-existing-pair.stderr @@ -15,15 +15,6 @@ error[E0412]: cannot find type `ConnImpl` in this scope LL | async fn foo(&'a self, key: &'b T) -> (&'a ConnImpl, &'b T); | ^^^^^^^^ not found in this scope -error[E0186]: method `foo` has a `&self` declaration in the trait, but not in the impl - --> $DIR/return-not-existing-pair.rs:11:5 - | -LL | async fn foo(&'a self, key: &'b T) -> (&'a ConnImpl, &'b T); - | ------------------------------------------------------------ `&self` used in trait -... -LL | async fn foo(_: T) -> (&'a U, &'b T) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&self` in impl - error[E0308]: mismatched types --> $DIR/return-not-existing-pair.rs:11:42 | @@ -33,7 +24,7 @@ LL | async fn foo(_: T) -> (&'a U, &'b T) {} = note: expected tuple `(&'a U, &'b T)` found unit type `()` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0186, E0308, E0412, E0726. -For more information about an error, try `rustc --explain E0186`. +Some errors have detailed explanations: E0308, E0412, E0726. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/async-await/in-trait/unconstrained-impl-region.rs b/tests/ui/async-await/in-trait/unconstrained-impl-region.rs index 9382c232364..95ba1f3f277 100644 --- a/tests/ui/async-await/in-trait/unconstrained-impl-region.rs +++ b/tests/ui/async-await/in-trait/unconstrained-impl-region.rs @@ -14,6 +14,7 @@ impl<'a> Actor for () { //~^ ERROR the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates type Message = &'a (); async fn on_mount(self, _: impl Inbox<&'a ()>) {} + //~^ ERROR the trait bound `impl Inbox<&'a ()>: Inbox<&'a ()>` is not satisfied } fn main() {} diff --git a/tests/ui/async-await/in-trait/unconstrained-impl-region.stderr b/tests/ui/async-await/in-trait/unconstrained-impl-region.stderr index ef7e4ef0eb8..66819d1fcf7 100644 --- a/tests/ui/async-await/in-trait/unconstrained-impl-region.stderr +++ b/tests/ui/async-await/in-trait/unconstrained-impl-region.stderr @@ -1,9 +1,26 @@ +error[E0277]: the trait bound `impl Inbox<&'a ()>: Inbox<&'a ()>` is not satisfied + --> $DIR/unconstrained-impl-region.rs:16:5 + | +LL | async fn on_mount(self, _: impl Inbox<&'a ()>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Inbox<&'a ()>` is not implemented for `impl Inbox<&'a ()>` + | +note: required by a bound in `<() as Actor>::on_mount` + --> $DIR/unconstrained-impl-region.rs:16:37 + | +LL | async fn on_mount(self, _: impl Inbox<&'a ()>) {} + | ^^^^^^^^^^^^^ required by this bound in `<() as Actor>::on_mount` +help: consider further restricting this bound + | +LL | async fn on_mount(self, _: impl Inbox<&'a ()> + Inbox<&'a ()>) {} + | +++++++++++++++ + error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates --> $DIR/unconstrained-impl-region.rs:13:6 | LL | impl<'a> Actor for () { | ^^ unconstrained lifetime parameter -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0207`. +Some errors have detailed explanations: E0207, E0277. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/async-await/issues/issue-65159.rs b/tests/ui/async-await/issues/issue-65159.rs index 781f8fe88d4..78492d55fda 100644 --- a/tests/ui/async-await/issues/issue-65159.rs +++ b/tests/ui/async-await/issues/issue-65159.rs @@ -4,6 +4,7 @@ async fn copy() -> Result<()> //~^ ERROR enum takes 2 generic arguments +//~| ERROR enum takes 2 generic arguments { Ok(()) } diff --git a/tests/ui/async-await/issues/issue-65159.stderr b/tests/ui/async-await/issues/issue-65159.stderr index 19512116a66..834927060b1 100644 --- a/tests/ui/async-await/issues/issue-65159.stderr +++ b/tests/ui/async-await/issues/issue-65159.stderr @@ -11,6 +11,20 @@ help: add missing generic argument LL | async fn copy() -> Result<(), E> | +++ -error: aborting due to 1 previous error +error[E0107]: enum takes 2 generic arguments but 1 generic argument was supplied + --> $DIR/issue-65159.rs:5:20 + | +LL | async fn copy() -> Result<()> + | ^^^^^^ -- supplied 1 generic argument + | | + | expected 2 generic arguments + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing generic argument + | +LL | async fn copy() -> Result<(), E> + | +++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/async-await/send-bound-async-closure.rs b/tests/ui/async-await/send-bound-async-closure.rs index 2732fa5d466..e4a9ae4cc75 100644 --- a/tests/ui/async-await/send-bound-async-closure.rs +++ b/tests/ui/async-await/send-bound-async-closure.rs @@ -1,5 +1,8 @@ //@ edition: 2021 //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver // This test verifies that we do not create a query cycle when typechecking has several inference // variables that point to the same coroutine interior type. diff --git a/tests/ui/borrowck/ice-mutability-error-slicing-121807.rs b/tests/ui/borrowck/ice-mutability-error-slicing-121807.rs new file mode 100644 index 00000000000..bbdd895d763 --- /dev/null +++ b/tests/ui/borrowck/ice-mutability-error-slicing-121807.rs @@ -0,0 +1,27 @@ +//@ edition:2015 +// test for ICE #121807 begin <= end (12 <= 11) when slicing 'Self::Assoc<'_>' +// fixed by #122749 + +trait MemoryUnit { // ERROR: not all trait items implemented, missing: `read_word` + extern "C" fn read_word(&mut self) -> u8; + extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + //~^ WARN anonymous parameters are deprecated and will be removed in the next edition + //~^^ WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + //~^^^ ERROR associated type `Assoc` not found for `Self` +} + +struct ROM {} + +impl MemoryUnit for ROM { +//~^ ERROR not all trait items implemented, missing: `read_word` + extern "C" fn read_dword(&'s self) -> u16 { + //~^ ERROR use of undeclared lifetime name `'s` + //~^^ ERROR method `read_dword` has a `&self` declaration in the impl, but not in the trait + let a16 = self.read_word() as u16; + let b16 = self.read_word() as u16; + + (b16 << 8) | a16 + } +} + +pub fn main() {} diff --git a/tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr b/tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr new file mode 100644 index 00000000000..3a6b8008fce --- /dev/null +++ b/tests/ui/borrowck/ice-mutability-error-slicing-121807.stderr @@ -0,0 +1,53 @@ +error[E0261]: use of undeclared lifetime name `'s` + --> $DIR/ice-mutability-error-slicing-121807.rs:17:31 + | +LL | extern "C" fn read_dword(&'s self) -> u16 { + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'s` here + | +LL | extern "C" fn read_dword<'s>(&'s self) -> u16 { + | ++++ +help: consider introducing lifetime `'s` here + | +LL | impl<'s> MemoryUnit for ROM { + | ++++ + +warning: anonymous parameters are deprecated and will be removed in the next edition + --> $DIR/ice-mutability-error-slicing-121807.rs:7:30 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ^^^^^^^^^^^^^^^ help: try naming the parameter or explicitly ignoring it: `_: Self::Assoc<'_>` + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + = note: for more information, see issue #41686 <https://github.com/rust-lang/rust/issues/41686> + = note: `#[warn(anonymous_parameters)]` on by default + +error[E0220]: associated type `Assoc` not found for `Self` + --> $DIR/ice-mutability-error-slicing-121807.rs:7:36 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ^^^^^ associated type `Assoc` not found + +error[E0185]: method `read_dword` has a `&self` declaration in the impl, but not in the trait + --> $DIR/ice-mutability-error-slicing-121807.rs:17:5 + | +LL | extern "C" fn read_dword(Self::Assoc<'_>) -> u16; + | ------------------------------------------------- trait method declared without `&self` +... +LL | extern "C" fn read_dword(&'s self) -> u16 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&self` used in impl + +error[E0046]: not all trait items implemented, missing: `read_word` + --> $DIR/ice-mutability-error-slicing-121807.rs:15:1 + | +LL | extern "C" fn read_word(&mut self) -> u8; + | ----------------------------------------- `read_word` from trait +... +LL | impl MemoryUnit for ROM { + | ^^^^^^^^^^^^^^^^^^^^^^^ missing `read_word` in implementation + +error: aborting due to 4 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0046, E0185, E0220, E0261. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs b/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs index 15be5fb3fac..ebffa237f96 100644 --- a/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs +++ b/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs @@ -15,7 +15,9 @@ impl MarketMultiplier { async fn buy_lock(coroutine: &Mutex<MarketMultiplier>) -> LockedMarket<'_> { //~^ ERROR struct takes 0 lifetime arguments but 1 lifetime argument was supplied - //~^^ ERROR struct takes 1 generic argument but 0 generic arguments were supplied + //~| ERROR struct takes 1 generic argument but 0 generic arguments were supplied + //~| ERROR struct takes 0 lifetime arguments but 1 lifetime argument was supplied + //~| ERROR struct takes 1 generic argument but 0 generic arguments were supplied LockedMarket(coroutine.lock().unwrap().buy()) } diff --git a/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr b/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr index 516c1d065e6..c0b6dcd1512 100644 --- a/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr +++ b/tests/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr @@ -7,7 +7,7 @@ LL | async fn buy_lock(coroutine: &Mutex<MarketMultiplier>) -> LockedMarket<'_> | expected 0 lifetime arguments | note: struct defined here, with 0 lifetime parameters - --> $DIR/issue-82126-mismatched-subst-and-hir.rs:22:8 + --> $DIR/issue-82126-mismatched-subst-and-hir.rs:24:8 | LL | struct LockedMarket<T>(T); | ^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | async fn buy_lock(coroutine: &Mutex<MarketMultiplier>) -> LockedMarket<'_> | ^^^^^^^^^^^^ expected 1 generic argument | note: struct defined here, with 1 generic parameter: `T` - --> $DIR/issue-82126-mismatched-subst-and-hir.rs:22:8 + --> $DIR/issue-82126-mismatched-subst-and-hir.rs:24:8 | LL | struct LockedMarket<T>(T); | ^^^^^^^^^^^^ - @@ -28,6 +28,38 @@ help: add missing generic argument LL | async fn buy_lock(coroutine: &Mutex<MarketMultiplier>) -> LockedMarket<'_, T> { | +++ -error: aborting due to 2 previous errors +error[E0107]: struct takes 0 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/issue-82126-mismatched-subst-and-hir.rs:16:59 + | +LL | async fn buy_lock(coroutine: &Mutex<MarketMultiplier>) -> LockedMarket<'_> { + | ^^^^^^^^^^^^---- help: remove these generics + | | + | expected 0 lifetime arguments + | +note: struct defined here, with 0 lifetime parameters + --> $DIR/issue-82126-mismatched-subst-and-hir.rs:24:8 + | +LL | struct LockedMarket<T>(T); + | ^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0107]: struct takes 1 generic argument but 0 generic arguments were supplied + --> $DIR/issue-82126-mismatched-subst-and-hir.rs:16:59 + | +LL | async fn buy_lock(coroutine: &Mutex<MarketMultiplier>) -> LockedMarket<'_> { + | ^^^^^^^^^^^^ expected 1 generic argument + | +note: struct defined here, with 1 generic parameter: `T` + --> $DIR/issue-82126-mismatched-subst-and-hir.rs:24:8 + | +LL | struct LockedMarket<T>(T); + | ^^^^^^^^^^^^ - + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing generic argument + | +LL | async fn buy_lock(coroutine: &Mutex<MarketMultiplier>) -> LockedMarket<'_, T> { + | +++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.rs b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.rs new file mode 100644 index 00000000000..b9e729bff62 --- /dev/null +++ b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.rs @@ -0,0 +1,26 @@ +// issue: rust-lang/rust#104779 +// ICE region infer, IndexMap: key not found + +struct Inv<'a>(&'a mut &'a ()); +enum Foo<T> { + Bar, + Var(T), +} +type Subtype = Foo<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>; +type Supertype = Foo<for<'a> fn(Inv<'a>, Inv<'a>)>; + +fn foo() -> impl Sized { +//~^ WARN function cannot return without recursing + loop { + match foo() { + //~^ ERROR higher-ranked subtype error + //~^^ ERROR higher-ranked subtype error + Subtype::Bar => (), + //~^ ERROR higher-ranked subtype error + //~^^ ERROR higher-ranked subtype error + Supertype::Var(x) => {} + } + } +} + +pub fn main() {} diff --git a/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr new file mode 100644 index 00000000000..887cb14a769 --- /dev/null +++ b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr @@ -0,0 +1,42 @@ +warning: function cannot return without recursing + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:12:1 + | +LL | fn foo() -> impl Sized { + | ^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +... +LL | match foo() { + | ----- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:15:15 + | +LL | match foo() { + | ^^^^^ + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:15:15 + | +LL | match foo() { + | ^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:18:13 + | +LL | Subtype::Bar => (), + | ^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:18:13 + | +LL | Subtype::Bar => (), + | ^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors; 1 warning emitted + diff --git a/tests/ui/cfg/cfg-false-feature.stderr b/tests/ui/cfg/cfg-false-feature.stderr index 9309b59ca59..542aeaf5caf 100644 --- a/tests/ui/cfg/cfg-false-feature.stderr +++ b/tests/ui/cfg/cfg-false-feature.stderr @@ -1,15 +1,3 @@ -warning: trait aliases are experimental - --> $DIR/cfg-false-feature.rs:12:1 - | -LL | trait A = Clone; - | ^^^^^^^^^^^^^^^^ - | - = note: see issue #41517 <https://github.com/rust-lang/rust/issues/41517> for more information - = help: add `#![feature(trait_alias)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - = warning: unstable syntax can change at any point in the future, causing a hard error! - = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860> - warning: box pattern syntax is experimental --> $DIR/cfg-false-feature.rs:16:9 | @@ -22,5 +10,17 @@ LL | let box _ = Box::new(0); = warning: unstable syntax can change at any point in the future, causing a hard error! = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860> +warning: trait aliases are experimental + --> $DIR/cfg-false-feature.rs:12:1 + | +LL | trait A = Clone; + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #41517 <https://github.com/rust-lang/rust/issues/41517> for more information + = help: add `#![feature(trait_alias)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860> + warning: 2 warnings emitted diff --git a/tests/ui/const-generics/generic_const_exprs/ice-generics_of-no-entry-found-for-key-113017.rs b/tests/ui/const-generics/generic_const_exprs/ice-generics_of-no-entry-found-for-key-113017.rs new file mode 100644 index 00000000000..a2f8c876b5e --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/ice-generics_of-no-entry-found-for-key-113017.rs @@ -0,0 +1,13 @@ +// test for ICE "no entry found for key" in generics_of.rs #113017 + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +pub fn foo() +where + for<const N: usize = { || {}; 1 }> ():, + //~^ ERROR only lifetime parameters can be used in this context + //~^^ ERROR defaults for generic parameters are not allowed in `for<...>` binders +{} + +pub fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/ice-generics_of-no-entry-found-for-key-113017.stderr b/tests/ui/const-generics/generic_const_exprs/ice-generics_of-no-entry-found-for-key-113017.stderr new file mode 100644 index 00000000000..edf27f58efd --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/ice-generics_of-no-entry-found-for-key-113017.stderr @@ -0,0 +1,19 @@ +error[E0658]: only lifetime parameters can be used in this context + --> $DIR/ice-generics_of-no-entry-found-for-key-113017.rs:8:15 + | +LL | for<const N: usize = { || {}; 1 }> ():, + | ^ + | + = note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information + = help: add `#![feature(non_lifetime_binders)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: defaults for generic parameters are not allowed in `for<...>` binders + --> $DIR/ice-generics_of-no-entry-found-for-key-113017.rs:8:9 + | +LL | for<const N: usize = { || {}; 1 }> ():, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.rs b/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.rs new file mode 100644 index 00000000000..4ba696f4ae0 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.rs @@ -0,0 +1,18 @@ +// test for ICE #119275 "no entry found for key" in predicates_of.rs + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +fn bug<const N: Nat>(&self) +//~^ ERROR `self` parameter is only allowed in associated functions +//~^^ ERROR cannot find type `Nat` in this scope +where + for<const N: usize = 3, T = u32> [(); COT::BYTES]:, + //~^ ERROR only lifetime parameters can be used in this context + //~^^ ERROR defaults for generic parameters are not allowed in `for<...>` binders + //~^^^ ERROR defaults for generic parameters are not allowed in `for<...>` binders + //~^^^^ ERROR failed to resolve: use of undeclared type `COT` +{ +} + +pub fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.stderr b/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.stderr new file mode 100644 index 00000000000..ee0ec38ab06 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/ice-predicates-of-no-entry-found-for-key-119275.stderr @@ -0,0 +1,46 @@ +error: `self` parameter is only allowed in associated functions + --> $DIR/ice-predicates-of-no-entry-found-for-key-119275.rs:6:22 + | +LL | fn bug<const N: Nat>(&self) + | ^^^^^ not semantically valid as function parameter + | + = note: associated functions are those in `impl` or `trait` definitions + +error[E0412]: cannot find type `Nat` in this scope + --> $DIR/ice-predicates-of-no-entry-found-for-key-119275.rs:6:17 + | +LL | fn bug<const N: Nat>(&self) + | ^^^ not found in this scope + +error[E0658]: only lifetime parameters can be used in this context + --> $DIR/ice-predicates-of-no-entry-found-for-key-119275.rs:10:15 + | +LL | for<const N: usize = 3, T = u32> [(); COT::BYTES]:, + | ^ ^ + | + = note: see issue #108185 <https://github.com/rust-lang/rust/issues/108185> for more information + = help: add `#![feature(non_lifetime_binders)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: defaults for generic parameters are not allowed in `for<...>` binders + --> $DIR/ice-predicates-of-no-entry-found-for-key-119275.rs:10:9 + | +LL | for<const N: usize = 3, T = u32> [(); COT::BYTES]:, + | ^^^^^^^^^^^^^^^^^^ + +error: defaults for generic parameters are not allowed in `for<...>` binders + --> $DIR/ice-predicates-of-no-entry-found-for-key-119275.rs:10:29 + | +LL | for<const N: usize = 3, T = u32> [(); COT::BYTES]:, + | ^^^^^^^ + +error[E0433]: failed to resolve: use of undeclared type `COT` + --> $DIR/ice-predicates-of-no-entry-found-for-key-119275.rs:10:43 + | +LL | for<const N: usize = 3, T = u32> [(); COT::BYTES]:, + | ^^^ use of undeclared type `COT` + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0412, E0433, E0658. +For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/const-generics/generic_const_exprs/issue-109141.rs b/tests/ui/const-generics/generic_const_exprs/issue-109141.rs index 148c3bda8d2..c6dd981cced 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-109141.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-109141.rs @@ -4,6 +4,7 @@ impl EntriesBuffer { fn a(&self) -> impl Iterator { self.0.iter_mut() //~ ERROR: cannot borrow `*self.0` as mutable, as it is behind a `&` reference + //~| ERROR captures lifetime that does not appear in bounds } } diff --git a/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr b/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr index 8b8489ac2bc..7a9572d000d 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `HashesEntryLEN` in this scope - --> $DIR/issue-109141.rs:10:32 + --> $DIR/issue-109141.rs:11:32 | LL | struct EntriesBuffer(Box<[[u8; HashesEntryLEN]; 5]>); | ^^^^^^^^^^^^^^ not found in this scope @@ -20,7 +20,22 @@ help: consider changing this to be a mutable reference LL | fn a(&mut self) -> impl Iterator { | ~~~~~~~~~ -error: aborting due to 2 previous errors +error[E0700]: hidden type for `impl Iterator` captures lifetime that does not appear in bounds + --> $DIR/issue-109141.rs:6:9 + | +LL | fn a(&self) -> impl Iterator { + | ----- ------------- opaque type defined here + | | + | hidden type `std::slice::IterMut<'_, [u8; {const error}]>` captures the anonymous lifetime defined here +LL | self.0.iter_mut() + | ^^^^^^^^^^^^^^^^^ + | +help: to declare that `impl Iterator` captures `'_`, you can add an explicit `'_` lifetime bound + | +LL | fn a(&self) -> impl Iterator + '_ { + | ++++ + +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0425, E0596. +Some errors have detailed explanations: E0425, E0596, E0700. For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/const-generics/generic_const_exprs/poly-const-uneval-ice-106423.rs b/tests/ui/const-generics/generic_const_exprs/poly-const-uneval-ice-106423.rs new file mode 100644 index 00000000000..ed5ba32b621 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/poly-const-uneval-ice-106423.rs @@ -0,0 +1,57 @@ +// issue: rust-lang/rust#106423 +// ICE collection encountered polymorphic constant: UnevaluatedConst {..} +//@ edition:2021 +//@ check-pass + +#![feature(generic_const_exprs, generic_arg_infer)] +#![allow(incomplete_features)] +#![allow(unused)] + +use core::mem::MaybeUninit; + +pub struct Arr<T, const N: usize> { + v: [MaybeUninit<T>; N], +} + +impl<T, const N: usize> Arr<T, N> { + const ELEM: MaybeUninit<T> = MaybeUninit::uninit(); + const INIT: [MaybeUninit<T>; N] = [Self::ELEM; N]; // important for optimization of `new` + + fn new() -> Self { + Arr { v: Self::INIT } + } +} + +pub struct BaFormatFilter<const N: usize> {} + +pub enum DigitalFilter<const N: usize> +where + [(); N * 2 + 1]: Sized, + [(); N * 2]: Sized, +{ + Ba(BaFormatFilter<{ N * 2 + 1 }>), +} + +pub fn iirfilter_st_copy<const N: usize, const M: usize>(_: [f32; M]) -> DigitalFilter<N> +where + [(); N * 2 + 1]: Sized, + [(); N * 2]: Sized, +{ + let zpk = zpk2tf_st(&Arr::<f32, { N * 2 }>::new(), &Arr::<f32, { N * 2 }>::new()); + DigitalFilter::Ba(zpk) +} + +pub fn zpk2tf_st<const N: usize>( + _z: &Arr<f32, N>, + _p: &Arr<f32, N>, +) -> BaFormatFilter<{ N + 1 }> +where + [(); N + 1]: Sized, +{ + BaFormatFilter {} +} + + +fn main() { + iirfilter_st_copy::<4, 2>([10., 50.,]); +} diff --git a/tests/ui/const-generics/ice-unexpected-inference-var-122549.rs b/tests/ui/const-generics/ice-unexpected-inference-var-122549.rs new file mode 100644 index 00000000000..126ea667290 --- /dev/null +++ b/tests/ui/const-generics/ice-unexpected-inference-var-122549.rs @@ -0,0 +1,29 @@ +// Regression test for https://github.com/rust-lang/rust/issues/122549 +// was fixed by https://github.com/rust-lang/rust/pull/122749 + +trait ConstChunksExactTrait<T> { + fn const_chunks_exact<const N: usize>(&self) -> ConstChunksExact<'a, T, { N }>; + //~^ ERROR undeclared lifetime +} + +impl<T> ConstChunksExactTrait<T> for [T] {} +//~^ ERROR not all trait items implemented, missing: `const_chunks_exact` +struct ConstChunksExact<'rem, T: 'a, const N: usize> {} +//~^ ERROR use of undeclared lifetime name `'a` +//~^^ ERROR lifetime parameter +//~^^^ ERROR type parameter +impl<'a, T, const N: usize> Iterator for ConstChunksExact<'a, T, {}> { +//~^ ERROR the const parameter `N` is not constrained by the impl trait, self type, or predicates +//~^^ ERROR mismatched types + type Item = &'a [T; N]; +} + +fn main() { + let slice = &[1i32, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + let mut iter = [[1, 2, 3], [4, 5, 6], [7, 8, 9]].iter(); + + for a in slice.const_chunks_exact::<3>() { + assert_eq!(a, iter.next().unwrap()); + } +} diff --git a/tests/ui/const-generics/ice-unexpected-inference-var-122549.stderr b/tests/ui/const-generics/ice-unexpected-inference-var-122549.stderr new file mode 100644 index 00000000000..afad3388145 --- /dev/null +++ b/tests/ui/const-generics/ice-unexpected-inference-var-122549.stderr @@ -0,0 +1,67 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ice-unexpected-inference-var-122549.rs:5:70 + | +LL | fn const_chunks_exact<const N: usize>(&self) -> ConstChunksExact<'a, T, { N }>; + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | fn const_chunks_exact<'a, const N: usize>(&self) -> ConstChunksExact<'a, T, { N }>; + | +++ +help: consider introducing lifetime `'a` here + | +LL | trait ConstChunksExactTrait<'a, T> { + | +++ + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ice-unexpected-inference-var-122549.rs:11:34 + | +LL | struct ConstChunksExact<'rem, T: 'a, const N: usize> {} + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `'a,` + +error[E0046]: not all trait items implemented, missing: `const_chunks_exact` + --> $DIR/ice-unexpected-inference-var-122549.rs:9:1 + | +LL | fn const_chunks_exact<const N: usize>(&self) -> ConstChunksExact<'a, T, { N }>; + | ------------------------------------------------------------------------------- `const_chunks_exact` from trait +... +LL | impl<T> ConstChunksExactTrait<T> for [T] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `const_chunks_exact` in implementation + +error[E0392]: lifetime parameter `'rem` is never used + --> $DIR/ice-unexpected-inference-var-122549.rs:11:25 + | +LL | struct ConstChunksExact<'rem, T: 'a, const N: usize> {} + | ^^^^ unused lifetime parameter + | + = help: consider removing `'rem`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0392]: type parameter `T` is never used + --> $DIR/ice-unexpected-inference-var-122549.rs:11:31 + | +LL | struct ConstChunksExact<'rem, T: 'a, const N: usize> {} + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates + --> $DIR/ice-unexpected-inference-var-122549.rs:15:13 + | +LL | impl<'a, T, const N: usize> Iterator for ConstChunksExact<'a, T, {}> { + | ^^^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error[E0308]: mismatched types + --> $DIR/ice-unexpected-inference-var-122549.rs:15:66 + | +LL | impl<'a, T, const N: usize> Iterator for ConstChunksExact<'a, T, {}> { + | ^^ expected `usize`, found `()` + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0046, E0207, E0261, E0308, E0392. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr b/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr index e42bb6e8cc5..7bef98b1d5d 100644 --- a/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr +++ b/tests/ui/const-generics/late-bound-vars/late-bound-in-return-issue-77357.stderr @@ -4,46 +4,5 @@ error: cannot capture late-bound lifetime in constant LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { | -- lifetime defined here ^^ -error: overly complex generic constant - --> $DIR/late-bound-in-return-issue-77357.rs:9:46 - | -LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constants - | - = help: consider moving this anonymous constant into a `const` function - = note: this operation may be supported in the future - -error[E0391]: cycle detected when evaluating type-level constant - --> $DIR/late-bound-in-return-issue-77357.rs:9:46 - | -LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires const-evaluating + checking `bug::{constant#0}`... - --> $DIR/late-bound-in-return-issue-77357.rs:9:46 - | -LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires caching mir of `bug::{constant#0}` for CTFE... - --> $DIR/late-bound-in-return-issue-77357.rs:9:46 - | -LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires elaborating drops for `bug::{constant#0}`... - --> $DIR/late-bound-in-return-issue-77357.rs:9:46 - | -LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires borrow-checking `bug::{constant#0}`... - --> $DIR/late-bound-in-return-issue-77357.rs:9:46 - | -LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires normalizing `Binder { value: ConstEvaluatable(UnevaluatedConst { def: DefId(0:8 ~ late_bound_in_return_issue_77357[9394]::bug::{constant#0}), args: [T/#0] }: usize), bound_vars: [] }`... - = note: ...which again requires evaluating type-level constant, completing the cycle - = note: cycle used when normalizing `&dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]>` - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/const-generics/min_const_generics/macro-fail.rs b/tests/ui/const-generics/min_const_generics/macro-fail.rs index f3df96d468c..2f101ecfb1f 100644 --- a/tests/ui/const-generics/min_const_generics/macro-fail.rs +++ b/tests/ui/const-generics/min_const_generics/macro-fail.rs @@ -13,6 +13,7 @@ impl<const N: usize> Marker<N> for Example<N> {} fn make_marker() -> impl Marker<gimme_a_const!(marker)> { //~^ ERROR: type provided when a constant was expected + //~| ERROR: type provided when a constant was expected Example::<gimme_a_const!(marker)> //~^ ERROR: type provided when a constant was expected } diff --git a/tests/ui/const-generics/min_const_generics/macro-fail.stderr b/tests/ui/const-generics/min_const_generics/macro-fail.stderr index 06a111008a3..34764982bb0 100644 --- a/tests/ui/const-generics/min_const_generics/macro-fail.stderr +++ b/tests/ui/const-generics/min_const_generics/macro-fail.stderr @@ -1,5 +1,5 @@ error: expected type, found `{` - --> $DIR/macro-fail.rs:29:27 + --> $DIR/macro-fail.rs:30:27 | LL | fn make_marker() -> impl Marker<gimme_a_const!(marker)> { | ---------------------- @@ -13,7 +13,7 @@ LL | ($rusty: ident) => {{ let $rusty = 3; *&$rusty }} = note: this error originates in the macro `gimme_a_const` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected type, found `{` - --> $DIR/macro-fail.rs:29:27 + --> $DIR/macro-fail.rs:30:27 | LL | Example::<gimme_a_const!(marker)> | ---------------------- @@ -41,7 +41,7 @@ LL | let _fail = Example::<external_macro!()>; = note: this error originates in the macro `external_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: unexpected end of macro invocation - --> $DIR/macro-fail.rs:39:25 + --> $DIR/macro-fail.rs:40:25 | LL | macro_rules! gimme_a_const { | -------------------------- when calling this macro @@ -50,7 +50,7 @@ LL | let _fail = Example::<gimme_a_const!()>; | ^^^^^^^^^^^^^^^^ missing tokens in macro arguments | note: while trying to match meta-variable `$rusty:ident` - --> $DIR/macro-fail.rs:29:8 + --> $DIR/macro-fail.rs:30:8 | LL | ($rusty: ident) => {{ let $rusty = 3; *&$rusty }} | ^^^^^^^^^^^^^ @@ -62,23 +62,31 @@ LL | fn make_marker() -> impl Marker<gimme_a_const!(marker)> { | ^^^^^^^^^^^^^^^^^^^^^^ error[E0747]: type provided when a constant was expected - --> $DIR/macro-fail.rs:16:13 + --> $DIR/macro-fail.rs:14:33 + | +LL | fn make_marker() -> impl Marker<gimme_a_const!(marker)> { + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0747]: type provided when a constant was expected + --> $DIR/macro-fail.rs:17:13 | LL | Example::<gimme_a_const!(marker)> | ^^^^^^^^^^^^^^^^^^^^^^ error[E0747]: type provided when a constant was expected - --> $DIR/macro-fail.rs:36:25 + --> $DIR/macro-fail.rs:37:25 | LL | let _fail = Example::<external_macro!()>; | ^^^^^^^^^^^^^^^^^ error[E0747]: type provided when a constant was expected - --> $DIR/macro-fail.rs:39:25 + --> $DIR/macro-fail.rs:40:25 | LL | let _fail = Example::<gimme_a_const!()>; | ^^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0747`. diff --git a/tests/ui/const-generics/occurs-check/unify-fixpoint.rs b/tests/ui/const-generics/occurs-check/unify-fixpoint.rs index 1c1ed41051d..02bc90988e2 100644 --- a/tests/ui/const-generics/occurs-check/unify-fixpoint.rs +++ b/tests/ui/const-generics/occurs-check/unify-fixpoint.rs @@ -1,3 +1,8 @@ +// -Zunstable-options added as test for ICE #97725 (left == right)` +// left: `Binder(<[u8; _] as std::default::Default>, [])`, +// right: `Binder(<[u8; 4] as std::default::Default>, []) + +//@ compile-flags: -Zunstable-options //@ check-pass #![feature(generic_const_exprs)] //~ WARN the feature `generic_const_exprs` is incomplete diff --git a/tests/ui/const-generics/occurs-check/unify-fixpoint.stderr b/tests/ui/const-generics/occurs-check/unify-fixpoint.stderr index fe3f24a67a2..8b63e8c55d5 100644 --- a/tests/ui/const-generics/occurs-check/unify-fixpoint.stderr +++ b/tests/ui/const-generics/occurs-check/unify-fixpoint.stderr @@ -1,5 +1,5 @@ warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/unify-fixpoint.rs:2:12 + --> $DIR/unify-fixpoint.rs:7:12 | LL | #![feature(generic_const_exprs)] | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const-generics/transmute-fail.rs b/tests/ui/const-generics/transmute-fail.rs index d7bf1b47fb5..90afd232534 100644 --- a/tests/ui/const-generics/transmute-fail.rs +++ b/tests/ui/const-generics/transmute-fail.rs @@ -32,4 +32,79 @@ fn overflow(v: [[[u32; 8888888]; 9999999]; 777777777]) -> [[[u32; 9999999]; 7777 } } +fn transpose<const W: usize, const H: usize>(v: [[u32;H]; W]) -> [[u32; W]; H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn ident<const W: usize, const H: usize>(v: [[u32; H]; W]) -> [[u32; H]; W] { + unsafe { + std::mem::transmute(v) + } +} + +fn flatten<const W: usize, const H: usize>(v: [[u32; H]; W]) -> [u32; W * H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn coagulate<const W: usize, const H: usize>(v: [u32; H*W]) -> [[u32; W];H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn flatten_3d<const W: usize, const H: usize, const D: usize>( + v: [[[u32; D]; H]; W] +) -> [u32; D * W * H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn flatten_somewhat<const W: usize, const H: usize, const D: usize>( + v: [[[u32; D]; H]; W] +) -> [[u32; D * W]; H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn known_size<const L: usize>(v: [u16; L]) -> [u8; L * 2] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn condense_bytes<const L: usize>(v: [u8; L * 2]) -> [u16; L] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn singleton_each<const L: usize>(v: [u8; L]) -> [[u8;1]; L] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + +fn transpose_with_const<const W: usize, const H: usize>( + v: [[u32; 2 * H]; W + W] +) -> [[u32; W + W]; 2 * H] { + unsafe { + std::mem::transmute(v) + //~^ ERROR: cannot transmute between types of different sizes, or dependently-sized types + } +} + fn main() {} diff --git a/tests/ui/const-generics/transmute-fail.stderr b/tests/ui/const-generics/transmute-fail.stderr index 397e3be768a..1b0d1ea50d0 100644 --- a/tests/ui/const-generics/transmute-fail.stderr +++ b/tests/ui/const-generics/transmute-fail.stderr @@ -4,8 +4,8 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | std::mem::transmute(v) | ^^^^^^^^^^^^^^^^^^^ | - = note: source type: `[[u32; H+1]; W]` (generic size (H + 1) * 4 * W) - = note: target type: `[[u32; W+1]; H]` (generic size (W + 1) * 4 * H) + = note: source type: `[[u32; H+1]; W]` (size can vary because of [u32; H+1]) + = note: target type: `[[u32; W+1]; H]` (size can vary because of [u32; W+1]) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:16:5 @@ -22,8 +22,8 @@ error[E0512]: cannot transmute between types of different sizes, or dependently- LL | std::mem::transmute(v) | ^^^^^^^^^^^^^^^^^^^ | - = note: source type: `[[u32; H]; W]` (generic size 4 * H * W) - = note: target type: `[u32; W * H * H]` (generic size 4 * H * H * W) + = note: source type: `[[u32; H]; W]` (size can vary because of [u32; H]) + = note: target type: `[u32; W * H * H]` (this type does not have a fixed size) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:30:5 @@ -34,6 +34,87 @@ LL | std::mem::transmute(v) = note: source type: `[[[u32; 8888888]; 9999999]; 777777777]` (values of the type `[[u32; 8888888]; 9999999]` are too big for the current architecture) = note: target type: `[[[u32; 9999999]; 777777777]; 8888888]` (values of the type `[[u32; 9999999]; 777777777]` are too big for the current architecture) +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:37:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[[u32; H]; W]` (size can vary because of [u32; H]) + = note: target type: `[[u32; W]; H]` (size can vary because of [u32; W]) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:50:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[[u32; H]; W]` (size can vary because of [u32; H]) + = note: target type: `[u32; W * H]` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:57:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u32; H*W]` (this type does not have a fixed size) + = note: target type: `[[u32; W]; H]` (size can vary because of [u32; W]) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:66:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[[[u32; D]; H]; W]` (size can vary because of [u32; D]) + = note: target type: `[u32; D * W * H]` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:75:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[[[u32; D]; H]; W]` (size can vary because of [u32; D]) + = note: target type: `[[u32; D * W]; H]` (size can vary because of [u32; D * W]) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:82:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u16; L]` (this type does not have a fixed size) + = note: target type: `[u8; L * 2]` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:89:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; L * 2]` (this type does not have a fixed size) + = note: target type: `[u16; L]` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:96:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[u8; L]` (this type does not have a fixed size) + = note: target type: `[[u8; 1]; L]` (this type does not have a fixed size) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-fail.rs:105:5 + | +LL | std::mem::transmute(v) + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `[[u32; 2 * H]; W + W]` (size can vary because of [u32; 2 * H]) + = note: target type: `[[u32; W + W]; 2 * H]` (size can vary because of [u32; W + W]) + error[E0308]: mismatched types --> $DIR/transmute-fail.rs:12:53 | @@ -46,7 +127,7 @@ error[E0308]: mismatched types LL | fn bar<const W: bool, const H: usize>(v: [[u32; H]; W]) -> [[u32; W]; H] { | ^ expected `usize`, found `bool` -error: aborting due to 6 previous errors +error: aborting due to 15 previous errors Some errors have detailed explanations: E0308, E0512. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/transmute.rs b/tests/ui/const-generics/transmute.rs index 245fcf5670e..e8ab8637932 100644 --- a/tests/ui/const-generics/transmute.rs +++ b/tests/ui/const-generics/transmute.rs @@ -3,81 +3,12 @@ #![feature(transmute_generic_consts)] #![allow(incomplete_features)] -fn transpose<const W: usize, const H: usize>(v: [[u32;H]; W]) -> [[u32; W]; H] { - unsafe { - std::mem::transmute(v) - } -} - fn ident<const W: usize, const H: usize>(v: [[u32; H]; W]) -> [[u32; H]; W] { unsafe { std::mem::transmute(v) } } -fn flatten<const W: usize, const H: usize>(v: [[u32; H]; W]) -> [u32; W * H] { - unsafe { - std::mem::transmute(v) - } -} - -fn coagulate<const W: usize, const H: usize>(v: [u32; H*W]) -> [[u32; W];H] { - unsafe { - std::mem::transmute(v) - } -} - -fn flatten_3d<const W: usize, const H: usize, const D: usize>( - v: [[[u32; D]; H]; W] -) -> [u32; D * W * H] { - unsafe { - std::mem::transmute(v) - } -} - -fn flatten_somewhat<const W: usize, const H: usize, const D: usize>( - v: [[[u32; D]; H]; W] -) -> [[u32; D * W]; H] { - unsafe { - std::mem::transmute(v) - } -} - -fn known_size<const L: usize>(v: [u16; L]) -> [u8; L * 2] { - unsafe { - std::mem::transmute(v) - } -} - -fn condense_bytes<const L: usize>(v: [u8; L * 2]) -> [u16; L] { - unsafe { - std::mem::transmute(v) - } -} - -fn singleton_each<const L: usize>(v: [u8; L]) -> [[u8;1]; L] { - unsafe { - std::mem::transmute(v) - } -} - -fn transpose_with_const<const W: usize, const H: usize>( - v: [[u32; 2 * H]; W + W] -) -> [[u32; W + W]; 2 * H] { - unsafe { - std::mem::transmute(v) - } -} - fn main() { - let _ = transpose([[0; 8]; 16]); - let _ = transpose_with_const::<8,4>([[0; 8]; 16]); let _ = ident([[0; 8]; 16]); - let _ = flatten([[0; 13]; 5]); - let _: [[_; 5]; 13] = coagulate([0; 65]); - let _ = flatten_3d([[[0; 3]; 13]; 5]); - let _ = flatten_somewhat([[[0; 3]; 13]; 5]); - let _ = known_size([16; 13]); - let _: [u16; 5] = condense_bytes([16u8; 10]); - let _ = singleton_each([16; 10]); } diff --git a/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.rs b/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.rs new file mode 100644 index 00000000000..dc9782295c1 --- /dev/null +++ b/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.rs @@ -0,0 +1,20 @@ +// test for #112824 ICE type mismatching when copying! + +pub struct Opcode(pub u8); + +pub struct Opcode2(&'a S); +//~^ ERROR use of undeclared lifetime name `'a` +//~^^ ERROR cannot find type `S` in this scope + +impl Opcode2 { + pub const OP2: Opcode2 = Opcode2(Opcode(0x1)); +} + +pub fn example2(msg_type: Opcode2) -> impl FnMut(&[u8]) { + move |i| match msg_type { + Opcode2::OP2 => unimplemented!(), + //~^ ERROR could not evaluate constant pattern + } +} + +pub fn main() {} diff --git a/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.stderr b/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.stderr new file mode 100644 index 00000000000..9442eac0cf5 --- /dev/null +++ b/tests/ui/const_prop/ice-type-mismatch-when-copying-112824.stderr @@ -0,0 +1,29 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ice-type-mismatch-when-copying-112824.rs:5:21 + | +LL | pub struct Opcode2(&'a S); + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'a` here: `<'a>` + +error[E0412]: cannot find type `S` in this scope + --> $DIR/ice-type-mismatch-when-copying-112824.rs:5:24 + | +LL | pub struct Opcode2(&'a S); + | ^ not found in this scope + | +help: you might be missing a type parameter + | +LL | pub struct Opcode2<S>(&'a S); + | +++ + +error: could not evaluate constant pattern + --> $DIR/ice-type-mismatch-when-copying-112824.rs:15:9 + | +LL | Opcode2::OP2 => unimplemented!(), + | ^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0261, E0412. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr index 3e3e8e976be..9e0506e7e38 100644 --- a/tests/ui/consts/missing_span_in_backtrace.stderr +++ b/tests/ui/consts/missing_span_in_backtrace.stderr @@ -5,8 +5,6 @@ error[E0080]: evaluation of constant value failed | note: inside `std::ptr::read::<MaybeUninit<MaybeUninit<u8>>>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `mem::swap_simple::<MaybeUninit<MaybeUninit<u8>>>` - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL note: inside `std::ptr::swap_nonoverlapping_simple_untyped::<MaybeUninit<u8>>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL note: inside `swap_nonoverlapping::<MaybeUninit<u8>>` diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr index 14a4cb0217f..c3b641a899a 100644 --- a/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::<i32>::C` failed - --> $DIR/collect-in-called-fn.rs:9:19 + --> $DIR/collect-in-called-fn.rs:10:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:10:19 | = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/collect-in-called-fn.rs:18:17 + --> $DIR/collect-in-called-fn.rs:19:17 | LL | let _ = Fail::<T>::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn called::<i32>` - --> $DIR/collect-in-called-fn.rs:23:5 + --> $DIR/collect-in-called-fn.rs:24:5 | LL | called::<i32>(); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr index 14a4cb0217f..c3b641a899a 100644 --- a/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::<i32>::C` failed - --> $DIR/collect-in-called-fn.rs:9:19 + --> $DIR/collect-in-called-fn.rs:10:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:9:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:10:19 | = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/collect-in-called-fn.rs:18:17 + --> $DIR/collect-in-called-fn.rs:19:17 | LL | let _ = Fail::<T>::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn called::<i32>` - --> $DIR/collect-in-called-fn.rs:23:5 + --> $DIR/collect-in-called-fn.rs:24:5 | LL | called::<i32>(); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.rs b/tests/ui/consts/required-consts/collect-in-called-fn.rs index 55133a10cd9..93947950af2 100644 --- a/tests/ui/consts/required-consts/collect-in-called-fn.rs +++ b/tests/ui/consts/required-consts/collect-in-called-fn.rs @@ -1,5 +1,6 @@ //@revisions: noopt opt //@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is //! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr new file mode 100644 index 00000000000..75c3575a110 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.noopt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-closure.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-closure.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-closure.rs:17:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::<i32>` + --> $DIR/collect-in-dead-closure.rs:24:33 + | +LL | let _closure: fn() = || not_called::<T>(); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr new file mode 100644 index 00000000000..75c3575a110 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-closure.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-closure.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-closure.rs:17:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::<i32>` + --> $DIR/collect-in-dead-closure.rs:24:33 + | +LL | let _closure: fn() = || not_called::<T>(); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-closure.rs b/tests/ui/consts/required-consts/collect-in-dead-closure.rs new file mode 100644 index 00000000000..a00214c62db --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-closure.rs @@ -0,0 +1,30 @@ +//@revisions: noopt opt +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +struct Fail<T>(T); +impl<T> Fail<T> { + const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed +} + +// This function is not actually called, but it is mentioned in a closure that is coerced to a +// function pointer in dead code in a function that is called. Make sure we still find this error. +#[inline(never)] +fn not_called<T>() { + if false { + let _ = Fail::<T>::C; + } +} + +#[inline(never)] +fn called<T>() { + if false { + let _closure: fn() = || not_called::<T>(); + } +} + +pub fn main() { + called::<i32>(); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr index 0bf231d09f1..73790f7517d 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::<i32>::C` failed - --> $DIR/collect-in-dead-drop.rs:12:19 + --> $DIR/collect-in-dead-drop.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.rs:12:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.rs:9:19 | = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/collect-in-dead-drop.rs:19:17 + --> $DIR/collect-in-dead-drop.rs:16:17 | LL | let _ = Fail::<T>::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr new file mode 100644 index 00000000000..73790f7517d --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-drop.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-drop.rs:16:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn <Fail<i32> as std::ops::Drop>::drop` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.rs b/tests/ui/consts/required-consts/collect-in-dead-drop.rs index c9ffcec6903..389fcf5dfc9 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-drop.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.rs @@ -1,15 +1,12 @@ //@revisions: noopt opt -//@[noopt] build-fail +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O -//FIXME: `opt` revision currently does not stop with an error due to -//<https://github.com/rust-lang/rust/issues/107503>. -//@[opt] build-pass -//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is -//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) +//! This fails without optimizations, so it should also fail with optimizations. struct Fail<T>(T); impl<T> Fail<T> { - const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::<i32>::C` failed + const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed } // This function is not actually called, but is mentioned implicitly as destructor in dead code in a diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr new file mode 100644 index 00000000000..706c0d55b62 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:10:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.rs:10:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:16:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::<i32>` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr new file mode 100644 index 00000000000..706c0d55b62 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:10:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-assoc-type.rs:10:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fn-behind-assoc-type.rs:16:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::<i32>` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs new file mode 100644 index 00000000000..9c36af50bb7 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-assoc-type.rs @@ -0,0 +1,49 @@ +#![feature(impl_trait_in_assoc_type)] +//@revisions: noopt opt +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +struct Fail<T>(T); +impl<T> Fail<T> { + const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed +} + +#[inline(never)] +fn not_called<T>() { + if false { + let _ = Fail::<T>::C; + } +} + +#[inline(never)] +fn callit_not(f: impl Fn()) { + if false { + f(); + } +} + +// Using `Fn` here is important; with `FnOnce` another shim gets involved which somehow makes this +// easier to collect properly. +trait Hideaway { + type T: Fn(); + const C: Self::T; +} +impl Hideaway for () { + type T = impl Fn(); + const C: Self::T = not_called::<i32>; +} + +#[inline(never)] +fn reveal<T: Hideaway>() { + if false { + callit_not(T::C); + } +} + +fn main() { + if false { + reveal::<()>() + } +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr new file mode 100644 index 00000000000..581edd2b7b8 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-fn-behind-generic.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-generic.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fn-behind-generic.rs:15:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::<i32>` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr new file mode 100644 index 00000000000..581edd2b7b8 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-fn-behind-generic.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-generic.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fn-behind-generic.rs:15:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::<i32>` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs new file mode 100644 index 00000000000..5829d914ee1 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-generic.rs @@ -0,0 +1,30 @@ +//@revisions: noopt opt +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +struct Fail<T>(T); +impl<T> Fail<T> { + const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed +} + +#[inline(never)] +fn not_called<T>() { + if false { + let _ = Fail::<T>::C; + } +} + +#[inline(never)] +fn callit_not(f: impl Fn()) { + if false { + f(); + } +} + +fn main() { + if false { + callit_not(not_called::<i32>) + } +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr new file mode 100644 index 00000000000..07e46b8a816 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `m::Fail::<i32>::C` failed + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:11:23 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-opaque-type.rs:11:23 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:19:21 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn m::not_called::<i32>` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr new file mode 100644 index 00000000000..07e46b8a816 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `m::Fail::<i32>::C` failed + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:11:23 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn-behind-opaque-type.rs:11:23 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fn-behind-opaque-type.rs:19:21 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn m::not_called::<i32>` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs new file mode 100644 index 00000000000..795e021ceb0 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn-behind-opaque-type.rs @@ -0,0 +1,37 @@ +//@revisions: noopt opt +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. +#![feature(type_alias_impl_trait)] + +mod m { + struct Fail<T>(T); + impl<T> Fail<T> { + const C: () = panic!(); //~ERROR evaluation of `m::Fail::<i32>::C` failed + } + + pub type NotCalledFn = impl Fn(); + + #[inline(never)] + fn not_called<T>() { + if false { + let _ = Fail::<T>::C; + } + } + + fn mk_not_called() -> NotCalledFn { + not_called::<i32> + } +} + +fn main() { + // This does not involve a constant of `FnDef` type, it generates the value via unsafe + // shenanigans instead. This ensures that we check all `FnDef` types that occur in a function, + // not just those of constants. Furthermore the `FnDef` is behind an opaque type which bust be + // normalized away to reveal the function type. + if false { + let x: m::NotCalledFn = unsafe { std::mem::transmute(()) }; + x(); + } +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr index 8bb99efe8e4..52462076ff9 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr @@ -1,19 +1,19 @@ error[E0080]: evaluation of `Fail::<i32>::C` failed - --> $DIR/collect-in-dead-fn.rs:12:19 + --> $DIR/collect-in-dead-fn.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn.rs:12:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn.rs:9:19 | = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/collect-in-dead-fn.rs:22:17 + --> $DIR/collect-in-dead-fn.rs:19:17 | LL | let _ = Fail::<T>::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn not_called::<i32>` - --> $DIR/collect-in-dead-fn.rs:29:9 + --> $DIR/collect-in-dead-fn.rs:26:9 | LL | not_called::<T>(); | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr new file mode 100644 index 00000000000..52462076ff9 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-fn.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fn.rs:19:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::<i32>` + --> $DIR/collect-in-dead-fn.rs:26:9 + | +LL | not_called::<T>(); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.rs b/tests/ui/consts/required-consts/collect-in-dead-fn.rs index 9e6b1519153..1c95e0c303f 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-fn.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.rs @@ -1,15 +1,12 @@ //@revisions: noopt opt -//@[noopt] build-fail +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O -//FIXME: `opt` revision currently does not stop with an error due to -//<https://github.com/rust-lang/rust/issues/107503>. -//@[opt] build-pass -//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is -//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) +//! This fails without optimizations, so it should also fail with optimizations. struct Fail<T>(T); impl<T> Fail<T> { - const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::<i32>::C` failed + const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed } // This function is not actually called, but it is mentioned in dead code in a function that is diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr new file mode 100644 index 00000000000..dea2a342383 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.noopt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Late::<i32>::FAIL` failed + --> $DIR/collect-in-dead-fnptr-in-const.rs:9:22 + | +LL | const FAIL: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr-in-const.rs:9:22 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fnptr-in-const.rs:10:28 + | +LL | const FNPTR: fn() = || Self::FAIL; + | ^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn Late::<i32>::FNPTR::{closure#0}` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr new file mode 100644 index 00000000000..dea2a342383 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Late::<i32>::FAIL` failed + --> $DIR/collect-in-dead-fnptr-in-const.rs:9:22 + | +LL | const FAIL: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr-in-const.rs:9:22 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fnptr-in-const.rs:10:28 + | +LL | const FNPTR: fn() = || Self::FAIL; + | ^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn Late::<i32>::FNPTR::{closure#0}` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.rs b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.rs new file mode 100644 index 00000000000..8b6344c93f3 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr-in-const.rs @@ -0,0 +1,34 @@ +//@revisions: noopt opt +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +struct Late<T>(T); +impl<T> Late<T> { + const FAIL: () = panic!(); //~ERROR evaluation of `Late::<i32>::FAIL` failed + const FNPTR: fn() = || Self::FAIL; +} + +// This function is not actually called, but it is mentioned in dead code in a function that is +// called. The function then mentions a const that evaluates to a fnptr that points to a function +// that used a const that fails to evaluate. +// This tests that when processing mentioned items, we also check the fnptrs in the final values +// of consts that we encounter. +#[inline(never)] +fn not_called<T>() { + if false { + let _ = Late::<T>::FNPTR; + } +} + +#[inline(never)] +fn called<T>() { + if false { + not_called::<T>(); + } +} + +pub fn main() { + called::<i32>(); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr new file mode 100644 index 00000000000..51c68782687 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.noopt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-fnptr.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fnptr.rs:18:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::<i32>` + --> $DIR/collect-in-dead-fnptr.rs:27:28 + | +LL | let _fnptr: fn() = not_called::<T>; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr new file mode 100644 index 00000000000..51c68782687 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-fnptr.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fnptr.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-fnptr.rs:18:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn not_called::<i32>` + --> $DIR/collect-in-dead-fnptr.rs:27:28 + | +LL | let _fnptr: fn() = not_called::<T>; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fnptr.rs b/tests/ui/consts/required-consts/collect-in-dead-fnptr.rs new file mode 100644 index 00000000000..acbe34829e8 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fnptr.rs @@ -0,0 +1,33 @@ +//@revisions: noopt opt +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 +//@[opt] compile-flags: -O +//! This fails without optimizations, so it should also fail with optimizations. + +struct Fail<T>(T); +impl<T> Fail<T> { + const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed +} + +// This function is not actually called, but it is mentioned in dead code in a function that is +// called. Make sure we still find this error. +// This ensures that we consider ReifyFnPointer coercions when gathering "mentioned" items. +#[inline(never)] +fn not_called<T>() { + if false { + let _ = Fail::<T>::C; + } +} + +#[inline(never)] +fn called<T>() { + if false { + // We don't call the function, but turn it to a function pointer. + // Make sure it still gest added to `mentioned_items`. + let _fnptr: fn() = not_called::<T>; + } +} + +pub fn main() { + called::<i32>(); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-forget.rs b/tests/ui/consts/required-consts/collect-in-dead-forget.rs index 720b7a499f7..7586004116c 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-forget.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-forget.rs @@ -1,8 +1,8 @@ //@revisions: noopt opt //@build-pass +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O -//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is -//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) +//! This passes without optimizations, so it can (and should) also pass with optimizations. struct Fail<T>(T); impl<T> Fail<T> { diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr index 5b1df78b232..2ab1f80e2d3 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::<i32>::C` failed - --> $DIR/collect-in-dead-move.rs:12:19 + --> $DIR/collect-in-dead-move.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.rs:12:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.rs:9:19 | = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/collect-in-dead-move.rs:19:17 + --> $DIR/collect-in-dead-move.rs:16:17 | LL | let _ = Fail::<T>::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr new file mode 100644 index 00000000000..2ab1f80e2d3 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-move.opt.stderr @@ -0,0 +1,20 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-move.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-move.rs:16:17 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn <Fail<i32> as std::ops::Drop>::drop` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.rs b/tests/ui/consts/required-consts/collect-in-dead-move.rs index f3a6ba8a657..6a224a375cf 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-move.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-move.rs @@ -1,15 +1,12 @@ //@revisions: noopt opt -//@[noopt] build-fail +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O -//FIXME: `opt` revision currently does not stop with an error due to -//<https://github.com/rust-lang/rust/issues/107503>. -//@[opt] build-pass -//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is -//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) +//! This fails without optimizations, so it should also fail with optimizations. struct Fail<T>(T); impl<T> Fail<T> { - const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::<i32>::C` failed + const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed } // This function is not actually called, but is mentioned implicitly as destructor in dead code in a diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr index 56b6989b441..b4e18706489 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr @@ -1,21 +1,21 @@ error[E0080]: evaluation of `Fail::<i32>::C` failed - --> $DIR/collect-in-dead-vtable.rs:12:19 + --> $DIR/collect-in-dead-vtable.rs:9:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.rs:12:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.rs:9:19 | = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/collect-in-dead-vtable.rs:26:21 + --> $DIR/collect-in-dead-vtable.rs:22:21 | LL | let _ = Fail::<T>::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn <std::vec::Vec<i32> as MyTrait>::not_called` - --> $DIR/collect-in-dead-vtable.rs:35:40 + --> $DIR/collect-in-dead-vtable.rs:31:40 | -LL | let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here +LL | let gen_vtable: &dyn MyTrait = &v; // vtable is "mentioned" here | ^^ error: aborting due to 1 previous error diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr new file mode 100644 index 00000000000..b4e18706489 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.opt.stderr @@ -0,0 +1,23 @@ +error[E0080]: evaluation of `Fail::<i32>::C` failed + --> $DIR/collect-in-dead-vtable.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/collect-in-dead-vtable.rs:22:21 + | +LL | let _ = Fail::<T>::C; + | ^^^^^^^^^^^^ + +note: the above error was encountered while instantiating `fn <std::vec::Vec<i32> as MyTrait>::not_called` + --> $DIR/collect-in-dead-vtable.rs:31:40 + | +LL | let gen_vtable: &dyn MyTrait = &v; // vtable is "mentioned" here + | ^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.rs b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs index f21a1cc1fc2..f63207eafec 100644 --- a/tests/ui/consts/required-consts/collect-in-dead-vtable.rs +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs @@ -1,15 +1,12 @@ //@revisions: noopt opt -//@[noopt] build-fail +//@ build-fail +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O -//FIXME: `opt` revision currently does not stop with an error due to -//<https://github.com/rust-lang/rust/issues/107503>. -//@[opt] build-pass -//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is -//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) +//! This fails without optimizations, so it should also fail with optimizations. struct Fail<T>(T); impl<T> Fail<T> { - const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::<i32>::C` failed + const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed } trait MyTrait { @@ -18,8 +15,7 @@ trait MyTrait { // This function is not actually called, but it is mentioned in a vtable in a function that is // called. Make sure we still find this error. -// This relies on mono-item collection checking `required_consts` in functions that are referenced -// in vtables that syntactically appear in collected functions (even inside dead code). +// This ensures that we are properly considering vtables when gathering "mentioned" items. impl<T> MyTrait for Vec<T> { fn not_called(&self) { if false { @@ -32,7 +28,7 @@ impl<T> MyTrait for Vec<T> { fn called<T>() { if false { let v: Vec<T> = Vec::new(); - let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here + let gen_vtable: &dyn MyTrait = &v; // vtable is "mentioned" here } } diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr index 75304591b9f..0e3bbbcc2ec 100644 --- a/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::<i32>::C` failed - --> $DIR/interpret-in-const-called-fn.rs:7:19 + --> $DIR/interpret-in-const-called-fn.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-const-called-fn.rs:7:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-const-called-fn.rs:8:19 | = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/interpret-in-const-called-fn.rs:16:9 + --> $DIR/interpret-in-const-called-fn.rs:17:9 | LL | Fail::<T>::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr b/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr index 75304591b9f..0e3bbbcc2ec 100644 --- a/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::<i32>::C` failed - --> $DIR/interpret-in-const-called-fn.rs:7:19 + --> $DIR/interpret-in-const-called-fn.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-const-called-fn.rs:7:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-const-called-fn.rs:8:19 | = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/interpret-in-const-called-fn.rs:16:9 + --> $DIR/interpret-in-const-called-fn.rs:17:9 | LL | Fail::<T>::C; | ^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs b/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs index c409fae0bb9..f2e83f56f37 100644 --- a/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs @@ -1,4 +1,5 @@ //@revisions: noopt opt +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they are unused. diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr index 491131daf8d..6ab991b6471 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr @@ -6,18 +6,18 @@ error[E0080]: evaluation of constant value failed note: inside `unreachable_unchecked` --> $SRC_DIR/core/src/hint.rs:LL:COL note: inside `ub` - --> $DIR/interpret-in-promoted.rs:6:5 + --> $DIR/interpret-in-promoted.rs:7:5 | LL | std::hint::unreachable_unchecked(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: inside `FOO` - --> $DIR/interpret-in-promoted.rs:12:28 + --> $DIR/interpret-in-promoted.rs:13:28 | LL | let _x: &'static () = &ub(); | ^^^^ note: erroneous constant encountered - --> $DIR/interpret-in-promoted.rs:12:27 + --> $DIR/interpret-in-promoted.rs:13:27 | LL | let _x: &'static () = &ub(); | ^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr index 491131daf8d..6ab991b6471 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr @@ -6,18 +6,18 @@ error[E0080]: evaluation of constant value failed note: inside `unreachable_unchecked` --> $SRC_DIR/core/src/hint.rs:LL:COL note: inside `ub` - --> $DIR/interpret-in-promoted.rs:6:5 + --> $DIR/interpret-in-promoted.rs:7:5 | LL | std::hint::unreachable_unchecked(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: inside `FOO` - --> $DIR/interpret-in-promoted.rs:12:28 + --> $DIR/interpret-in-promoted.rs:13:28 | LL | let _x: &'static () = &ub(); | ^^^^ note: erroneous constant encountered - --> $DIR/interpret-in-promoted.rs:12:27 + --> $DIR/interpret-in-promoted.rs:13:27 | LL | let _x: &'static () = &ub(); | ^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.rs b/tests/ui/consts/required-consts/interpret-in-promoted.rs index 9c2cf4e70d3..187494180ad 100644 --- a/tests/ui/consts/required-consts/interpret-in-promoted.rs +++ b/tests/ui/consts/required-consts/interpret-in-promoted.rs @@ -1,4 +1,5 @@ //@revisions: noopt opt +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they are unused. diff --git a/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr index 159c9449fc0..5e8da609e76 100644 --- a/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::<i32>::C` failed - --> $DIR/interpret-in-static.rs:7:19 + --> $DIR/interpret-in-static.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-static.rs:7:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-static.rs:8:19 | = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/interpret-in-static.rs:15:9 + --> $DIR/interpret-in-static.rs:16:9 | LL | Fail::<i32>::C; | ^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-static.opt.stderr b/tests/ui/consts/required-consts/interpret-in-static.opt.stderr index 159c9449fc0..5e8da609e76 100644 --- a/tests/ui/consts/required-consts/interpret-in-static.opt.stderr +++ b/tests/ui/consts/required-consts/interpret-in-static.opt.stderr @@ -1,13 +1,13 @@ error[E0080]: evaluation of `Fail::<i32>::C` failed - --> $DIR/interpret-in-static.rs:7:19 + --> $DIR/interpret-in-static.rs:8:19 | LL | const C: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-static.rs:7:19 + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-static.rs:8:19 | = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) note: erroneous constant encountered - --> $DIR/interpret-in-static.rs:15:9 + --> $DIR/interpret-in-static.rs:16:9 | LL | Fail::<i32>::C; | ^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/interpret-in-static.rs b/tests/ui/consts/required-consts/interpret-in-static.rs index 559e281b2b0..8bacd030440 100644 --- a/tests/ui/consts/required-consts/interpret-in-static.rs +++ b/tests/ui/consts/required-consts/interpret-in-static.rs @@ -1,4 +1,5 @@ //@revisions: noopt opt +//@[noopt] compile-flags: -Copt-level=0 //@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they are unused. diff --git a/tests/ui/coroutine/gen_block_is_fused_iter.rs b/tests/ui/coroutine/gen_block_is_fused_iter.rs new file mode 100644 index 00000000000..f3e19a7f54f --- /dev/null +++ b/tests/ui/coroutine/gen_block_is_fused_iter.rs @@ -0,0 +1,21 @@ +//@ revisions: next old +//@compile-flags: --edition 2024 -Zunstable-options +//@[next] compile-flags: -Znext-solver +//@ check-pass +#![feature(gen_blocks)] + +use std::iter::FusedIterator; + +fn foo() -> impl FusedIterator { + gen { yield 42 } +} + +fn bar() -> impl FusedIterator<Item = u16> { + gen { yield 42 } +} + +fn baz() -> impl FusedIterator + Iterator<Item = i64> { + gen { yield 42 } +} + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs new file mode 100644 index 00000000000..8d8917fd319 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs @@ -0,0 +1,45 @@ +#[diagnostic::on_unimplemented(message = "{{Test } thing")] +//~^WARN unmatched `}` found +//~|WARN unmatched `}` found +trait ImportantTrait1 {} + +#[diagnostic::on_unimplemented(message = "Test {}")] +//~^WARN positional format arguments are not allowed here +//~|WARN positional format arguments are not allowed here +trait ImportantTrait2 {} + +#[diagnostic::on_unimplemented(message = "Test {1:}")] +//~^WARN positional format arguments are not allowed here +//~|WARN positional format arguments are not allowed here +trait ImportantTrait3 {} + +#[diagnostic::on_unimplemented(message = "Test {Self:123}")] +//~^WARN invalid format specifier +//~|WARN invalid format specifier +trait ImportantTrait4 {} + +#[diagnostic::on_unimplemented(message = "Test {Self:!}")] +//~^WARN expected `'}'`, found `'!'` +//~|WARN expected `'}'`, found `'!'` +//~|WARN unmatched `}` found +//~|WARN unmatched `}` found +trait ImportantTrait5 {} + +fn check_1(_: impl ImportantTrait1) {} +fn check_2(_: impl ImportantTrait2) {} +fn check_3(_: impl ImportantTrait3) {} +fn check_4(_: impl ImportantTrait4) {} +fn check_5(_: impl ImportantTrait5) {} + +fn main() { + check_1(()); + //~^ERROR {{Test } thing + check_2(()); + //~^ERROR Test {} + check_3(()); + //~^ERROR Test {1} + check_4(()); + //~^ERROR Test () + check_5(()); + //~^ERROR Test {Self:!} +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr new file mode 100644 index 00000000000..932e81ca48e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr @@ -0,0 +1,193 @@ +warning: unmatched `}` found + --> $DIR/broken_format.rs:1:32 + | +LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default + +warning: positional format arguments are not allowed here + --> $DIR/broken_format.rs:6:32 + | +LL | #[diagnostic::on_unimplemented(message = "Test {}")] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: only named format arguments with the name of one of the generic types are allowed in this context + +warning: positional format arguments are not allowed here + --> $DIR/broken_format.rs:11:32 + | +LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: only named format arguments with the name of one of the generic types are allowed in this context + +warning: invalid format specifier + --> $DIR/broken_format.rs:16:32 + | +LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: no format specifier are supported in this position + +warning: expected `'}'`, found `'!'` + --> $DIR/broken_format.rs:21:32 + | +LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unmatched `}` found + --> $DIR/broken_format.rs:21:32 + | +LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unmatched `}` found + --> $DIR/broken_format.rs:1:32 + | +LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: {{Test } thing + --> $DIR/broken_format.rs:35:13 + | +LL | check_1(()); + | ------- ^^ the trait `ImportantTrait1` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/broken_format.rs:4:1 + | +LL | trait ImportantTrait1 {} + | ^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `check_1` + --> $DIR/broken_format.rs:28:20 + | +LL | fn check_1(_: impl ImportantTrait1) {} + | ^^^^^^^^^^^^^^^ required by this bound in `check_1` + +warning: positional format arguments are not allowed here + --> $DIR/broken_format.rs:6:32 + | +LL | #[diagnostic::on_unimplemented(message = "Test {}")] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: only named format arguments with the name of one of the generic types are allowed in this context + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: Test {} + --> $DIR/broken_format.rs:37:13 + | +LL | check_2(()); + | ------- ^^ the trait `ImportantTrait2` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/broken_format.rs:9:1 + | +LL | trait ImportantTrait2 {} + | ^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `check_2` + --> $DIR/broken_format.rs:29:20 + | +LL | fn check_2(_: impl ImportantTrait2) {} + | ^^^^^^^^^^^^^^^ required by this bound in `check_2` + +warning: positional format arguments are not allowed here + --> $DIR/broken_format.rs:11:32 + | +LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: only named format arguments with the name of one of the generic types are allowed in this context + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: Test {1} + --> $DIR/broken_format.rs:39:13 + | +LL | check_3(()); + | ------- ^^ the trait `ImportantTrait3` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/broken_format.rs:14:1 + | +LL | trait ImportantTrait3 {} + | ^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `check_3` + --> $DIR/broken_format.rs:30:20 + | +LL | fn check_3(_: impl ImportantTrait3) {} + | ^^^^^^^^^^^^^^^ required by this bound in `check_3` + +warning: invalid format specifier + --> $DIR/broken_format.rs:16:32 + | +LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: no format specifier are supported in this position + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: Test () + --> $DIR/broken_format.rs:41:13 + | +LL | check_4(()); + | ------- ^^ the trait `ImportantTrait4` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/broken_format.rs:19:1 + | +LL | trait ImportantTrait4 {} + | ^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `check_4` + --> $DIR/broken_format.rs:31:20 + | +LL | fn check_4(_: impl ImportantTrait4) {} + | ^^^^^^^^^^^^^^^ required by this bound in `check_4` + +warning: expected `'}'`, found `'!'` + --> $DIR/broken_format.rs:21:32 + | +LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: unmatched `}` found + --> $DIR/broken_format.rs:21:32 + | +LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: Test {Self:!} + --> $DIR/broken_format.rs:43:13 + | +LL | check_5(()); + | ------- ^^ the trait `ImportantTrait5` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/broken_format.rs:26:1 + | +LL | trait ImportantTrait5 {} + | ^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `check_5` + --> $DIR/broken_format.rs:32:20 + | +LL | fn check_5(_: impl ImportantTrait5) {} + | ^^^^^^^^^^^^^^^ required by this bound in `check_5` + +error: aborting due to 5 previous errors; 12 warnings emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/drop/norm-ice-106444.rs b/tests/ui/drop/norm-ice-106444.rs new file mode 100644 index 00000000000..b248bc73bbe --- /dev/null +++ b/tests/ui/drop/norm-ice-106444.rs @@ -0,0 +1,16 @@ +// issue: rust-lang/rust#106444 +// ICE failed to normalize +//@ compile-flags: -Zmir-opt-level=3 +//@ check-pass + +#![crate_type="lib"] + +pub trait A { + type B; +} + +pub struct S<T: A>(T::B); + +pub fn foo<T: A>(p: *mut S<T>) { + unsafe { core::ptr::drop_in_place(p) }; +} diff --git a/tests/ui/error-codes/E0637.rs b/tests/ui/error-codes/E0637.rs index e107ea9521b..382ce3ed01f 100644 --- a/tests/ui/error-codes/E0637.rs +++ b/tests/ui/error-codes/E0637.rs @@ -2,9 +2,9 @@ fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { //~^ ERROR: `'_` cannot be used here [E0637] //~| ERROR: missing lifetime specifier if str1.len() > str2.len() { - str1 //~ ERROR: lifetime may not live long enough + str1 } else { - str2 //~ ERROR: lifetime may not live long enough + str2 } } diff --git a/tests/ui/error-codes/E0637.stderr b/tests/ui/error-codes/E0637.stderr index 217881b8e7c..d9db89ddb0c 100644 --- a/tests/ui/error-codes/E0637.stderr +++ b/tests/ui/error-codes/E0637.stderr @@ -27,25 +27,7 @@ help: consider introducing a higher-ranked lifetime here LL | T: for<'a> Into<&'a u32>, | +++++++ ++ -error: lifetime may not live long enough - --> $DIR/E0637.rs:5:9 - | -LL | fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { - | - let's call the lifetime of this reference `'1` -... -LL | str1 - | ^^^^ returning this value requires that `'1` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/E0637.rs:7:9 - | -LL | fn underscore_lifetime<'_>(str1: &'_ str, str2: &'_ str) -> &'_ str { - | - let's call the lifetime of this reference `'2` -... -LL | str2 - | ^^^^ returning this value requires that `'2` must outlive `'static` - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0106, E0637. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/feature-gates/feature-gate-deref_patterns.rs b/tests/ui/feature-gates/feature-gate-deref_patterns.rs new file mode 100644 index 00000000000..b43001f2d53 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-deref_patterns.rs @@ -0,0 +1,9 @@ +fn main() { + // We reuse the `box` syntax so this doesn't actually test the feature gate but eh. + let box x = Box::new('c'); //~ ERROR box pattern syntax is experimental + println!("x: {}", x); + + // `box` syntax is allowed to be cfg-ed out for historical reasons (#65742). + #[cfg(FALSE)] + let box _x = Box::new('c'); +} diff --git a/tests/ui/feature-gates/feature-gate-deref_patterns.stderr b/tests/ui/feature-gates/feature-gate-deref_patterns.stderr new file mode 100644 index 00000000000..48426b50d89 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-deref_patterns.stderr @@ -0,0 +1,13 @@ +error[E0658]: box pattern syntax is experimental + --> $DIR/feature-gate-deref_patterns.rs:3:9 + | +LL | let box x = Box::new('c'); + | ^^^^^ + | + = note: see issue #29641 <https://github.com/rust-lang/rust/issues/29641> for more information + = help: add `#![feature(box_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-postfix_match.rs b/tests/ui/feature-gates/feature-gate-postfix_match.rs new file mode 100644 index 00000000000..dce7e81a9ae --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-postfix_match.rs @@ -0,0 +1,17 @@ +// Testing that postfix match doesn't work without enabling the feature + +fn main() { + let val = Some(42); + + val.match { //~ ERROR postfix match is experimental + Some(42) => "the answer to life, the universe, and everything", + _ => "might be the answer to something" + }; + + // Test that the gate works behind a cfg + #[cfg(FALSE)] + val.match { //~ ERROR postfix match is experimental + Some(42) => "the answer to life, the universe, and everything", + _ => "might be the answer to something" + }; +} diff --git a/tests/ui/feature-gates/feature-gate-postfix_match.stderr b/tests/ui/feature-gates/feature-gate-postfix_match.stderr new file mode 100644 index 00000000000..136838788dd --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-postfix_match.stderr @@ -0,0 +1,23 @@ +error[E0658]: postfix match is experimental + --> $DIR/feature-gate-postfix_match.rs:6:9 + | +LL | val.match { + | ^^^^^ + | + = note: see issue #121618 <https://github.com/rust-lang/rust/issues/121618> for more information + = help: add `#![feature(postfix_match)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: postfix match is experimental + --> $DIR/feature-gate-postfix_match.rs:13:9 + | +LL | val.match { + | ^^^^^ + | + = note: see issue #121618 <https://github.com/rust-lang/rust/issues/121618> for more information + = help: add `#![feature(postfix_match)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/generic-associated-types/issue-102335-gat.rs b/tests/ui/generic-associated-types/issue-102335-gat.rs index a7255fdcbf5..3a4a0c10771 100644 --- a/tests/ui/generic-associated-types/issue-102335-gat.rs +++ b/tests/ui/generic-associated-types/issue-102335-gat.rs @@ -1,6 +1,7 @@ trait T { type A: S<C<(), i32 = ()> = ()>; //~^ ERROR associated type bindings are not allowed here + //~| ERROR associated type bindings are not allowed here } trait Q {} diff --git a/tests/ui/generic-associated-types/issue-102335-gat.stderr b/tests/ui/generic-associated-types/issue-102335-gat.stderr index 39ca7954ede..f5e782e92fc 100644 --- a/tests/ui/generic-associated-types/issue-102335-gat.stderr +++ b/tests/ui/generic-associated-types/issue-102335-gat.stderr @@ -4,6 +4,14 @@ error[E0229]: associated type bindings are not allowed here LL | type A: S<C<(), i32 = ()> = ()>; | ^^^^^^^^ associated type not allowed here -error: aborting due to 1 previous error +error[E0229]: associated type bindings are not allowed here + --> $DIR/issue-102335-gat.rs:2:21 + | +LL | type A: S<C<(), i32 = ()> = ()>; + | ^^^^^^^^ associated type not allowed here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0229`. diff --git a/tests/ui/generic-associated-types/issue-80433.rs b/tests/ui/generic-associated-types/issue-80433.rs index 6d23427f16f..53057542440 100644 --- a/tests/ui/generic-associated-types/issue-80433.rs +++ b/tests/ui/generic-associated-types/issue-80433.rs @@ -29,5 +29,5 @@ fn test_simpler<'a>(dst: &'a mut impl TestMut<Output = &'a mut f32>) fn main() { let mut t1: E<f32> = Default::default(); - test_simpler(&mut t1); //~ ERROR does not live long enough + test_simpler(&mut t1); } diff --git a/tests/ui/generic-associated-types/issue-80433.stderr b/tests/ui/generic-associated-types/issue-80433.stderr index 1ca080f5df2..a9a14d3f51c 100644 --- a/tests/ui/generic-associated-types/issue-80433.stderr +++ b/tests/ui/generic-associated-types/issue-80433.stderr @@ -48,20 +48,7 @@ LL | *dst.test_mut() = n.into(); | `dst` escapes the function body here | argument requires that `'a` must outlive `'static` -error[E0597]: `t1` does not live long enough - --> $DIR/issue-80433.rs:32:18 - | -LL | let mut t1: E<f32> = Default::default(); - | ------ binding `t1` declared here -LL | test_simpler(&mut t1); - | -------------^^^^^^^- - | | | - | | borrowed value does not live long enough - | argument requires that `t1` is borrowed for `'static` -LL | } - | - `t1` dropped here while still borrowed - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0107, E0499, E0521, E0597. +Some errors have detailed explanations: E0107, E0499, E0521. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/issue-81712-cyclic-traits.rs b/tests/ui/generic-associated-types/issue-81712-cyclic-traits.rs index a7cc9a6053e..412eeb6e29a 100644 --- a/tests/ui/generic-associated-types/issue-81712-cyclic-traits.rs +++ b/tests/ui/generic-associated-types/issue-81712-cyclic-traits.rs @@ -13,6 +13,7 @@ trait C { trait D<T> { type CType: C<DType = Self>; //~^ ERROR missing generics for associated type + //~| ERROR missing generics for associated type } fn main() {} diff --git a/tests/ui/generic-associated-types/issue-81712-cyclic-traits.stderr b/tests/ui/generic-associated-types/issue-81712-cyclic-traits.stderr index 5eb988ea042..33c40f88f87 100644 --- a/tests/ui/generic-associated-types/issue-81712-cyclic-traits.stderr +++ b/tests/ui/generic-associated-types/issue-81712-cyclic-traits.stderr @@ -14,6 +14,23 @@ help: add missing generic argument LL | type CType: C<DType<T> = Self>; | +++ -error: aborting due to 1 previous error +error[E0107]: missing generics for associated type `C::DType` + --> $DIR/issue-81712-cyclic-traits.rs:14:19 + | +LL | type CType: C<DType = Self>; + | ^^^^^ expected 1 generic argument + | +note: associated type defined here, with 1 generic parameter: `T` + --> $DIR/issue-81712-cyclic-traits.rs:11:10 + | +LL | type DType<T>: D<T, CType = Self>; + | ^^^^^ - + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing generic argument + | +LL | type CType: C<DType<T> = Self>; + | +++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/generics/generic-type-less-params-with-defaults.rs b/tests/ui/generics/generic-type-less-params-with-defaults.rs index 6b877ab8aee..d04b1c80d34 100644 --- a/tests/ui/generics/generic-type-less-params-with-defaults.rs +++ b/tests/ui/generics/generic-type-less-params-with-defaults.rs @@ -5,7 +5,23 @@ struct Heap; struct Vec<T, A = Heap>( marker::PhantomData<(T,A)>); +struct HashMap<K, V, S = ()>(marker::PhantomData<(K,V,S)>); + fn main() { let _: Vec; //~^ ERROR missing generics for struct `Vec` + //~| SUGGESTION <T> + + let _x = (1..10).collect::<HashMap>(); + //~^ ERROR missing generics for struct `HashMap` + //~| SUGGESTION <_, _> + + ().extend::<[(); 0]>({ + fn not_the_extend() { + let _: Vec; + //~^ ERROR missing generics for struct `Vec` + //~| SUGGESTION <T> + } + [] + }); } diff --git a/tests/ui/generics/generic-type-less-params-with-defaults.stderr b/tests/ui/generics/generic-type-less-params-with-defaults.stderr index 6f79b09f6cd..a6771a6d5c8 100644 --- a/tests/ui/generics/generic-type-less-params-with-defaults.stderr +++ b/tests/ui/generics/generic-type-less-params-with-defaults.stderr @@ -1,5 +1,5 @@ error[E0107]: missing generics for struct `Vec` - --> $DIR/generic-type-less-params-with-defaults.rs:9:12 + --> $DIR/generic-type-less-params-with-defaults.rs:11:12 | LL | let _: Vec; | ^^^ expected at least 1 generic argument @@ -14,6 +14,38 @@ help: add missing generic argument LL | let _: Vec<T>; | +++ -error: aborting due to 1 previous error +error[E0107]: missing generics for struct `HashMap` + --> $DIR/generic-type-less-params-with-defaults.rs:15:32 + | +LL | let _x = (1..10).collect::<HashMap>(); + | ^^^^^^^ expected at least 2 generic arguments + | +note: struct defined here, with at least 2 generic parameters: `K`, `V` + --> $DIR/generic-type-less-params-with-defaults.rs:8:8 + | +LL | struct HashMap<K, V, S = ()>(marker::PhantomData<(K,V,S)>); + | ^^^^^^^ - - +help: add missing generic arguments + | +LL | let _x = (1..10).collect::<HashMap<_, _>>(); + | ++++++ + +error[E0107]: missing generics for struct `Vec` + --> $DIR/generic-type-less-params-with-defaults.rs:21:20 + | +LL | let _: Vec; + | ^^^ expected at least 1 generic argument + | +note: struct defined here, with at least 1 generic parameter: `T` + --> $DIR/generic-type-less-params-with-defaults.rs:5:8 + | +LL | struct Vec<T, A = Heap>( + | ^^^ - +help: add missing generic argument + | +LL | let _: Vec<T>; + | +++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/higher-ranked/structually-relate-aliases.rs b/tests/ui/higher-ranked/structually-relate-aliases.rs new file mode 100644 index 00000000000..8df24702811 --- /dev/null +++ b/tests/ui/higher-ranked/structually-relate-aliases.rs @@ -0,0 +1,17 @@ +// regression test for issue #121649. + +trait ToUnit<'a> { + type Unit; +} + +trait Overlap<T> {} + +type Assoc<'a, T> = <T as ToUnit<'a>>::Unit; + +impl<T> Overlap<T> for T {} + +impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} +//~^ ERROR 13:17: 13:49: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied [E0277] +//~| ERROR 13:36: 13:48: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied [E0277] + +fn main() {} diff --git a/tests/ui/higher-ranked/structually-relate-aliases.stderr b/tests/ui/higher-ranked/structually-relate-aliases.stderr new file mode 100644 index 00000000000..59fab52b221 --- /dev/null +++ b/tests/ui/higher-ranked/structually-relate-aliases.stderr @@ -0,0 +1,27 @@ +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } +error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied + --> $DIR/structually-relate-aliases.rs:13:36 + | +LL | impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} + | ^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | impl<T: for<'a> ToUnit<'a>> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} + | ++++++++++++++++++++ + +error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied + --> $DIR/structually-relate-aliases.rs:13:17 + | +LL | impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | impl<T: for<'a> ToUnit<'a>> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} + | ++++++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-not-adjacent-to-type.rs b/tests/ui/impl-not-adjacent-to-type.rs index 7fc927b1d64..ccf59ed4393 100644 --- a/tests/ui/impl-not-adjacent-to-type.rs +++ b/tests/ui/impl-not-adjacent-to-type.rs @@ -3,6 +3,7 @@ mod foo { pub struct Point { pub x: i32, + #[allow(dead_code)] pub y: i32, } } diff --git a/tests/ui/impl-trait/fresh-lifetime-from-bare-trait-obj-114664.rs b/tests/ui/impl-trait/fresh-lifetime-from-bare-trait-obj-114664.rs index 41c5b9f5074..e75cfc88ef4 100644 --- a/tests/ui/impl-trait/fresh-lifetime-from-bare-trait-obj-114664.rs +++ b/tests/ui/impl-trait/fresh-lifetime-from-bare-trait-obj-114664.rs @@ -5,6 +5,8 @@ fn ice() -> impl AsRef<Fn(&())> { //~^ WARN trait objects without an explicit `dyn` are deprecated //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| WARN trait objects without an explicit `dyn` are deprecated + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! Foo } diff --git a/tests/ui/impl-trait/fresh-lifetime-from-bare-trait-obj-114664.stderr b/tests/ui/impl-trait/fresh-lifetime-from-bare-trait-obj-114664.stderr index 3cb3af89bfc..d82b2c0f606 100644 --- a/tests/ui/impl-trait/fresh-lifetime-from-bare-trait-obj-114664.stderr +++ b/tests/ui/impl-trait/fresh-lifetime-from-bare-trait-obj-114664.stderr @@ -12,5 +12,19 @@ help: if this is an object-safe trait, use `dyn` LL | fn ice() -> impl AsRef<dyn Fn(&())> { | +++ -warning: 1 warning emitted +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/fresh-lifetime-from-bare-trait-obj-114664.rs:5:24 + | +LL | fn ice() -> impl AsRef<Fn(&())> { + | ^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: if this is an object-safe trait, use `dyn` + | +LL | fn ice() -> impl AsRef<dyn Fn(&())> { + | +++ + +warning: 2 warnings emitted diff --git a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs new file mode 100644 index 00000000000..d6fa56663a3 --- /dev/null +++ b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.rs @@ -0,0 +1,30 @@ +// test for ICE #112823 +// Unexpected parameter Type(Repr) when substituting in region + +#![feature(impl_trait_in_assoc_type)] + +use std::future::Future; + +trait Stream {} + +trait X { + type LineStream<'a, Repr> + where + Self: 'a; + type LineStreamFut<'a, Repr> + where + Self: 'a; +} + +struct Y; + +impl X for Y { + type LineStream<'c, 'd> = impl Stream; + //~^ ERROR type `LineStream` has 0 type parameters but its trait declaration has 1 type parameter + type LineStreamFut<'a, Repr> = impl Future<Output = Self::LineStream<'a, Repr>>; + fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} + //~^ ERROR `()` is not a future + //~^^ method `line_stream` is not a member of trait `X` +} + +pub fn main() {} diff --git a/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.stderr b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.stderr new file mode 100644 index 00000000000..28a0f7461e2 --- /dev/null +++ b/tests/ui/impl-trait/ice-unexpected-param-type-whensubstituting-in-region-112823.stderr @@ -0,0 +1,31 @@ +error[E0407]: method `line_stream` is not a member of trait `X` + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:25:5 + | +LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `X` + +error[E0049]: type `LineStream` has 0 type parameters but its trait declaration has 1 type parameter + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:22:21 + | +LL | type LineStream<'a, Repr> + | -- ---- + | | + | expected 1 type parameter +... +LL | type LineStream<'c, 'd> = impl Stream; + | ^^ ^^ + | | + | found 0 type parameters + +error[E0277]: `()` is not a future + --> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:25:43 + | +LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not a future + | + = help: the trait `Future` is not implemented for `()` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0049, E0277, E0407. +For more information about an error, try `rustc --explain E0049`. diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs b/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs index a9ea657f10e..da7530b4e7a 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.rs @@ -4,19 +4,16 @@ use std::fmt::Debug; fn a() -> impl Fn(&u8) -> (impl Debug + '_) { //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` |x| x - //~^ ERROR lifetime may not live long enough } fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) { //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` |x| x - //~^ ERROR lifetime may not live long enough } fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` |x| x - //~^ ERROR lifetime may not live long enough } fn d() -> impl Fn() -> (impl Debug + '_) { diff --git a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr index bdb099619b7..7d108b30b76 100644 --- a/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr +++ b/tests/ui/impl-trait/impl-fn-hrtb-bounds.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifier - --> $DIR/impl-fn-hrtb-bounds.rs:22:38 + --> $DIR/impl-fn-hrtb-bounds.rs:19:38 | LL | fn d() -> impl Fn() -> (impl Debug + '_) { | ^^ expected named lifetime parameter @@ -22,58 +22,31 @@ note: lifetime declared here LL | fn a() -> impl Fn(&u8) -> (impl Debug + '_) { | ^ -error: lifetime may not live long enough - --> $DIR/impl-fn-hrtb-bounds.rs:6:9 - | -LL | |x| x - | -- ^ returning this value requires that `'1` must outlive `'2` - | || - | |return type of closure is impl Debug + '2 - | has type `&'1 u8` - error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` - --> $DIR/impl-fn-hrtb-bounds.rs:10:52 + --> $DIR/impl-fn-hrtb-bounds.rs:9:52 | LL | fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) { | ^^ | note: lifetime declared here - --> $DIR/impl-fn-hrtb-bounds.rs:10:20 + --> $DIR/impl-fn-hrtb-bounds.rs:9:20 | LL | fn b() -> impl for<'a> Fn(&'a u8) -> (impl Debug + 'a) { | ^^ -error: lifetime may not live long enough - --> $DIR/impl-fn-hrtb-bounds.rs:12:9 - | -LL | |x| x - | -- ^ returning this value requires that `'1` must outlive `'2` - | || - | |return type of closure is impl Debug + '2 - | has type `&'1 u8` - error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` - --> $DIR/impl-fn-hrtb-bounds.rs:16:52 + --> $DIR/impl-fn-hrtb-bounds.rs:14:52 | LL | fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { | ^^ | note: lifetime declared here - --> $DIR/impl-fn-hrtb-bounds.rs:16:20 + --> $DIR/impl-fn-hrtb-bounds.rs:14:20 | LL | fn c() -> impl for<'a> Fn(&'a u8) -> (impl Debug + '_) { | ^^ -error: lifetime may not live long enough - --> $DIR/impl-fn-hrtb-bounds.rs:18:9 - | -LL | |x| x - | -- ^ returning this value requires that `'1` must outlive `'2` - | || - | |return type of closure is impl Debug + '2 - | has type `&'1 u8` - -error: aborting due to 7 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0106, E0657. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/impl-trait/impl-fn-parsing-ambiguities.rs b/tests/ui/impl-trait/impl-fn-parsing-ambiguities.rs index ef9d8733509..7679b7ec478 100644 --- a/tests/ui/impl-trait/impl-fn-parsing-ambiguities.rs +++ b/tests/ui/impl-trait/impl-fn-parsing-ambiguities.rs @@ -5,7 +5,6 @@ fn a() -> impl Fn(&u8) -> impl Debug + '_ { //~^ ERROR ambiguous `+` in a type //~| ERROR cannot capture higher-ranked lifetime from outer `impl Trait` |x| x - //~^ ERROR lifetime may not live long enough } fn b() -> impl Fn() -> impl Debug + Send { diff --git a/tests/ui/impl-trait/impl-fn-parsing-ambiguities.stderr b/tests/ui/impl-trait/impl-fn-parsing-ambiguities.stderr index 3881b37a0cb..e0955faac7c 100644 --- a/tests/ui/impl-trait/impl-fn-parsing-ambiguities.stderr +++ b/tests/ui/impl-trait/impl-fn-parsing-ambiguities.stderr @@ -5,7 +5,7 @@ LL | fn a() -> impl Fn(&u8) -> impl Debug + '_ { | ^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(impl Debug + '_)` error: ambiguous `+` in a type - --> $DIR/impl-fn-parsing-ambiguities.rs:11:24 + --> $DIR/impl-fn-parsing-ambiguities.rs:10:24 | LL | fn b() -> impl Fn() -> impl Debug + Send { | ^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(impl Debug + Send)` @@ -22,15 +22,6 @@ note: lifetime declared here LL | fn a() -> impl Fn(&u8) -> impl Debug + '_ { | ^ -error: lifetime may not live long enough - --> $DIR/impl-fn-parsing-ambiguities.rs:7:9 - | -LL | |x| x - | -- ^ returning this value requires that `'1` must outlive `'2` - | || - | |return type of closure is impl Debug + '2 - | has type `&'1 u8` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0657`. diff --git a/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.rs b/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.rs index 6f0dbd752b0..21e2fda1c3a 100644 --- a/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.rs +++ b/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.rs @@ -1,5 +1,4 @@ -struct Wrapper<'rom>(T); -//~^ ERROR cannot find type `T` in this scope +struct Wrapper<'rom>(&'rom ()); trait Foo { fn bar() -> Wrapper<impl Sized>; @@ -15,4 +14,18 @@ impl Foo for () { } } +trait Bar { + fn foo() -> Wrapper<impl Sized>; + //~^ ERROR missing lifetime specifier + //~| ERROR struct takes 0 generic arguments but 1 generic argument was supplied +} + +impl Bar for () { + fn foo() -> Wrapper<impl Sized> { + //~^ ERROR missing lifetime specifier + //~| ERROR struct takes 0 generic arguments but 1 generic argument was supplied + Wrapper(&()) + } +} + fn main() {} diff --git a/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr b/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr index d30557c8a7b..d7fc40fa342 100644 --- a/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr +++ b/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifier - --> $DIR/opaque-and-lifetime-mismatch.rs:5:24 + --> $DIR/opaque-and-lifetime-mismatch.rs:4:24 | LL | fn bar() -> Wrapper<impl Sized>; | ^ expected named lifetime parameter @@ -10,19 +10,32 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're LL | fn bar() -> Wrapper<'static, impl Sized>; | ++++++++ -error[E0412]: cannot find type `T` in this scope - --> $DIR/opaque-and-lifetime-mismatch.rs:1:22 +error[E0106]: missing lifetime specifier + --> $DIR/opaque-and-lifetime-mismatch.rs:18:24 + | +LL | fn foo() -> Wrapper<impl Sized>; + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values + | +LL | fn foo() -> Wrapper<'static, impl Sized>; + | ++++++++ + +error[E0106]: missing lifetime specifier + --> $DIR/opaque-and-lifetime-mismatch.rs:24:24 | -LL | struct Wrapper<'rom>(T); - | ^ not found in this scope +LL | fn foo() -> Wrapper<impl Sized> { + | ^ expected named lifetime parameter | -help: you might be missing a type parameter + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | struct Wrapper<'rom, T>(T); - | +++ +LL | fn foo() -> Wrapper<'static, impl Sized> { + | ++++++++ error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/opaque-and-lifetime-mismatch.rs:5:17 + --> $DIR/opaque-and-lifetime-mismatch.rs:4:17 | LL | fn bar() -> Wrapper<impl Sized>; | ^^^^^^^ ---------- help: remove this generic argument @@ -32,11 +45,25 @@ LL | fn bar() -> Wrapper<impl Sized>; note: struct defined here, with 0 generic parameters --> $DIR/opaque-and-lifetime-mismatch.rs:1:8 | -LL | struct Wrapper<'rom>(T); +LL | struct Wrapper<'rom>(&'rom ()); + | ^^^^^^^ + +error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/opaque-and-lifetime-mismatch.rs:18:17 + | +LL | fn foo() -> Wrapper<impl Sized>; + | ^^^^^^^ ---------- help: remove this generic argument + | | + | expected 0 generic arguments + | +note: struct defined here, with 0 generic parameters + --> $DIR/opaque-and-lifetime-mismatch.rs:1:8 + | +LL | struct Wrapper<'rom>(&'rom ()); | ^^^^^^^ error[E0053]: method `bar` has an incompatible return type for trait - --> $DIR/opaque-and-lifetime-mismatch.rs:11:17 + --> $DIR/opaque-and-lifetime-mismatch.rs:10:17 | LL | fn bar() -> i32 { | ^^^ @@ -45,7 +72,7 @@ LL | fn bar() -> i32 { | return type in trait error[E0053]: method `bar` has an incompatible type for trait - --> $DIR/opaque-and-lifetime-mismatch.rs:11:17 + --> $DIR/opaque-and-lifetime-mismatch.rs:10:17 | LL | fn bar() -> i32 { | ^^^ @@ -54,14 +81,28 @@ LL | fn bar() -> i32 { | help: change the output type to match the trait: `Wrapper<'static>` | note: type in trait - --> $DIR/opaque-and-lifetime-mismatch.rs:5:17 + --> $DIR/opaque-and-lifetime-mismatch.rs:4:17 | LL | fn bar() -> Wrapper<impl Sized>; | ^^^^^^^^^^^^^^^^^^^ = note: expected signature `fn() -> Wrapper<'static>` found signature `fn() -> i32` -error: aborting due to 5 previous errors +error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/opaque-and-lifetime-mismatch.rs:24:17 + | +LL | fn foo() -> Wrapper<impl Sized> { + | ^^^^^^^ ---------- help: remove this generic argument + | | + | expected 0 generic arguments + | +note: struct defined here, with 0 generic parameters + --> $DIR/opaque-and-lifetime-mismatch.rs:1:8 + | +LL | struct Wrapper<'rom>(&'rom ()); + | ^^^^^^^ + +error: aborting due to 8 previous errors -Some errors have detailed explanations: E0053, E0106, E0107, E0412. +Some errors have detailed explanations: E0053, E0106, E0107. For more information about an error, try `rustc --explain E0053`. diff --git a/tests/ui/impl-trait/in-trait/span-bug-issue-121457.rs b/tests/ui/impl-trait/in-trait/span-bug-issue-121457.rs index 10167ee9352..ab21dae7dc5 100644 --- a/tests/ui/impl-trait/in-trait/span-bug-issue-121457.rs +++ b/tests/ui/impl-trait/in-trait/span-bug-issue-121457.rs @@ -12,6 +12,7 @@ impl<'a, I: 'a + Iterable> Iterable for &'a I { fn iter(&self) -> impl for<'missing> Iterator<Item = Self::Item<'missing>> {} //~^ ERROR binding for associated type `Item` references lifetime `'missing` + //~| ERROR binding for associated type `Item` references lifetime `'missing` //~| ERROR `()` is not an iterator } diff --git a/tests/ui/impl-trait/in-trait/span-bug-issue-121457.stderr b/tests/ui/impl-trait/in-trait/span-bug-issue-121457.stderr index 96c3644f893..d8a2eef94a1 100644 --- a/tests/ui/impl-trait/in-trait/span-bug-issue-121457.stderr +++ b/tests/ui/impl-trait/in-trait/span-bug-issue-121457.stderr @@ -4,6 +4,14 @@ error[E0582]: binding for associated type `Item` references lifetime `'missing`, LL | fn iter(&self) -> impl for<'missing> Iterator<Item = Self::Item<'missing>> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error[E0582]: binding for associated type `Item` references lifetime `'missing`, which does not appear in the trait input types + --> $DIR/span-bug-issue-121457.rs:13:51 + | +LL | fn iter(&self) -> impl for<'missing> Iterator<Item = Self::Item<'missing>> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0195]: lifetime parameters or bounds on type `Item` do not match the trait declaration --> $DIR/span-bug-issue-121457.rs:10:14 | @@ -24,7 +32,7 @@ LL | fn iter(&self) -> impl for<'missing> Iterator<Item = Self::Item<'missin | = help: the trait `Iterator` is not implemented for `()` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0195, E0277, E0582. For more information about an error, try `rustc --explain E0195`. diff --git a/tests/ui/impl-trait/issues/issue-92305.rs b/tests/ui/impl-trait/issues/issue-92305.rs index 5ecb6984cfe..be98ce807ec 100644 --- a/tests/ui/impl-trait/issues/issue-92305.rs +++ b/tests/ui/impl-trait/issues/issue-92305.rs @@ -4,6 +4,7 @@ use std::iter; fn f<T>(data: &[T]) -> impl Iterator<Item = Vec> { //~^ ERROR: missing generics for struct `Vec` [E0107] + //~| ERROR: missing generics for struct `Vec` [E0107] iter::empty() } diff --git a/tests/ui/impl-trait/issues/issue-92305.stderr b/tests/ui/impl-trait/issues/issue-92305.stderr index 88fb1fb2707..4591d2c53f7 100644 --- a/tests/ui/impl-trait/issues/issue-92305.stderr +++ b/tests/ui/impl-trait/issues/issue-92305.stderr @@ -9,6 +9,18 @@ help: add missing generic argument LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec<T>> { | +++ -error: aborting due to 1 previous error +error[E0107]: missing generics for struct `Vec` + --> $DIR/issue-92305.rs:5:45 + | +LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec> { + | ^^^ expected at least 1 generic argument + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing generic argument + | +LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec<T>> { + | +++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.rs b/tests/ui/impl-trait/nested-rpit-hrtb.rs index c10bfbfe4dc..a696e1710f0 100644 --- a/tests/ui/impl-trait/nested-rpit-hrtb.rs +++ b/tests/ui/impl-trait/nested-rpit-hrtb.rs @@ -35,7 +35,6 @@ fn one_hrtb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'a> {} fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from outer `impl Trait` -//~| ERROR: the trait bound `for<'a> &'a (): Qux<'_>` is not satisfied // This should resolve. fn one_hrtb_mention_fn_trait_param<'b>() -> impl for<'a> Foo<'a, Assoc = impl Qux<'b>> {} @@ -45,7 +44,7 @@ fn one_hrtb_mention_fn_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl Sized // This should resolve. fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Qux<'b>> {} -//~^ ERROR: the trait bound `for<'a> &'a (): Qux<'b>` is not satisfied +//~^ ERROR type annotations needed: cannot satisfy `for<'a> &'a (): Qux<'b>` // This should resolve. fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {} diff --git a/tests/ui/impl-trait/nested-rpit-hrtb.stderr b/tests/ui/impl-trait/nested-rpit-hrtb.stderr index 2779694a517..64f801ea685 100644 --- a/tests/ui/impl-trait/nested-rpit-hrtb.stderr +++ b/tests/ui/impl-trait/nested-rpit-hrtb.stderr @@ -1,5 +1,5 @@ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/nested-rpit-hrtb.rs:58:77 + --> $DIR/nested-rpit-hrtb.rs:57:77 | LL | fn two_htrb_outlives() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Sized + 'b> {} | ^^ undeclared lifetime @@ -15,7 +15,7 @@ LL | fn two_htrb_outlives<'b>() -> impl for<'a> Foo<'a, Assoc = impl for<'b> Siz | ++++ error[E0261]: use of undeclared lifetime name `'b` - --> $DIR/nested-rpit-hrtb.rs:66:82 + --> $DIR/nested-rpit-hrtb.rs:65:82 | LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {} | ^^ undeclared lifetime @@ -86,26 +86,18 @@ note: lifetime declared here LL | fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} | ^^ -error[E0277]: the trait bound `for<'a> &'a (): Qux<'_>` is not satisfied - --> $DIR/nested-rpit-hrtb.rs:36:64 - | -LL | fn one_hrtb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl Qux<'a>> {} - | ^^^^^^^^^^^^ the trait `for<'a> Qux<'_>` is not implemented for `&'a ()` - | - = help: the trait `Qux<'_>` is implemented for `()` - = help: for that trait implementation, expected `()`, found `&'a ()` - -error[E0277]: the trait bound `for<'a> &'a (): Qux<'b>` is not satisfied - --> $DIR/nested-rpit-hrtb.rs:47:79 +error[E0283]: type annotations needed: cannot satisfy `for<'a> &'a (): Qux<'b>` + --> $DIR/nested-rpit-hrtb.rs:46:79 | LL | fn one_hrtb_mention_fn_trait_param_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Qux<'b>> {} - | ^^^^^^^^^^^^ the trait `for<'a> Qux<'b>` is not implemented for `&'a ()` + | ^^^^^^^^^^^^ | + = note: cannot satisfy `for<'a> &'a (): Qux<'b>` = help: the trait `Qux<'_>` is implemented for `()` = help: for that trait implementation, expected `()`, found `&'a ()` error: implementation of `Bar` is not general enough - --> $DIR/nested-rpit-hrtb.rs:51:93 + --> $DIR/nested-rpit-hrtb.rs:50:93 | LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = impl Sized + 'b> {} | ^^ implementation of `Bar` is not general enough @@ -114,7 +106,7 @@ LL | fn one_hrtb_mention_fn_outlives_uses<'b>() -> impl for<'a> Bar<'a, Assoc = = note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0` error[E0277]: the trait bound `for<'a, 'b> &'a (): Qux<'b>` is not satisfied - --> $DIR/nested-rpit-hrtb.rs:62:64 + --> $DIR/nested-rpit-hrtb.rs:61:64 | LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Qux<'b>> {} | ^^^^^^^^^^^^^^^^^^^^ the trait `for<'a, 'b> Qux<'b>` is not implemented for `&'a ()` @@ -123,7 +115,7 @@ LL | fn two_htrb_trait_param_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> = help: for that trait implementation, expected `()`, found `&'a ()` error: implementation of `Bar` is not general enough - --> $DIR/nested-rpit-hrtb.rs:66:86 + --> $DIR/nested-rpit-hrtb.rs:65:86 | LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Sized + 'b> {} | ^^ implementation of `Bar` is not general enough @@ -131,7 +123,7 @@ LL | fn two_htrb_outlives_uses() -> impl for<'a> Bar<'a, Assoc = impl for<'b> Si = note: `()` must implement `Bar<'a>` = note: ...but it actually implements `Bar<'0>`, for some specific lifetime `'0` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors -Some errors have detailed explanations: E0261, E0277, E0657. +Some errors have detailed explanations: E0261, E0277, E0283, E0657. For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/impl-trait/opaque-used-in-extraneous-argument.rs b/tests/ui/impl-trait/opaque-used-in-extraneous-argument.rs index 529913479ef..8d4855c101c 100644 --- a/tests/ui/impl-trait/opaque-used-in-extraneous-argument.rs +++ b/tests/ui/impl-trait/opaque-used-in-extraneous-argument.rs @@ -7,6 +7,7 @@ fn frob() -> impl Fn<P, Output = T> + '_ {} //~| ERROR cannot find type `P` //~| ERROR cannot find type `T` //~| ERROR `Fn`-family traits' type parameters is subject to change +//~| ERROR `Fn`-family traits' type parameters is subject to change fn open_parent<'path>() { todo!() diff --git a/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr b/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr index b54b9f908b2..6d417488533 100644 --- a/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr +++ b/tests/ui/impl-trait/opaque-used-in-extraneous-argument.stderr @@ -42,8 +42,19 @@ LL | fn frob() -> impl Fn<P, Output = T> + '_ {} = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change + --> $DIR/opaque-used-in-extraneous-argument.rs:5:19 + | +LL | fn frob() -> impl Fn<P, Output = T> + '_ {} + | ^^^^^^^^^^^^^^^^^ help: use parenthetical notation instead: `Fn(P) -> T` + | + = note: see issue #29625 <https://github.com/rust-lang/rust/issues/29625> for more information + = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0061]: this function takes 0 arguments but 1 argument was supplied - --> $DIR/opaque-used-in-extraneous-argument.rs:16:20 + --> $DIR/opaque-used-in-extraneous-argument.rs:17:20 | LL | let old_path = frob("hello"); | ^^^^ ------- @@ -58,7 +69,7 @@ LL | fn frob() -> impl Fn<P, Output = T> + '_ {} | ^^^^ error[E0061]: this function takes 0 arguments but 1 argument was supplied - --> $DIR/opaque-used-in-extraneous-argument.rs:19:5 + --> $DIR/opaque-used-in-extraneous-argument.rs:20:5 | LL | open_parent(&old_path) | ^^^^^^^^^^^ --------- @@ -67,12 +78,12 @@ LL | open_parent(&old_path) | help: remove the extra argument | note: function defined here - --> $DIR/opaque-used-in-extraneous-argument.rs:11:4 + --> $DIR/opaque-used-in-extraneous-argument.rs:12:4 | LL | fn open_parent<'path>() { | ^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0061, E0106, E0412, E0658. For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/impl-trait/recursive-ice-101862.rs b/tests/ui/impl-trait/recursive-ice-101862.rs new file mode 100644 index 00000000000..02f95fe5604 --- /dev/null +++ b/tests/ui/impl-trait/recursive-ice-101862.rs @@ -0,0 +1,12 @@ +// issue: rust-lang/rust#101852 +// ICE opaque type with non-universal region substs + +pub fn ice(x: impl AsRef<str>) -> impl IntoIterator<Item = ()> { +//~^ WARN function cannot return without recursing + vec![].append(&mut ice(x.as_ref())); + //~^ ERROR expected generic type parameter, found `&str` + + Vec::new() +} + +fn main() {} diff --git a/tests/ui/impl-trait/recursive-ice-101862.stderr b/tests/ui/impl-trait/recursive-ice-101862.stderr new file mode 100644 index 00000000000..f4148720c33 --- /dev/null +++ b/tests/ui/impl-trait/recursive-ice-101862.stderr @@ -0,0 +1,24 @@ +warning: function cannot return without recursing + --> $DIR/recursive-ice-101862.rs:4:1 + | +LL | pub fn ice(x: impl AsRef<str>) -> impl IntoIterator<Item = ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | vec![].append(&mut ice(x.as_ref())); + | --------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error[E0792]: expected generic type parameter, found `&str` + --> $DIR/recursive-ice-101862.rs:6:5 + | +LL | pub fn ice(x: impl AsRef<str>) -> impl IntoIterator<Item = ()> { + | --------------- this generic parameter must be used with a generic type parameter +LL | +LL | vec![].append(&mut ice(x.as_ref())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/impl-trait/stranded-opaque.rs b/tests/ui/impl-trait/stranded-opaque.rs index c7ab390e1fd..6ce5cbd3b55 100644 --- a/tests/ui/impl-trait/stranded-opaque.rs +++ b/tests/ui/impl-trait/stranded-opaque.rs @@ -7,6 +7,7 @@ impl Trait for i32 {} // ICE in this case. fn produce<T>() -> impl Trait<Assoc = impl Trait> { //~^ ERROR associated type `Assoc` not found for `Trait` + //~| ERROR associated type `Assoc` not found for `Trait` 16 } diff --git a/tests/ui/impl-trait/stranded-opaque.stderr b/tests/ui/impl-trait/stranded-opaque.stderr index 75f5480bc8b..5bea3e2af6b 100644 --- a/tests/ui/impl-trait/stranded-opaque.stderr +++ b/tests/ui/impl-trait/stranded-opaque.stderr @@ -4,6 +4,14 @@ error[E0220]: associated type `Assoc` not found for `Trait` LL | fn produce<T>() -> impl Trait<Assoc = impl Trait> { | ^^^^^ associated type `Assoc` not found -error: aborting due to 1 previous error +error[E0220]: associated type `Assoc` not found for `Trait` + --> $DIR/stranded-opaque.rs:8:31 + | +LL | fn produce<T>() -> impl Trait<Assoc = impl Trait> { + | ^^^^^ associated type `Assoc` not found + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/inference/ice-cannot-relate-region-109178.rs b/tests/ui/inference/ice-cannot-relate-region-109178.rs new file mode 100644 index 00000000000..3282f95a992 --- /dev/null +++ b/tests/ui/inference/ice-cannot-relate-region-109178.rs @@ -0,0 +1,14 @@ +// test for ice #109178 cannot relate region: LUB(ReErased, ReError) + +#![allow(incomplete_features)] +#![crate_type = "lib"] +#![feature(adt_const_params, generic_const_exprs)] + +struct Changes<const CHANGES: &[&'static str]> +//~^ ERROR `&` without an explicit lifetime name cannot be used here +where + [(); CHANGES.len()]:, {} + +impl<const CHANGES: &[&str]> Changes<CHANGES> where [(); CHANGES.len()]: {} +//~^ ERROR `&` without an explicit lifetime name cannot be used here +//~^^ ERROR `&` without an explicit lifetime name cannot be used here diff --git a/tests/ui/inference/ice-cannot-relate-region-109178.stderr b/tests/ui/inference/ice-cannot-relate-region-109178.stderr new file mode 100644 index 00000000000..0ac924452c0 --- /dev/null +++ b/tests/ui/inference/ice-cannot-relate-region-109178.stderr @@ -0,0 +1,21 @@ +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/ice-cannot-relate-region-109178.rs:7:31 + | +LL | struct Changes<const CHANGES: &[&'static str]> + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/ice-cannot-relate-region-109178.rs:12:21 + | +LL | impl<const CHANGES: &[&str]> Changes<CHANGES> where [(); CHANGES.len()]: {} + | ^ explicit lifetime name needed here + +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> $DIR/ice-cannot-relate-region-109178.rs:12:23 + | +LL | impl<const CHANGES: &[&str]> Changes<CHANGES> where [(); CHANGES.len()]: {} + | ^ explicit lifetime name needed here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0637`. diff --git a/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.rs b/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.rs new file mode 100644 index 00000000000..3c2aa176c0f --- /dev/null +++ b/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.rs @@ -0,0 +1,25 @@ +// test for #122098 ICE snapshot_vec.rs: index out of bounds: the len is 4 but the index is 4 + +trait LendingIterator { + type Item<'q>: 'a; + //~^ ERROR use of undeclared lifetime name `'a` + + fn for_each(mut self, mut f: Box<dyn FnMut(Self::Item<'_>) + 'static>) {} + //~^ ERROR the size for values of type `Self` cannot be known at compilation time +} + +struct Query<'q> {} +//~^ ERROR lifetime parameter `'q` is never used + +impl<'static> Query<'q> { +//~^ ERROR invalid lifetime parameter name: `'static` +//~^^ ERROR use of undeclared lifetime name `'q` + pub fn new() -> Self {} +} + +fn data() { + LendingIterator::for_each(Query::new(&data), Box::new); + //~^ ERROR this function takes 0 arguments but 1 argument was supplied +} + +pub fn main() {} diff --git a/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.stderr b/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.stderr new file mode 100644 index 00000000000..e2ddf474c4a --- /dev/null +++ b/tests/ui/inference/ice-ifer-var-leaked-out-of-rollback-122098.stderr @@ -0,0 +1,72 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:4:20 + | +LL | type Item<'q>: 'a; + | ^^ undeclared lifetime + | +help: consider introducing lifetime `'a` here + | +LL | type Item<'a, 'q>: 'a; + | +++ +help: consider introducing lifetime `'a` here + | +LL | trait LendingIterator<'a> { + | ++++ + +error[E0262]: invalid lifetime parameter name: `'static` + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:14:6 + | +LL | impl<'static> Query<'q> { + | ^^^^^^^ 'static is a reserved lifetime name + +error[E0261]: use of undeclared lifetime name `'q` + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:14:21 + | +LL | impl<'static> Query<'q> { + | - ^^ undeclared lifetime + | | + | help: consider introducing lifetime `'q` here: `'q,` + +error[E0392]: lifetime parameter `'q` is never used + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:11:14 + | +LL | struct Query<'q> {} + | ^^ unused lifetime parameter + | + = help: consider removing `'q`, referring to it in a field, or using a marker such as `PhantomData` + +error[E0277]: the size for values of type `Self` cannot be known at compilation time + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:7:17 + | +LL | fn for_each(mut self, mut f: Box<dyn FnMut(Self::Item<'_>) + 'static>) {} + | ^^^^^^^^ doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider further restricting `Self` + | +LL | fn for_each(mut self, mut f: Box<dyn FnMut(Self::Item<'_>) + 'static>) where Self: Sized {} + | +++++++++++++++++ +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn for_each(mut &self, mut f: Box<dyn FnMut(Self::Item<'_>) + 'static>) {} + | + + +error[E0061]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:21:31 + | +LL | LendingIterator::for_each(Query::new(&data), Box::new); + | ^^^^^^^^^^ ----- + | | + | unexpected argument of type `&fn() {data}` + | help: remove the extra argument + | +note: associated function defined here + --> $DIR/ice-ifer-var-leaked-out-of-rollback-122098.rs:17:12 + | +LL | pub fn new() -> Self {} + | ^^^ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0061, E0261, E0262, E0277, E0392. +For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/inference/issue-107090.rs b/tests/ui/inference/issue-107090.rs index d1c86fb03d7..799c3641833 100644 --- a/tests/ui/inference/issue-107090.rs +++ b/tests/ui/inference/issue-107090.rs @@ -19,7 +19,7 @@ impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> { fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { //~^ ERROR use of undeclared lifetime name - sadness.cast() //~ ERROR: mismatched types + sadness.cast() } fn main() {} diff --git a/tests/ui/inference/issue-107090.stderr b/tests/ui/inference/issue-107090.stderr index 55825f7765b..e509e262fb1 100644 --- a/tests/ui/inference/issue-107090.stderr +++ b/tests/ui/inference/issue-107090.stderr @@ -66,19 +66,6 @@ LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, | | | help: consider introducing lifetime `'short` here: `'short,` -error[E0308]: mismatched types - --> $DIR/issue-107090.rs:22:5 - | -LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T { - | - expected this type parameter ------- expected `&'out T` because of return type -LL | -LL | sadness.cast() - | ^^^^^^^^^^^^^^ expected `&T`, found `&Foo<'_, '_, T>` - | - = note: expected reference `&'out T` - found reference `&Foo<'_, '_, T>` - -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0261, E0308. -For more information about an error, try `rustc --explain E0261`. +For more information about this error, try `rustc --explain E0261`. diff --git a/tests/ui/inference/str-as-char.stderr b/tests/ui/inference/str-as-char.stderr index 216f4cda698..4ca71c5f067 100644 --- a/tests/ui/inference/str-as-char.stderr +++ b/tests/ui/inference/str-as-char.stderr @@ -4,7 +4,7 @@ error: character literal may only contain one codepoint LL | let _: &str = '"""'; | ^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "\"\"\""; | ~~~~~~~~ @@ -15,10 +15,10 @@ error: character literal may only contain one codepoint LL | let _: &str = '\"\"\"'; | ^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "\"\"\""; - | ~~~~~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/str-as-char.rs:10:19 @@ -26,7 +26,7 @@ error: character literal may only contain one codepoint LL | let _: &str = '"\"\"\\"\\"'; | ^^^^^^^^^^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "\"\"\\"\\"\\\""; | ~~~~~~~~~~~~~~~~~~~~ @@ -39,10 +39,10 @@ LL | let _: &str = 'a'; | | | expected due to this | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "a"; - | ~~~ + | ~ ~ error: aborting due to 4 previous errors diff --git a/tests/ui/issues/issue-10412.rs b/tests/ui/issues/issue-10412.rs index 0de170161b5..68ce0c2ea3c 100644 --- a/tests/ui/issues/issue-10412.rs +++ b/tests/ui/issues/issue-10412.rs @@ -8,7 +8,6 @@ impl<'self> Serializable<str> for &'self str { //~^ ERROR lifetimes cannot use keyword names //~| ERROR lifetimes cannot use keyword names //~| ERROR implicit elided lifetime not allowed here - //~| ERROR the size for values of type `str` cannot be known at compilation time [E0277] fn serialize(val: &'self str) -> Vec<u8> { //~^ ERROR lifetimes cannot use keyword names vec![1] diff --git a/tests/ui/issues/issue-10412.stderr b/tests/ui/issues/issue-10412.stderr index 02a26034f9a..c74ba1306cc 100644 --- a/tests/ui/issues/issue-10412.stderr +++ b/tests/ui/issues/issue-10412.stderr @@ -29,13 +29,13 @@ LL | impl<'self> Serializable<str> for &'self str { | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:12:24 + --> $DIR/issue-10412.rs:11:24 | LL | fn serialize(val: &'self str) -> Vec<u8> { | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:16:37 + --> $DIR/issue-10412.rs:15:37 | LL | fn deserialize(repr: &[u8]) -> &'self str { | ^^^^^ @@ -51,24 +51,6 @@ help: indicate the anonymous lifetime LL | impl<'self> Serializable<'_, str> for &'self str { | +++ -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/issue-10412.rs:7:13 - | -LL | impl<'self> Serializable<str> for &'self str { - | ^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `str` -note: required by an implicit `Sized` bound in `Serializable` - --> $DIR/issue-10412.rs:1:27 - | -LL | trait Serializable<'self, T> { - | ^ required by the implicit `Sized` requirement on this type parameter in `Serializable` -help: consider relaxing the implicit `Sized` restriction - | -LL | trait Serializable<'self, T: ?Sized> { - | ++++++++ - -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors -Some errors have detailed explanations: E0277, E0726. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0726`. diff --git a/tests/ui/issues/issue-17361.rs b/tests/ui/issues/issue-17361.rs index 8e85f0791d6..1b1eeb5a252 100644 --- a/tests/ui/issues/issue-17361.rs +++ b/tests/ui/issues/issue-17361.rs @@ -1,5 +1,5 @@ //@ run-pass -// Test that astconv doesn't forget about mutability of &mut str +// Test that HIR ty lowering doesn't forget about mutability of `&mut str`. //@ pretty-expanded FIXME #23616 diff --git a/tests/ui/issues/issue-23589.stderr b/tests/ui/issues/issue-23589.stderr index 1a91f5e04db..21d383b0e8c 100644 --- a/tests/ui/issues/issue-23589.stderr +++ b/tests/ui/issues/issue-23589.stderr @@ -15,10 +15,10 @@ error[E0308]: mismatched types LL | let v: Vec(&str) = vec!['1', '2']; | ^^^ expected `&str`, found `char` | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let v: Vec(&str) = vec!["1", '2']; - | ~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-27942.stderr b/tests/ui/issues/issue-27942.stderr index 7ea9345a668..8ea46bae26d 100644 --- a/tests/ui/issues/issue-27942.stderr +++ b/tests/ui/issues/issue-27942.stderr @@ -6,16 +6,16 @@ LL | fn select(&self) -> BufferViewHandle<R>; | = note: expected trait `Resources<'_>` found trait `Resources<'a>` -note: the anonymous lifetime defined here... - --> $DIR/issue-27942.rs:5:15 - | -LL | fn select(&self) -> BufferViewHandle<R>; - | ^^^^^ -note: ...does not necessarily outlive the lifetime `'a` as defined here +note: the lifetime `'a` as defined here... --> $DIR/issue-27942.rs:3:18 | LL | pub trait Buffer<'a, R: Resources<'a>> { | ^^ +note: ...does not necessarily outlive the anonymous lifetime defined here + --> $DIR/issue-27942.rs:5:15 + | +LL | fn select(&self) -> BufferViewHandle<R>; + | ^^^^^ error[E0308]: mismatched types --> $DIR/issue-27942.rs:5:25 @@ -25,16 +25,16 @@ LL | fn select(&self) -> BufferViewHandle<R>; | = note: expected trait `Resources<'_>` found trait `Resources<'a>` -note: the lifetime `'a` as defined here... - --> $DIR/issue-27942.rs:3:18 - | -LL | pub trait Buffer<'a, R: Resources<'a>> { - | ^^ -note: ...does not necessarily outlive the anonymous lifetime defined here +note: the anonymous lifetime defined here... --> $DIR/issue-27942.rs:5:15 | LL | fn select(&self) -> BufferViewHandle<R>; | ^^^^^ +note: ...does not necessarily outlive the lifetime `'a` as defined here + --> $DIR/issue-27942.rs:3:18 + | +LL | pub trait Buffer<'a, R: Resources<'a>> { + | ^^ error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-87199.rs b/tests/ui/issues/issue-87199.rs index 081c45d6151..34879c5a7ca 100644 --- a/tests/ui/issues/issue-87199.rs +++ b/tests/ui/issues/issue-87199.rs @@ -11,6 +11,7 @@ fn ref_arg<T: ?Send>(_: &T) {} //~^ warning: relaxing a default bound only does something for `?Sized` fn ret() -> impl Iterator<Item = ()> + ?Send { std::iter::empty() } //~^ warning: relaxing a default bound only does something for `?Sized` +//~| warning: relaxing a default bound only does something for `?Sized` // Check that there's no `?Sized` relaxation! fn main() { diff --git a/tests/ui/issues/issue-87199.stderr b/tests/ui/issues/issue-87199.stderr index 34433eef5c7..a0ed2946fb4 100644 --- a/tests/ui/issues/issue-87199.stderr +++ b/tests/ui/issues/issue-87199.stderr @@ -16,8 +16,16 @@ warning: relaxing a default bound only does something for `?Sized`; all other tr LL | fn ret() -> impl Iterator<Item = ()> + ?Send { std::iter::empty() } | ^^^^^ +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/issue-87199.rs:12:40 + | +LL | fn ret() -> impl Iterator<Item = ()> + ?Send { std::iter::empty() } + | ^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0277]: the size for values of type `[i32]` cannot be known at compilation time - --> $DIR/issue-87199.rs:18:15 + --> $DIR/issue-87199.rs:19:15 | LL | ref_arg::<[i32]>(&[5]); | ^^^^^ doesn't have a size known at compile-time @@ -33,6 +41,6 @@ help: consider relaxing the implicit `Sized` restriction LL | fn ref_arg<T: ?Send + ?Sized>(_: &T) {} | ++++++++ -error: aborting due to 1 previous error; 3 warnings emitted +error: aborting due to 1 previous error; 4 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/lexer/lex-bad-char-literals-2.stderr b/tests/ui/lexer/lex-bad-char-literals-2.stderr index 1518a37ab53..76cde00404a 100644 --- a/tests/ui/lexer/lex-bad-char-literals-2.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-2.stderr @@ -4,10 +4,10 @@ error: character literal may only contain one codepoint LL | 'nope' | ^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | "nope" - | ~~~~~~ + | ~ ~ error: aborting due to 1 previous error diff --git a/tests/ui/lexer/lex-bad-char-literals-3.stderr b/tests/ui/lexer/lex-bad-char-literals-3.stderr index 62a5e424cb4..3f339b2ef7d 100644 --- a/tests/ui/lexer/lex-bad-char-literals-3.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-3.stderr @@ -4,10 +4,10 @@ error: character literal may only contain one codepoint LL | static c: char = '●●'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | static c: char = "●●"; - | ~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-3.rs:5:20 @@ -15,10 +15,10 @@ error: character literal may only contain one codepoint LL | let ch: &str = '●●'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let ch: &str = "●●"; - | ~~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-5.stderr b/tests/ui/lexer/lex-bad-char-literals-5.stderr index 184817a6579..8004157e87f 100644 --- a/tests/ui/lexer/lex-bad-char-literals-5.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-5.stderr @@ -4,10 +4,10 @@ error: character literal may only contain one codepoint LL | static c: char = '\x10\x10'; | ^^^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | static c: char = "\x10\x10"; - | ~~~~~~~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-5.rs:5:20 @@ -15,10 +15,10 @@ error: character literal may only contain one codepoint LL | let ch: &str = '\x10\x10'; | ^^^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let ch: &str = "\x10\x10"; - | ~~~~~~~~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-6.stderr b/tests/ui/lexer/lex-bad-char-literals-6.stderr index 2fe30304a50..96d409d59bb 100644 --- a/tests/ui/lexer/lex-bad-char-literals-6.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-6.stderr @@ -4,10 +4,10 @@ error: character literal may only contain one codepoint LL | let x: &str = 'ab'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let x: &str = "ab"; - | ~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-6.rs:4:19 @@ -15,10 +15,10 @@ error: character literal may only contain one codepoint LL | let y: char = 'cd'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let y: char = "cd"; - | ~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-6.rs:6:13 @@ -26,10 +26,10 @@ error: character literal may only contain one codepoint LL | let z = 'ef'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let z = "ef"; - | ~~~~ + | ~ ~ error[E0308]: mismatched types --> $DIR/lex-bad-char-literals-6.rs:13:20 diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.fixed b/tests/ui/lexer/lex-bad-str-literal-as-char-1.fixed new file mode 100644 index 00000000000..b12139b0b40 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.fixed @@ -0,0 +1,6 @@ +//@ run-rustfix +fn main() { + println!("1 + 1"); + //~^ ERROR unterminated character literal + //~| ERROR lifetimes cannot start with a number +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-1.rs new file mode 100644 index 00000000000..6548792f33b --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.rs @@ -0,0 +1,6 @@ +//@ run-rustfix +fn main() { + println!('1 + 1'); + //~^ ERROR unterminated character literal + //~| ERROR lifetimes cannot start with a number +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr new file mode 100644 index 00000000000..57c5f82704e --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr @@ -0,0 +1,20 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-1.rs:3:20 + | +LL | println!('1 + 1'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("1 + 1"); + | ~ ~ + +error: lifetimes cannot start with a number + --> $DIR/lex-bad-str-literal-as-char-1.rs:3:14 + | +LL | println!('1 + 1'); + | ^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.fixed b/tests/ui/lexer/lex-bad-str-literal-as-char-2.fixed new file mode 100644 index 00000000000..3ccec537c6c --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.fixed @@ -0,0 +1,4 @@ +//@ run-rustfix +fn main() { + println!(" 1 + 1"); //~ ERROR character literal may only contain one codepoint +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-2.rs new file mode 100644 index 00000000000..8af72e47dbb --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.rs @@ -0,0 +1,4 @@ +//@ run-rustfix +fn main() { + println!(' 1 + 1'); //~ ERROR character literal may only contain one codepoint +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr new file mode 100644 index 00000000000..f64761af641 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr @@ -0,0 +1,13 @@ +error: character literal may only contain one codepoint + --> $DIR/lex-bad-str-literal-as-char-2.rs:3:14 + | +LL | println!(' 1 + 1'); + | ^^^^^^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!(" 1 + 1"); + | ~ ~ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs new file mode 100644 index 00000000000..0ae227da5f1 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs @@ -0,0 +1,7 @@ +//@ revisions: rust2015 rust2018 rust2021 +//@[rust2018] edition:2018 +//@[rust2021] edition:2021 +fn main() { + println!('hello world'); + //[rust2015,rust2018,rust2021]~^ ERROR unterminated character literal +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr new file mode 100644 index 00000000000..06f12742667 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr @@ -0,0 +1,14 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 + | +LL | println!('hello world'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr new file mode 100644 index 00000000000..06f12742667 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr @@ -0,0 +1,14 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 + | +LL | println!('hello world'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr new file mode 100644 index 00000000000..06f12742667 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr @@ -0,0 +1,14 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 + | +LL | println!('hello world'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/lifetimes/could-not-resolve-issue-121503.rs b/tests/ui/lifetimes/could-not-resolve-issue-121503.rs index 6bc70a907d9..363162370f2 100644 --- a/tests/ui/lifetimes/could-not-resolve-issue-121503.rs +++ b/tests/ui/lifetimes/could-not-resolve-issue-121503.rs @@ -4,8 +4,7 @@ struct Struct; impl Struct { async fn box_ref_Struct(self: Box<Self, impl FnMut(&mut Self)>) -> &u32 { - //~^ ERROR the trait bound `impl FnMut(&mut Self): Allocator` is not satisfied - //~| ERROR Box<Struct, impl FnMut(&mut Self)>` cannot be used as the type of `self` without + //~^ ERROR Box<Struct, impl FnMut(&mut Self)>` cannot be used as the type of `self` without &1 } } diff --git a/tests/ui/lifetimes/could-not-resolve-issue-121503.stderr b/tests/ui/lifetimes/could-not-resolve-issue-121503.stderr index a5d8239a2df..3babf63347c 100644 --- a/tests/ui/lifetimes/could-not-resolve-issue-121503.stderr +++ b/tests/ui/lifetimes/could-not-resolve-issue-121503.stderr @@ -1,16 +1,3 @@ -error[E0277]: the trait bound `impl FnMut(&mut Self): Allocator` is not satisfied - --> $DIR/could-not-resolve-issue-121503.rs:6:5 - | -LL | async fn box_ref_Struct(self: Box<Self, impl FnMut(&mut Self)>) -> &u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Allocator` is not implemented for `impl FnMut(&mut Self)` - | -note: required by a bound in `Box` - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL -help: consider further restricting this bound - | -LL | async fn box_ref_Struct(self: Box<Self, impl FnMut(&mut Self) + std::alloc::Allocator>) -> &u32 { - | +++++++++++++++++++++++ - error[E0658]: `Box<Struct, impl FnMut(&mut Self)>` cannot be used as the type of `self` without the `arbitrary_self_types` feature --> $DIR/could-not-resolve-issue-121503.rs:6:35 | @@ -22,7 +9,6 @@ LL | async fn box_ref_Struct(self: Box<Self, impl FnMut(&mut Self)>) -> &u32 = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0277, E0658. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/lifetimes/issue-26638.rs b/tests/ui/lifetimes/issue-26638.rs index 4bec3b3415b..11c730165f2 100644 --- a/tests/ui/lifetimes/issue-26638.rs +++ b/tests/ui/lifetimes/issue-26638.rs @@ -1,6 +1,5 @@ fn parse_type(iter: Box<dyn Iterator<Item=&str>+'static>) -> &str { iter.next() } //~^ ERROR missing lifetime specifier [E0106] -//~| ERROR mismatched types fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } //~^ ERROR missing lifetime specifier [E0106] diff --git a/tests/ui/lifetimes/issue-26638.stderr b/tests/ui/lifetimes/issue-26638.stderr index ee958686259..403a8c67ccb 100644 --- a/tests/ui/lifetimes/issue-26638.stderr +++ b/tests/ui/lifetimes/issue-26638.stderr @@ -11,7 +11,7 @@ LL | fn parse_type<'a>(iter: Box<dyn Iterator<Item=&'a str>+'static>) -> &'a str | ++++ ++ ++ error[E0106]: missing lifetime specifier - --> $DIR/issue-26638.rs:5:40 + --> $DIR/issue-26638.rs:4:40 | LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } | ^ expected named lifetime parameter @@ -31,7 +31,7 @@ LL | fn parse_type_2(iter: fn(&u8)->&u8) -> String { iter() } | ~~~~~~ error[E0106]: missing lifetime specifier - --> $DIR/issue-26638.rs:10:22 + --> $DIR/issue-26638.rs:9:22 | LL | fn parse_type_3() -> &str { unimplemented!() } | ^ expected named lifetime parameter @@ -46,23 +46,8 @@ help: instead, you are more likely to want to return an owned value LL | fn parse_type_3() -> String { unimplemented!() } | ~~~~~~ -error[E0308]: mismatched types - --> $DIR/issue-26638.rs:1:69 - | -LL | fn parse_type(iter: Box<dyn Iterator<Item=&str>+'static>) -> &str { iter.next() } - | ---- ^^^^^^^^^^^ expected `&str`, found `Option<&str>` - | | - | expected `&str` because of return type - | - = note: expected reference `&str` - found enum `Option<&str>` -help: consider using `Option::expect` to unwrap the `Option<&str>` value, panicking if the value is an `Option::None` - | -LL | fn parse_type(iter: Box<dyn Iterator<Item=&str>+'static>) -> &str { iter.next().expect("REASON") } - | +++++++++++++++++ - error[E0061]: this function takes 1 argument but 0 arguments were supplied - --> $DIR/issue-26638.rs:5:47 + --> $DIR/issue-26638.rs:4:47 | LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } | ^^^^-- an argument of type `&u8` is missing @@ -73,7 +58,7 @@ LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter(/* &u8 */) } | ~~~~~~~~~~~ error[E0308]: mismatched types - --> $DIR/issue-26638.rs:5:47 + --> $DIR/issue-26638.rs:4:47 | LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } | ---- ^^^^^^ expected `&str`, found `&u8` @@ -83,7 +68,7 @@ LL | fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() } = note: expected reference `&'static str` found reference `&u8` -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0061, E0106, E0308. For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.rs b/tests/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.rs index 56f89b70410..d6c918843c7 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.rs +++ b/tests/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.rs @@ -1,7 +1,5 @@ fn foo(x: &i32, y: &i32) -> &i32 { //~ ERROR missing lifetime if x > y { x } else { y } - //~^ ERROR: lifetime may not live long enough - //~| ERROR: lifetime may not live long enough } fn main() {} diff --git a/tests/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.stderr b/tests/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.stderr index db5b039d1c2..62b0a8a04bf 100644 --- a/tests/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.stderr +++ b/tests/ui/lifetimes/lifetime-errors/ex1b-return-no-names-if-else.stderr @@ -10,22 +10,6 @@ help: consider introducing a named lifetime parameter LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { | ++++ ++ ++ ++ -error: lifetime may not live long enough - --> $DIR/ex1b-return-no-names-if-else.rs:2:16 - | -LL | fn foo(x: &i32, y: &i32) -> &i32 { - | - let's call the lifetime of this reference `'1` -LL | if x > y { x } else { y } - | ^ returning this value requires that `'1` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/ex1b-return-no-names-if-else.rs:2:27 - | -LL | fn foo(x: &i32, y: &i32) -> &i32 { - | - let's call the lifetime of this reference `'2` -LL | if x > y { x } else { y } - | ^ returning this value requires that `'2` must outlive `'static` - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/lint/dead-code/pub-field-in-priv-mod.rs b/tests/ui/lint/dead-code/pub-field-in-priv-mod.rs new file mode 100644 index 00000000000..e49a164e940 --- /dev/null +++ b/tests/ui/lint/dead-code/pub-field-in-priv-mod.rs @@ -0,0 +1,11 @@ +#![deny(dead_code)] + +fn main() { + let _ = foo::S{f: false}; +} + +mod foo { + pub struct S { + pub f: bool, //~ ERROR field `f` is never read + } +} diff --git a/tests/ui/lint/dead-code/pub-field-in-priv-mod.stderr b/tests/ui/lint/dead-code/pub-field-in-priv-mod.stderr new file mode 100644 index 00000000000..11dd387315f --- /dev/null +++ b/tests/ui/lint/dead-code/pub-field-in-priv-mod.stderr @@ -0,0 +1,16 @@ +error: field `f` is never read + --> $DIR/pub-field-in-priv-mod.rs:9:13 + | +LL | pub struct S { + | - field in this struct +LL | pub f: bool, + | ^ + | +note: the lint level is defined here + --> $DIR/pub-field-in-priv-mod.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/lint-qualification.fixed b/tests/ui/lint/lint-qualification.fixed index 2070bbcef52..7c8fd5236e6 100644 --- a/tests/ui/lint/lint-qualification.fixed +++ b/tests/ui/lint/lint-qualification.fixed @@ -35,6 +35,7 @@ fn main() { foo::bar(); foo::$b(); // issue #96698 $a::bar(); + $a::$b(); } } m!(foo, bar); } diff --git a/tests/ui/lint/lint-qualification.rs b/tests/ui/lint/lint-qualification.rs index 41b7e17c6ed..009b3080d5c 100644 --- a/tests/ui/lint/lint-qualification.rs +++ b/tests/ui/lint/lint-qualification.rs @@ -35,6 +35,7 @@ fn main() { foo::bar(); foo::$b(); // issue #96698 $a::bar(); + $a::$b(); } } m!(foo, bar); } diff --git a/tests/ui/lint/unused/unused-associated-item.rs b/tests/ui/lint/unused/unused-associated-item.rs new file mode 100644 index 00000000000..27cb4e979f1 --- /dev/null +++ b/tests/ui/lint/unused/unused-associated-item.rs @@ -0,0 +1,21 @@ +//@ check-pass + +#![deny(unused_must_use)] + +use std::future::Future; +use std::pin::Pin; + +trait Factory { + type Output; +} + +impl Factory for () { + type Output = Pin<Box<dyn Future<Output = ()> + 'static>>; +} + +// Make sure we don't get an `unused_must_use` error on the *associated type bound*. +fn f() -> impl Factory<Output: Future> {} + +fn main() { + f(); +} diff --git a/tests/ui/match/postfix-match/pf-match-chain.rs b/tests/ui/match/postfix-match/pf-match-chain.rs new file mode 100644 index 00000000000..80546e1963b --- /dev/null +++ b/tests/ui/match/postfix-match/pf-match-chain.rs @@ -0,0 +1,16 @@ +//@ run-pass + +#![feature(postfix_match)] + +fn main() { + 1.match { + 2 => Some(0), + _ => None, + }.match { + None => Ok(true), + Some(_) => Err("nope") + }.match { + Ok(_) => (), + Err(_) => panic!() + } +} diff --git a/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs b/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs new file mode 100644 index 00000000000..f4cac46f7cd --- /dev/null +++ b/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs @@ -0,0 +1,7 @@ +#![feature(postfix_match)] + +fn main() { + Some(1).match { //~ non-exhaustive patterns + None => {}, + } +} diff --git a/tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr b/tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr new file mode 100644 index 00000000000..f458218bb5d --- /dev/null +++ b/tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr @@ -0,0 +1,21 @@ +error[E0004]: non-exhaustive patterns: `Some(_)` not covered + --> $DIR/pf-match-exhaustiveness.rs:4:5 + | +LL | Some(1).match { + | ^^^^^^^ pattern `Some(_)` not covered + | +note: `Option<i32>` defined here + --> $SRC_DIR/core/src/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL + | + = note: not covered + = note: the matched value is of type `Option<i32>` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ None => {}, +LL ~ Some(_) => todo!(), + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/match/postfix-match/pf-match-types.rs b/tests/ui/match/postfix-match/pf-match-types.rs new file mode 100644 index 00000000000..af205926fb6 --- /dev/null +++ b/tests/ui/match/postfix-match/pf-match-types.rs @@ -0,0 +1,15 @@ +#![feature(postfix_match)] + +fn main() { + Some(10).match { + //~^ NOTE `match` arms have incompatible types + Some(5) => false, + //~^ NOTE this is found to be of type `bool` + Some(2) => true, + //~^ NOTE this is found to be of type `bool` + None => (), + //~^ ERROR `match` arms have incompatible types + //~| NOTE expected `bool`, found `()` + _ => true + } +} diff --git a/tests/ui/match/postfix-match/pf-match-types.stderr b/tests/ui/match/postfix-match/pf-match-types.stderr new file mode 100644 index 00000000000..0cfc1363d5f --- /dev/null +++ b/tests/ui/match/postfix-match/pf-match-types.stderr @@ -0,0 +1,21 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/pf-match-types.rs:10:20 + | +LL | / Some(10).match { +LL | | +LL | | Some(5) => false, + | | ----- this is found to be of type `bool` +LL | | +LL | | Some(2) => true, + | | ---- this is found to be of type `bool` +LL | | +LL | | None => (), + | | ^^ expected `bool`, found `()` +... | +LL | | _ => true +LL | | } + | |_____- `match` arms have incompatible types + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/postfix-match/postfix-match.rs b/tests/ui/match/postfix-match/postfix-match.rs new file mode 100644 index 00000000000..03c4e8ab545 --- /dev/null +++ b/tests/ui/match/postfix-match/postfix-match.rs @@ -0,0 +1,62 @@ +//@ run-pass + +#![feature(postfix_match)] + +struct Bar { + foo: u8, + baz: u8, +} + +pub fn main() { + let thing = Some("thing"); + + thing.match { + Some("nothing") => {}, + Some(text) if text.eq_ignore_ascii_case("tapir") => {}, + Some("true") | Some("false") => {}, + Some("thing") => {}, + Some(_) => {}, + None => {} + }; + + let num = 2u8; + + num.match { + 0 => {}, + 1..=5 => {}, + _ => {}, + }; + + let slic = &[1, 2, 3, 4][..]; + + slic.match { + [1] => {}, + [2, _tail @ ..] => {}, + [1, _] => {}, + _ => {}, + }; + + slic[0].match { + 1 => 0, + i => i, + }; + + let out = (1, 2).match { + (1, 3) => 0, + (_, 1) => 0, + (1, i) => i, + _ => 3, + }; + assert!(out == 2); + + let strct = Bar { + foo: 3, + baz: 4 + }; + + strct.match { + Bar { foo: 1, .. } => {}, + Bar { baz: 2, .. } => {}, + _ => (), + }; +} diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index 9feb2a7320d..d7933a39eaa 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -53,17 +53,16 @@ LL | fn case2() { error[E0597]: `a` does not live long enough --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:30:26 | -LL | let a = 0; - | - binding `a` declared here -LL | let cell = Cell::new(&a); - | ^^ borrowed value does not live long enough +LL | let a = 0; + | - binding `a` declared here +LL | let cell = Cell::new(&a); + | ----------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `a` is borrowed for `'static` ... -LL | / foo(cell, |cell_a, cell_x| { -LL | | cell_x.set(cell_a.get()); // forces 'a: 'x, implies 'a = 'static -> borrow error -LL | | }) - | |______- argument requires that `a` is borrowed for `'static` -LL | } - | - `a` dropped here while still borrowed +LL | } + | - `a` dropped here while still borrowed error: aborting due to 2 previous errors diff --git a/tests/ui/nll/ice-106874.rs b/tests/ui/nll/ice-106874.rs new file mode 100644 index 00000000000..9337eee961b --- /dev/null +++ b/tests/ui/nll/ice-106874.rs @@ -0,0 +1,48 @@ +// issue: rust-lang/rust#106874 +// ICE BoundUniversalRegionError + +use std::marker::PhantomData; +use std::rc::Rc; + +pub fn func<V, F: Fn(&mut V)>(f: F) -> A<impl X> { + A(B(C::new(D::new(move |st| f(st))))) + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `Fn` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `Fn` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `Fn` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR higher-ranked subtype error + //~| ERROR higher-ranked subtype error +} + +trait X {} +trait Y { + type V; +} + +struct A<T>(T); + +struct B<T>(Rc<T>); +impl<T> X for B<T> {} + +struct C<T: Y>(T::V); +impl<T: Y> C<T> { + fn new(_: T) -> Rc<Self> { + todo!() + } +} +struct D<V, F>(F, PhantomData<fn(&mut V)>); + +impl<V, F> D<V, F> { + fn new(_: F) -> Self { + todo!() + } +} +impl<V, F: Fn(&mut V)> Y for D<V, F> { + type V = V; +} + +pub fn main() {} diff --git a/tests/ui/nll/ice-106874.stderr b/tests/ui/nll/ice-106874.stderr new file mode 100644 index 00000000000..ead4d490a62 --- /dev/null +++ b/tests/ui/nll/ice-106874.stderr @@ -0,0 +1,90 @@ +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:5 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'0 mut V)` must implement `FnOnce<(&mut V,)>`, for some specific lifetime `'0`... + = note: ...but it actually implements `FnOnce<(&'1 mut V,)>`, for some specific lifetime `'1` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:5 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'0 mut V)` must implement `FnOnce<(&mut V,)>`, for some specific lifetime `'0`... + = note: ...but it actually implements `FnOnce<(&'1 mut V,)>`, for some specific lifetime `'1` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `Fn` is not general enough + --> $DIR/ice-106874.rs:8:7 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:7 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `Fn` is not general enough + --> $DIR/ice-106874.rs:8:7 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:9 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `Fn` is not general enough + --> $DIR/ice-106874.rs:8:9 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:9 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: higher-ranked subtype error + --> $DIR/ice-106874.rs:8:41 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^ + +error: higher-ranked subtype error + --> $DIR/ice-106874.rs:8:41 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 10 previous errors + diff --git a/tests/ui/nll/issue-54189.rs b/tests/ui/nll/issue-54189.rs index 70aecc384ef..2339a722a7b 100644 --- a/tests/ui/nll/issue-54189.rs +++ b/tests/ui/nll/issue-54189.rs @@ -1,5 +1,6 @@ fn bug() -> impl for <'r> Fn() -> &'r () { || { &() } } //~^ ERROR binding for associated type `Output` references lifetime `'r` +//~| ERROR binding for associated type `Output` references lifetime `'r` fn main() { let f = bug(); diff --git a/tests/ui/nll/issue-54189.stderr b/tests/ui/nll/issue-54189.stderr index 14ed2bb222d..ce756a91f56 100644 --- a/tests/ui/nll/issue-54189.stderr +++ b/tests/ui/nll/issue-54189.stderr @@ -4,6 +4,14 @@ error[E0582]: binding for associated type `Output` references lifetime `'r`, whi LL | fn bug() -> impl for <'r> Fn() -> &'r () { || { &() } } | ^^^^^^ -error: aborting due to 1 previous error +error[E0582]: binding for associated type `Output` references lifetime `'r`, which does not appear in the trait input types + --> $DIR/issue-54189.rs:1:35 + | +LL | fn bug() -> impl for <'r> Fn() -> &'r () { || { &() } } + | ^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0582`. diff --git a/tests/ui/nll/user-annotations/adt-nullary-enums.stderr b/tests/ui/nll/user-annotations/adt-nullary-enums.stderr index 5b385feeedc..644fc94f730 100644 --- a/tests/ui/nll/user-annotations/adt-nullary-enums.stderr +++ b/tests/ui/nll/user-annotations/adt-nullary-enums.stderr @@ -1,16 +1,17 @@ error[E0597]: `c` does not live long enough --> $DIR/adt-nullary-enums.rs:33:41 | -LL | let c = 66; - | - binding `c` declared here -LL | / combine( -LL | | SomeEnum::SomeVariant(Cell::new(&c)), - | | ^^ borrowed value does not live long enough -LL | | SomeEnum::SomeOtherVariant::<Cell<&'static u32>>, -LL | | ); - | |_____- argument requires that `c` is borrowed for `'static` -LL | } - | - `c` dropped here while still borrowed +LL | let c = 66; + | - binding `c` declared here +LL | combine( +LL | SomeEnum::SomeVariant(Cell::new(&c)), + | ----------^^- + | | | + | | borrowed value does not live long enough + | argument requires that `c` is borrowed for `'static` +... +LL | } + | - `c` dropped here while still borrowed error[E0597]: `c` does not live long enough --> $DIR/adt-nullary-enums.rs:41:41 diff --git a/tests/ui/nll/user-annotations/region-error-ice-109072.rs b/tests/ui/nll/user-annotations/region-error-ice-109072.rs index 3f2ad3ccbf5..bcdc6651cf5 100644 --- a/tests/ui/nll/user-annotations/region-error-ice-109072.rs +++ b/tests/ui/nll/user-annotations/region-error-ice-109072.rs @@ -11,4 +11,5 @@ impl Lt<'missing> for () { //~ ERROR undeclared lifetime fn main() { let _: <() as Lt<'_>>::T = &(); + //~^ ERROR the trait bound `(): Lt<'_>` is not satisfied } diff --git a/tests/ui/nll/user-annotations/region-error-ice-109072.stderr b/tests/ui/nll/user-annotations/region-error-ice-109072.stderr index d90971bed25..c187c17d98c 100644 --- a/tests/ui/nll/user-annotations/region-error-ice-109072.stderr +++ b/tests/ui/nll/user-annotations/region-error-ice-109072.stderr @@ -21,6 +21,13 @@ help: consider introducing lifetime `'missing` here LL | impl<'missing> Lt<'missing> for () { | ++++++++++ -error: aborting due to 2 previous errors +error[E0277]: the trait bound `(): Lt<'_>` is not satisfied + --> $DIR/region-error-ice-109072.rs:13:13 + | +LL | let _: <() as Lt<'_>>::T = &(); + | ^^ the trait `Lt<'_>` is not implemented for `()` + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0261`. +Some errors have detailed explanations: E0261, E0277. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs index 345c8a25f79..51be999a632 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs +++ b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.rs @@ -15,7 +15,6 @@ fn is_static<T>(_: T) where T: 'static { } // code forces us into a conservative, hacky path. fn bar(x: &str) -> &dyn Foo<Item = dyn Bar> { &() } //~^ ERROR please supply an explicit bound -//~| ERROR `(): Foo<'_>` is not satisfied fn main() { let s = format!("foo"); diff --git a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr index d227c8778fe..688f8af0822 100644 --- a/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr +++ b/tests/ui/object-lifetime/object-lifetime-default-dyn-binding-nonstatic3.stderr @@ -4,20 +4,6 @@ error[E0228]: the lifetime bound for this object type cannot be deduced from con LL | fn bar(x: &str) -> &dyn Foo<Item = dyn Bar> { &() } | ^^^^^^^ -error[E0277]: the trait bound `(): Foo<'_>` is not satisfied - --> $DIR/object-lifetime-default-dyn-binding-nonstatic3.rs:16:47 - | -LL | fn bar(x: &str) -> &dyn Foo<Item = dyn Bar> { &() } - | ^^^ the trait `Foo<'_>` is not implemented for `()` - | -help: this trait has no implementations, consider adding one - --> $DIR/object-lifetime-default-dyn-binding-nonstatic3.rs:4:1 - | -LL | trait Foo<'a> { - | ^^^^^^^^^^^^^ - = note: required for the cast from `&()` to `&dyn Foo<'_, Item = dyn Bar>` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0228, E0277. -For more information about an error, try `rustc --explain E0228`. +For more information about this error, try `rustc --explain E0228`. diff --git a/tests/ui/panics/panic-in-message-fmt.rs b/tests/ui/panics/panic-in-message-fmt.rs new file mode 100644 index 00000000000..e5bedf96b35 --- /dev/null +++ b/tests/ui/panics/panic-in-message-fmt.rs @@ -0,0 +1,25 @@ +// Checks what happens when formatting the panic message panics. + +//@ run-fail +//@ exec-env:RUST_BACKTRACE=0 +//@ check-run-results +//@ error-pattern: panicked while processing panic +//@ normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@ normalize-stderr-test: "\n +at [^\n]+" -> "" +//@ normalize-stderr-test: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL" +//@ ignore-emscripten "RuntimeError" junk in output + +use std::fmt::{Display, self}; + +struct MyStruct; + +impl Display for MyStruct { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + todo!() + } +} + +fn main() { + let instance = MyStruct; + panic!("this is wrong: {}", instance); +} diff --git a/tests/ui/panics/panic-in-message-fmt.run.stderr b/tests/ui/panics/panic-in-message-fmt.run.stderr new file mode 100644 index 00000000000..c3a5733c8ae --- /dev/null +++ b/tests/ui/panics/panic-in-message-fmt.run.stderr @@ -0,0 +1,2 @@ +panicked at $DIR/panic-in-message-fmt.rs:18:9: +thread panicked while processing panic. aborting. diff --git a/tests/ui/parser/attribute/attr-bad-meta-4.rs b/tests/ui/parser/attribute/attr-bad-meta-4.rs new file mode 100644 index 00000000000..cedbd1d6686 --- /dev/null +++ b/tests/ui/parser/attribute/attr-bad-meta-4.rs @@ -0,0 +1,12 @@ +macro_rules! mac { + ($attr_item: meta) => { + #[cfg($attr_item)] + //~^ ERROR expected unsuffixed literal or identifier, found `an(arbitrary token stream)` + //~| ERROR expected unsuffixed literal or identifier, found `an(arbitrary token stream)` + struct S; + } +} + +mac!(an(arbitrary token stream)); + +fn main() {} diff --git a/tests/ui/parser/attribute/attr-bad-meta-4.stderr b/tests/ui/parser/attribute/attr-bad-meta-4.stderr new file mode 100644 index 00000000000..a543bcb692e --- /dev/null +++ b/tests/ui/parser/attribute/attr-bad-meta-4.stderr @@ -0,0 +1,25 @@ +error: expected unsuffixed literal or identifier, found `an(arbitrary token stream)` + --> $DIR/attr-bad-meta-4.rs:3:15 + | +LL | #[cfg($attr_item)] + | ^^^^^^^^^^ +... +LL | mac!(an(arbitrary token stream)); + | -------------------------------- in this macro invocation + | + = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected unsuffixed literal or identifier, found `an(arbitrary token stream)` + --> $DIR/attr-bad-meta-4.rs:3:15 + | +LL | #[cfg($attr_item)] + | ^^^^^^^^^^ +... +LL | mac!(an(arbitrary token stream)); + | -------------------------------- in this macro invocation + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/issues/issue-64732.rs b/tests/ui/parser/issues/issue-64732.rs index 2db51ea6042..ff0f97ea211 100644 --- a/tests/ui/parser/issues/issue-64732.rs +++ b/tests/ui/parser/issues/issue-64732.rs @@ -5,5 +5,5 @@ fn main() { //~| HELP if you meant to write a byte string literal, use double quotes let _bar = 'hello'; //~^ ERROR character literal may only contain one codepoint - //~| HELP if you meant to write a `str` literal, use double quotes + //~| HELP if you meant to write a string literal, use double quotes } diff --git a/tests/ui/parser/issues/issue-64732.stderr b/tests/ui/parser/issues/issue-64732.stderr index 80462549377..7ec2df6d3bf 100644 --- a/tests/ui/parser/issues/issue-64732.stderr +++ b/tests/ui/parser/issues/issue-64732.stderr @@ -7,7 +7,7 @@ LL | let _foo = b'hello\0'; help: if you meant to write a byte string literal, use double quotes | LL | let _foo = b"hello\0"; - | ~~~~~~~~~~ + | ~~ ~ error: character literal may only contain one codepoint --> $DIR/issue-64732.rs:6:16 @@ -15,10 +15,10 @@ error: character literal may only contain one codepoint LL | let _bar = 'hello'; | ^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _bar = "hello"; - | ~~~~~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/parser/parser-ice-ed2021-await-105210.rs b/tests/ui/parser/parser-ice-ed2021-await-105210.rs new file mode 100644 index 00000000000..95383cc81c2 --- /dev/null +++ b/tests/ui/parser/parser-ice-ed2021-await-105210.rs @@ -0,0 +1,10 @@ +// ICE #105210 self.lines.iter().all(|r| !r.iter().any(|sc| sc.chr == \'\\t\')) +// ignore-tidy-tab +//@ edition:2021 +pub fn main() {} + +fn box () { + (( h (const {( default ( await ( await ( (move {await((((}} + //~^ ERROR mismatched closing delimiter: `}` + //~^^ ERROR mismatched closing delimiter: `}` +//~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/parser-ice-ed2021-await-105210.stderr b/tests/ui/parser/parser-ice-ed2021-await-105210.stderr new file mode 100644 index 00000000000..fc54476c220 --- /dev/null +++ b/tests/ui/parser/parser-ice-ed2021-await-105210.stderr @@ -0,0 +1,34 @@ +error: mismatched closing delimiter: `}` + --> $DIR/parser-ice-ed2021-await-105210.rs:7:58 + | +LL | (( h (const {( default ( await ( await ( (move {await((((}} + | - ^^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: mismatched closing delimiter: `}` + --> $DIR/parser-ice-ed2021-await-105210.rs:7:43 + | +LL | (( h (const {( default ( await ( await ( (move {await((((}} + | - ^ ^ mismatched closing delimiter + | | | + | | unclosed delimiter + | closing delimiter possibly meant for this + +error: this file contains an unclosed delimiter + --> $DIR/parser-ice-ed2021-await-105210.rs:10:52 + | +LL | fn box () { + | - unclosed delimiter +LL | (( h (const {( default ( await ( await ( (move {await((((}} + | -- - unclosed delimiter + | || + | |unclosed delimiter + | unclosed delimiter +... +LL | + | ^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/parser/unicode-character-literal.fixed b/tests/ui/parser/unicode-character-literal.fixed index 9e31890151c..e401ecaf5da 100644 --- a/tests/ui/parser/unicode-character-literal.fixed +++ b/tests/ui/parser/unicode-character-literal.fixed @@ -7,12 +7,12 @@ fn main() { let _spade = "♠️"; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `♠` is followed by the combining mark `\u{fe0f}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _s = "ṩ̂̊"; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _a = 'Å'; //~^ ERROR: character literal may only contain one codepoint diff --git a/tests/ui/parser/unicode-character-literal.rs b/tests/ui/parser/unicode-character-literal.rs index d886e5b26a5..428e4e1ac5a 100644 --- a/tests/ui/parser/unicode-character-literal.rs +++ b/tests/ui/parser/unicode-character-literal.rs @@ -7,12 +7,12 @@ fn main() { let _spade = '♠️'; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `♠` is followed by the combining mark `\u{fe0f}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _s = 'ṩ̂̊'; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _a = 'Å'; //~^ ERROR: character literal may only contain one codepoint diff --git a/tests/ui/parser/unicode-character-literal.stderr b/tests/ui/parser/unicode-character-literal.stderr index 5cd3bd0fe69..726cde2b413 100644 --- a/tests/ui/parser/unicode-character-literal.stderr +++ b/tests/ui/parser/unicode-character-literal.stderr @@ -9,10 +9,10 @@ note: this `♠` is followed by the combining mark `\u{fe0f}` | LL | let _spade = '♠️'; | ^ -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _spade = "♠️"; - | ~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/unicode-character-literal.rs:12:14 @@ -25,10 +25,10 @@ note: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}` | LL | let _s = 'ṩ̂̊'; | ^ -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _s = "ṩ̂̊"; - | ~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/unicode-character-literal.rs:17:14 diff --git a/tests/ui/pattern/deref-patterns/typeck.rs b/tests/ui/pattern/deref-patterns/typeck.rs new file mode 100644 index 00000000000..ead6dcdbaf0 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/typeck.rs @@ -0,0 +1,31 @@ +//@ check-pass +#![feature(deref_patterns)] +#![allow(incomplete_features)] + +use std::rc::Rc; + +fn main() { + let vec: Vec<u32> = Vec::new(); + match vec { + deref!([..]) => {} + _ => {} + } + match Box::new(true) { + deref!(true) => {} + _ => {} + } + match &Box::new(true) { + deref!(true) => {} + _ => {} + } + match &Rc::new(0) { + deref!(1..) => {} + _ => {} + } + // FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a + // place of type `str`. + // match "foo".to_string() { + // box "foo" => {} + // _ => {} + // } +} diff --git a/tests/ui/print_type_sizes/async.stdout b/tests/ui/print_type_sizes/async.stdout index e1be98f85d8..1df4d85d09e 100644 --- a/tests/ui/print_type_sizes/async.stdout +++ b/tests/ui/print_type_sizes/async.stdout @@ -5,7 +5,7 @@ print-type-size upvar `.arg`: 8192 bytes print-type-size variant `Suspend0`: 16385 bytes print-type-size upvar `.arg`: 8192 bytes print-type-size local `.arg`: 8192 bytes -print-type-size local `.__awaitee`: 1 bytes +print-type-size local `.__awaitee`: 1 bytes, type: {async fn body@$DIR/async.rs:8:17: 8:19} print-type-size variant `Returned`: 8192 bytes print-type-size upvar `.arg`: 8192 bytes print-type-size variant `Panicked`: 8192 bytes diff --git a/tests/ui/privacy/suggest-making-field-public.fixed b/tests/ui/privacy/suggest-making-field-public.fixed index 29dcde88ab4..8a5686aa5e1 100644 --- a/tests/ui/privacy/suggest-making-field-public.fixed +++ b/tests/ui/privacy/suggest-making-field-public.fixed @@ -1,5 +1,5 @@ //@ run-rustfix -mod a { +pub mod a { pub struct A(pub String); } diff --git a/tests/ui/privacy/suggest-making-field-public.rs b/tests/ui/privacy/suggest-making-field-public.rs index c9f04757b2f..63fdb0bce6a 100644 --- a/tests/ui/privacy/suggest-making-field-public.rs +++ b/tests/ui/privacy/suggest-making-field-public.rs @@ -1,5 +1,5 @@ //@ run-rustfix -mod a { +pub mod a { pub struct A(pub(self)String); } diff --git a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs new file mode 100644 index 00000000000..87ce4f1e14d --- /dev/null +++ b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs @@ -0,0 +1,15 @@ +// issue: rust-lang/rust#105047 +// ICE raw ptr comparison should already be caught in the trait systems + +#![feature(raw_ref_op)] + +const RCZ: *const i32 = &raw const *&0; + +const fn f() { + if let RCZ = &raw const *&0 { } + //~^ WARN function pointers and raw pointers not derived from integers in patterns + //~| ERROR pointers cannot be reliably compared during const eval + //~| WARN this was previously accepted by the compiler but is being phased out +} + +fn main() {} diff --git a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr new file mode 100644 index 00000000000..9c472cda244 --- /dev/null +++ b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr @@ -0,0 +1,31 @@ +warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/const-eval-compare-ice-105047.rs:9:12 + | +LL | if let RCZ = &raw const *&0 { } + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #120362 <https://github.com/rust-lang/rust/issues/120362> + = note: `#[warn(pointer_structural_match)]` on by default + +error: pointers cannot be reliably compared during const eval + --> $DIR/const-eval-compare-ice-105047.rs:9:12 + | +LL | if let RCZ = &raw const *&0 { } + | ^^^ + | + = note: see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information + +error: aborting due to 1 previous error; 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/const-eval-compare-ice-105047.rs:9:12 + | +LL | if let RCZ = &raw const *&0 { } + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #120362 <https://github.com/rust-lang/rust/issues/120362> + = note: `#[warn(pointer_structural_match)]` on by default + diff --git a/tests/ui/raw-ref-op/raw-ref-temp.stderr b/tests/ui/raw-ref-op/raw-ref-temp.stderr index b9666162517..1f6d85e4a7e 100644 --- a/tests/ui/raw-ref-op/raw-ref-temp.stderr +++ b/tests/ui/raw-ref-op/raw-ref-temp.stderr @@ -75,24 +75,32 @@ error[E0745]: cannot take address of a temporary | LL | let ref_ascribe = &raw const type_ascribe!(2, i32); | ^^^^^^^^^^^^^^^^^^^^^ temporary value + | + = note: this error originates in the macro `type_ascribe` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0745]: cannot take address of a temporary --> $DIR/raw-ref-temp.rs:27:36 | LL | let mut_ref_ascribe = &raw mut type_ascribe!(3, i32); | ^^^^^^^^^^^^^^^^^^^^^ temporary value + | + = note: this error originates in the macro `type_ascribe` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0745]: cannot take address of a temporary --> $DIR/raw-ref-temp.rs:29:40 | LL | let ascribe_field_ref = &raw const type_ascribe!(PAIR.0, i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value + | + = note: this error originates in the macro `type_ascribe` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0745]: cannot take address of a temporary --> $DIR/raw-ref-temp.rs:30:38 | LL | let ascribe_index_ref = &raw mut type_ascribe!(ARRAY[0], i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value + | + = note: this error originates in the macro `type_ascribe` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 16 previous errors diff --git a/tests/ui/reachable/expr_type.stderr b/tests/ui/reachable/expr_type.stderr index 70536326fd8..008b867e230 100644 --- a/tests/ui/reachable/expr_type.stderr +++ b/tests/ui/reachable/expr_type.stderr @@ -12,6 +12,7 @@ note: the lint level is defined here | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `type_ascribe` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/return/return-impl-trait-bad.stderr b/tests/ui/return/return-impl-trait-bad.stderr index a015b9f53af..6277c5feb20 100644 --- a/tests/ui/return/return-impl-trait-bad.stderr +++ b/tests/ui/return/return-impl-trait-bad.stderr @@ -10,6 +10,7 @@ LL | "this should not suggest impl Trait" | = note: expected type parameter `T` found reference `&'static str` + = note: the caller chooses a type for `T` which can be different from `&'static str` error[E0308]: mismatched types --> $DIR/return-impl-trait-bad.rs:9:5 @@ -23,6 +24,7 @@ LL | "this will not suggest it, because that would probably be wrong" | = note: expected type parameter `T` found reference `&'static str` + = note: the caller chooses a type for `T` which can be different from `&'static str` error[E0308]: mismatched types --> $DIR/return-impl-trait-bad.rs:17:5 @@ -37,6 +39,7 @@ LL | "don't suggest this, because Option<T> places additional constraints" | = note: expected type parameter `T` found reference `&'static str` + = note: the caller chooses a type for `T` which can be different from `&'static str` error[E0308]: mismatched types --> $DIR/return-impl-trait-bad.rs:28:5 @@ -53,6 +56,7 @@ LL | "don't suggest this, because the generic param is used in the bound." | = note: expected type parameter `T` found reference `&'static str` + = note: the caller chooses a type for `T` which can be different from `&'static str` error: aborting due to 4 previous errors diff --git a/tests/ui/return/return-impl-trait.stderr b/tests/ui/return/return-impl-trait.stderr index 707f014a16f..114ae0c2445 100644 --- a/tests/ui/return/return-impl-trait.stderr +++ b/tests/ui/return/return-impl-trait.stderr @@ -12,6 +12,7 @@ LL | () | = note: expected type parameter `T` found unit type `()` + = note: the caller chooses a type for `T` which can be different from `()` error[E0308]: mismatched types --> $DIR/return-impl-trait.rs:23:5 @@ -28,6 +29,7 @@ LL | () | = note: expected type parameter `T` found unit type `()` + = note: the caller chooses a type for `T` which can be different from `()` error: aborting due to 2 previous errors diff --git a/tests/ui/return/return-ty-mismatch-note.rs b/tests/ui/return/return-ty-mismatch-note.rs new file mode 100644 index 00000000000..352bc2a1637 --- /dev/null +++ b/tests/ui/return/return-ty-mismatch-note.rs @@ -0,0 +1,21 @@ +// Checks existence of a note for "a caller chooses ty for ty param" upon return ty mismatch. + +fn f<T>() -> (T,) { + (0,) //~ ERROR mismatched types +} + +fn g<U, V>() -> (U, V) { + (0, "foo") + //~^ ERROR mismatched types + //~| ERROR mismatched types +} + +fn h() -> u8 { + 0u8 +} + +fn main() { + f::<()>(); + g::<(), ()>; + let _ = h(); +} diff --git a/tests/ui/return/return-ty-mismatch-note.stderr b/tests/ui/return/return-ty-mismatch-note.stderr new file mode 100644 index 00000000000..135903da5c2 --- /dev/null +++ b/tests/ui/return/return-ty-mismatch-note.stderr @@ -0,0 +1,36 @@ +error[E0308]: mismatched types + --> $DIR/return-ty-mismatch-note.rs:4:6 + | +LL | fn f<T>() -> (T,) { + | - expected this type parameter +LL | (0,) + | ^ expected type parameter `T`, found integer + | + = note: expected type parameter `T` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/return-ty-mismatch-note.rs:8:6 + | +LL | fn g<U, V>() -> (U, V) { + | - expected this type parameter +LL | (0, "foo") + | ^ expected type parameter `U`, found integer + | + = note: expected type parameter `U` + found type `{integer}` + +error[E0308]: mismatched types + --> $DIR/return-ty-mismatch-note.rs:8:9 + | +LL | fn g<U, V>() -> (U, V) { + | - expected this type parameter +LL | (0, "foo") + | ^^^^^ expected type parameter `V`, found `&str` + | + = note: expected type parameter `V` + found reference `&'static str` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-parse-not-item.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-parse-not-item.stderr index ace2e7e46c4..12cc79f5961 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-parse-not-item.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-parse-not-item.stderr @@ -4,5 +4,13 @@ error: `~const` can only be applied to `#[const_trait]` traits LL | const fn test() -> impl ~const Fn() { | ^^^^ -error: aborting due to 1 previous error +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-closure-parse-not-item.rs:7:32 + | +LL | const fn test() -> impl ~const Fn() { + | ^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs index 7d817d09c7f..21197fcaa27 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.rs @@ -1,7 +1,9 @@ #![feature(const_trait_impl, effects)] -const fn test() -> impl ~const Fn() { //~ ERROR `~const` can only be applied to `#[const_trait]` traits - //~^ ERROR cycle detected +const fn test() -> impl ~const Fn() { + //~^ ERROR `~const` can only be applied to `#[const_trait]` traits + //~| ERROR `~const` can only be applied to `#[const_trait]` traits + //~| ERROR cycle detected const move || { //~ ERROR const closures are experimental let sl: &[u8] = b"foo"; diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr index 0f6240cc03b..2f805110917 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/ice-112822-expected-type-for-param.stderr @@ -1,5 +1,5 @@ error[E0658]: const closures are experimental - --> $DIR/ice-112822-expected-type-for-param.rs:5:5 + --> $DIR/ice-112822-expected-type-for-param.rs:7:5 | LL | const move || { | ^^^^^ @@ -14,8 +14,16 @@ error: `~const` can only be applied to `#[const_trait]` traits LL | const fn test() -> impl ~const Fn() { | ^^^^ +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/ice-112822-expected-type-for-param.rs:3:32 + | +LL | const fn test() -> impl ~const Fn() { + | ^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0277]: can't compare `&u8` with `&u8` - --> $DIR/ice-112822-expected-type-for-param.rs:10:17 + --> $DIR/ice-112822-expected-type-for-param.rs:12:17 | LL | assert_eq!(first, &b'f'); | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `&u8 == &u8` @@ -54,7 +62,7 @@ LL | const fn test() -> impl ~const Fn() { | ^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0277, E0391, E0658. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs b/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs new file mode 100644 index 00000000000..a46a3afd734 --- /dev/null +++ b/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs @@ -0,0 +1,18 @@ +// Tests that converting a closure to a function pointer works +// The notable thing being tested here is that when the closure does not capture anything, +// the call method from its Fn trait takes a ZST representing its environment. The compiler then +// uses the assumption that the ZST is non-passed to reify this into a function pointer. +// +// This checks that the reified function pointer will have the expected alias set at its call-site. + +//@ needs-sanitizer-cfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi +//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0 +//@ run-pass + +pub fn main() { + let f: &fn() = &((|| ()) as _); + f(); +} diff --git a/tests/ui/specialization/broken-mir-drop-glue-107228.rs b/tests/ui/specialization/broken-mir-drop-glue-107228.rs new file mode 100644 index 00000000000..5a6dbf9ffc7 --- /dev/null +++ b/tests/ui/specialization/broken-mir-drop-glue-107228.rs @@ -0,0 +1,28 @@ +// issue: rust-lang/rust#107228 +// ICE broken MIR in DropGlue +//@ compile-flags: -Zvalidate-mir +//@ check-pass + +#![feature(specialization)] +#![crate_type="lib"] +#![allow(incomplete_features)] + +pub(crate) trait SpecTrait { + type Assoc; +} + +impl<C> SpecTrait for C { + default type Assoc = Vec<Self>; +} + +pub(crate) struct AssocWrap<C: SpecTrait> { + _assoc: C::Assoc, +} + +fn instantiate<C: SpecTrait>() -> AssocWrap<C> { + loop {} +} + +pub fn main() { + instantiate::<()>(); +} diff --git a/tests/ui/stable-mir-print/basic_function.rs b/tests/ui/stable-mir-print/basic_function.rs index 9b27a56dab1..deefef63bdb 100644 --- a/tests/ui/stable-mir-print/basic_function.rs +++ b/tests/ui/stable-mir-print/basic_function.rs @@ -2,7 +2,7 @@ //@ check-pass //@ only-x86_64 -fn foo(i:i32) -> i32 { +fn foo(i: i32) -> i32 { i + 1 } @@ -12,4 +12,13 @@ fn bar(vec: &mut Vec<i32>) -> Vec<i32> { new_vec } -fn main(){} +pub fn demux(input: u8) -> u8 { + match input { + 0 => 10, + 1 => 6, + 2 => 8, + _ => 0, + } +} + +fn main() {} diff --git a/tests/ui/stable-mir-print/basic_function.stdout b/tests/ui/stable-mir-print/basic_function.stdout index d9b33a4257c..3926c1048f5 100644 --- a/tests/ui/stable-mir-print/basic_function.stdout +++ b/tests/ui/stable-mir-print/basic_function.stdout @@ -1,234 +1,74 @@ // WARNING: This is highly experimental output it's intended for stable-mir developers only. // If you find a bug or want to improve the output open a issue at https://github.com/rust-lang/project-stable-mir. -fn foo(_0: i32) -> i32 { - let mut _0: (i32, bool); +fn foo(_1: i32) -> i32 { + let mut _0: i32; + let mut _2: (i32, bool); + debug i => _1; + bb0: { + _2 = CheckedAdd(_1, 1_i32); + assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", _1, 1_i32) -> [success: bb1, unwind continue]; + } + bb1: { + _0 = move (_2.0: i32); + return; + } } +fn bar(_1: &mut Vec<i32>) -> Vec<i32> { + let mut _0: Vec<i32>; + let mut _2: Vec<i32>; + let mut _3: &Vec<i32>; + let _4: (); + let mut _5: &mut Vec<i32>; + debug vec => _1; + debug new_vec => _2; bb0: { - _2 = 1 Add const 1_i32 - assert(!move _2 bool),"attempt to compute `{} + {}`, which would overflow", 1, const 1_i32) -> [success: bb1, unwind continue] + _3 = &(*_1); + _2 = <Vec<i32> as Clone>::clone(move _3) -> [return: bb1, unwind continue]; } bb1: { - _0 = move _2 - return + _5 = &mut _2; + _4 = Vec::<i32>::push(move _5, 1_i32) -> [return: bb2, unwind: bb3]; + } + bb2: { + _0 = move _2; + return; + } + bb3: { + drop(_2) -> [return: bb4, unwind terminate]; + } + bb4: { + resume; } -fn bar(_0: &mut Ty { - id: 10, - kind: RigidTy( - Adt( - AdtDef( - DefId { - id: 3, - name: "std::vec::Vec", - }, - ), - GenericArgs( - [ - Type( - Ty { - id: 11, - kind: Param( - ParamTy { - index: 0, - name: "T", - }, - ), - }, - ), - Type( - Ty { - id: 12, - kind: Param( - ParamTy { - index: 1, - name: "A", - }, - ), - }, - ), - ], - ), - ), - ), -}) -> Ty { - id: 10, - kind: RigidTy( - Adt( - AdtDef( - DefId { - id: 3, - name: "std::vec::Vec", - }, - ), - GenericArgs( - [ - Type( - Ty { - id: 11, - kind: Param( - ParamTy { - index: 0, - name: "T", - }, - ), - }, - ), - Type( - Ty { - id: 12, - kind: Param( - ParamTy { - index: 1, - name: "A", - }, - ), - }, - ), - ], - ), - ), - ), -} { - let mut _0: Ty { - id: 10, - kind: RigidTy( - Adt( - AdtDef( - DefId { - id: 3, - name: "std::vec::Vec", - }, - ), - GenericArgs( - [ - Type( - Ty { - id: 11, - kind: Param( - ParamTy { - index: 0, - name: "T", - }, - ), - }, - ), - Type( - Ty { - id: 12, - kind: Param( - ParamTy { - index: 1, - name: "A", - }, - ), - }, - ), - ], - ), - ), - ), -}; - let mut _1: &Ty { - id: 10, - kind: RigidTy( - Adt( - AdtDef( - DefId { - id: 3, - name: "std::vec::Vec", - }, - ), - GenericArgs( - [ - Type( - Ty { - id: 11, - kind: Param( - ParamTy { - index: 0, - name: "T", - }, - ), - }, - ), - Type( - Ty { - id: 12, - kind: Param( - ParamTy { - index: 1, - name: "A", - }, - ), - }, - ), - ], - ), - ), - ), -}; - let _2: (); - let mut _3: &mut Ty { - id: 10, - kind: RigidTy( - Adt( - AdtDef( - DefId { - id: 3, - name: "std::vec::Vec", - }, - ), - GenericArgs( - [ - Type( - Ty { - id: 11, - kind: Param( - ParamTy { - index: 0, - name: "T", - }, - ), - }, - ), - Type( - Ty { - id: 12, - kind: Param( - ParamTy { - index: 1, - name: "A", - }, - ), - }, - ), - ], - ), - ), - ), -}; } +fn demux(_1: u8) -> u8 { + let mut _0: u8; + debug input => _1; bb0: { - _3 = refShared1 - _2 = const <Vec<i32> as Clone>::clone(move _3) -> [return: bb1, unwind continue] + switchInt(_1) -> [0: bb2, 1: bb3, 2: bb4, otherwise: bb1]; } bb1: { - _5 = refMut { - kind: TwoPhaseBorrow, -}2 - _4 = const Vec::<i32>::push(move _5, const 1_i32) -> [return: bb2, unwind: bb3] + _0 = 0_u8; + goto -> bb5; } bb2: { - _0 = move _2 - return + _0 = 10_u8; + goto -> bb5; } bb3: { - drop(_2) -> [return: bb4, unwind terminate] + _0 = 6_u8; + goto -> bb5; } bb4: { - resume + _0 = 8_u8; + goto -> bb5; + } + bb5: { + return; } -fn main() -> () { } +fn main() -> () { + let mut _0: (); bb0: { - return + return; } +} diff --git a/tests/ui/str/str-as-char.stderr b/tests/ui/str/str-as-char.stderr index 44ec079e929..0638d371c17 100644 --- a/tests/ui/str/str-as-char.stderr +++ b/tests/ui/str/str-as-char.stderr @@ -4,10 +4,10 @@ error: character literal may only contain one codepoint LL | println!('●●'); | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | println!("●●"); - | ~~~~ + | ~ ~ error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr b/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr index afbb9c32d51..2c4be26a82b 100644 --- a/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr +++ b/tests/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr @@ -10,6 +10,7 @@ LL | t.clone() | = note: expected type parameter `_` found reference `&_` + = note: the caller chooses a type for `T` which can be different from `&T` note: `T` does not implement `Clone`, so `&T` was cloned instead --> $DIR/clone-on-unconstrained-borrowed-type-param.rs:3:5 | diff --git a/tests/ui/suggestions/issue-85347.rs b/tests/ui/suggestions/issue-85347.rs index 04d4c47d8e5..d14cf07d915 100644 --- a/tests/ui/suggestions/issue-85347.rs +++ b/tests/ui/suggestions/issue-85347.rs @@ -4,6 +4,9 @@ trait Foo { //~^ ERROR associated type takes 1 lifetime argument but 0 lifetime arguments were supplied //~| ERROR associated type bindings are not allowed here //~| HELP add missing + //~| ERROR associated type takes 1 lifetime argument but 0 lifetime arguments were supplied + //~| ERROR associated type bindings are not allowed here + //~| HELP add missing } fn main() {} diff --git a/tests/ui/suggestions/issue-85347.stderr b/tests/ui/suggestions/issue-85347.stderr index f330b3c1fad..45f87e539b4 100644 --- a/tests/ui/suggestions/issue-85347.stderr +++ b/tests/ui/suggestions/issue-85347.stderr @@ -20,7 +20,32 @@ error[E0229]: associated type bindings are not allowed here LL | type Bar<'a>: Deref<Target = <Self>::Bar<Target = Self>>; | ^^^^^^^^^^^^^ associated type not allowed here -error: aborting due to 2 previous errors +error[E0107]: associated type takes 1 lifetime argument but 0 lifetime arguments were supplied + --> $DIR/issue-85347.rs:3:42 + | +LL | type Bar<'a>: Deref<Target = <Self>::Bar<Target = Self>>; + | ^^^ expected 1 lifetime argument + | +note: associated type defined here, with 1 lifetime parameter: `'a` + --> $DIR/issue-85347.rs:3:10 + | +LL | type Bar<'a>: Deref<Target = <Self>::Bar<Target = Self>>; + | ^^^ -- + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: add missing lifetime argument + | +LL | type Bar<'a>: Deref<Target = <Self>::Bar<'a, Target = Self>>; + | +++ + +error[E0229]: associated type bindings are not allowed here + --> $DIR/issue-85347.rs:3:46 + | +LL | type Bar<'a>: Deref<Target = <Self>::Bar<Target = Self>>; + | ^^^^^^^^^^^^^ associated type not allowed here + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0107, E0229. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/suggestions/issue-86667.rs b/tests/ui/suggestions/issue-86667.rs index 8c18a879238..1f37e9a5f6d 100644 --- a/tests/ui/suggestions/issue-86667.rs +++ b/tests/ui/suggestions/issue-86667.rs @@ -6,13 +6,11 @@ async fn a(s1: &str, s2: &str) -> &str { //~^ ERROR: missing lifetime specifier [E0106] s1 - //~^ ERROR: lifetime may not live long enough } fn b(s1: &str, s2: &str) -> &str { //~^ ERROR: missing lifetime specifier [E0106] s1 - //~^ ERROR lifetime may not live long enough } fn main() {} diff --git a/tests/ui/suggestions/issue-86667.stderr b/tests/ui/suggestions/issue-86667.stderr index e3d673ff233..14dbbfffb0e 100644 --- a/tests/ui/suggestions/issue-86667.stderr +++ b/tests/ui/suggestions/issue-86667.stderr @@ -11,7 +11,7 @@ LL | async fn a<'a>(s1: &'a str, s2: &'a str) -> &'a str { | ++++ ++ ++ ++ error[E0106]: missing lifetime specifier - --> $DIR/issue-86667.rs:12:29 + --> $DIR/issue-86667.rs:11:29 | LL | fn b(s1: &str, s2: &str) -> &str { | ---- ---- ^ expected named lifetime parameter @@ -22,24 +22,6 @@ help: consider introducing a named lifetime parameter LL | fn b<'a>(s1: &'a str, s2: &'a str) -> &'a str { | ++++ ++ ++ ++ -error: lifetime may not live long enough - --> $DIR/issue-86667.rs:8:5 - | -LL | async fn a(s1: &str, s2: &str) -> &str { - | - let's call the lifetime of this reference `'1` -LL | -LL | s1 - | ^^ returning this value requires that `'1` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/issue-86667.rs:14:5 - | -LL | fn b(s1: &str, s2: &str) -> &str { - | - let's call the lifetime of this reference `'1` -LL | -LL | s1 - | ^^ returning this value requires that `'1` must outlive `'static` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0106`. diff --git a/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs new file mode 100644 index 00000000000..34afd363129 --- /dev/null +++ b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs @@ -0,0 +1,12 @@ +fn main() {} + +fn foo(_src: &crate::Foo) -> Option<i32> { + todo!() +} +fn bar(src: &crate::Foo) -> impl Iterator<Item = i32> { + [0].into_iter() + //~^ ERROR hidden type for `impl Iterator<Item = i32>` captures lifetime that does not appear in bounds + .filter_map(|_| foo(src)) +} + +struct Foo<'a>(&'a str); diff --git a/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr new file mode 100644 index 00000000000..aeeec3aca34 --- /dev/null +++ b/tests/ui/suggestions/lifetimes/explicit-lifetime-suggestion-in-proper-span-issue-121267.stderr @@ -0,0 +1,20 @@ +error[E0700]: hidden type for `impl Iterator<Item = i32>` captures lifetime that does not appear in bounds + --> $DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:7:5 + | +LL | fn bar(src: &crate::Foo) -> impl Iterator<Item = i32> { + | ---------- ------------------------- opaque type defined here + | | + | hidden type `FilterMap<std::slice::Iter<'static, i32>, {closure@$DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:9:21: 9:24}>` captures the anonymous lifetime defined here +LL | / [0].into_iter() +LL | | +LL | | .filter_map(|_| foo(src)) + | |_________________________________^ + | +help: to declare that `impl Iterator<Item = i32>` captures `'_`, you can introduce a named lifetime parameter `'a` + | +LL | fn bar<'a>(src: &'a crate::Foo<'a>) -> impl Iterator<Item = i32> + 'a { + | ++++ ++ ++++ ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/suggestions/missing-lifetime-specifier.rs b/tests/ui/suggestions/missing-lifetime-specifier.rs index 3fa7f75f862..cd7fa0c1d85 100644 --- a/tests/ui/suggestions/missing-lifetime-specifier.rs +++ b/tests/ui/suggestions/missing-lifetime-specifier.rs @@ -20,31 +20,21 @@ pub union Qux<'t, 'k, I> { trait Tar<'t, 'k, I> {} thread_local! { - //~^ ERROR lifetime may not live long enough - //~| ERROR lifetime may not live long enough static a: RefCell<HashMap<i32, Vec<Vec<Foo>>>> = RefCell::new(HashMap::new()); //~^ ERROR missing lifetime specifiers //~| ERROR missing lifetime specifiers } thread_local! { - //~^ ERROR lifetime may not live long enough - //~| ERROR lifetime may not live long enough - //~| ERROR lifetime may not live long enough static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new()); //~^ ERROR missing lifetime specifiers //~| ERROR missing lifetime specifiers } thread_local! { - //~^ ERROR lifetime may not live long enough - //~| ERROR lifetime may not live long enough static c: RefCell<HashMap<i32, Vec<Vec<Qux<i32>>>>> = RefCell::new(HashMap::new()); //~^ ERROR missing lifetime specifiers //~| ERROR missing lifetime specifiers } thread_local! { - //~^ ERROR lifetime may not live long enough - //~| ERROR lifetime may not live long enough - //~| ERROR lifetime may not live long enough static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new()); //~^ ERROR missing lifetime specifiers //~| ERROR missing lifetime specifiers diff --git a/tests/ui/suggestions/missing-lifetime-specifier.stderr b/tests/ui/suggestions/missing-lifetime-specifier.stderr index 62eca162148..2b85cfde7b6 100644 --- a/tests/ui/suggestions/missing-lifetime-specifier.stderr +++ b/tests/ui/suggestions/missing-lifetime-specifier.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:25:44 + --> $DIR/missing-lifetime-specifier.rs:23:44 | LL | static a: RefCell<HashMap<i32, Vec<Vec<Foo>>>> = RefCell::new(HashMap::new()); | ^^^ expected 2 lifetime parameters @@ -11,11 +11,9 @@ LL | static a: RefCell<HashMap<i32, Vec<Vec<Foo<'static, 'static>>>>> = RefC | ++++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:25:44 + --> $DIR/missing-lifetime-specifier.rs:23:44 | LL | / thread_local! { -LL | | -LL | | LL | | static a: RefCell<HashMap<i32, Vec<Vec<Foo>>>> = RefCell::new(HashMap::new()); | | ^^^ expected 2 lifetime parameters LL | | @@ -26,7 +24,7 @@ LL | | } = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:33:44 + --> $DIR/missing-lifetime-specifier.rs:28:44 | LL | static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new()); | ^^^^ expected 2 lifetime parameters @@ -40,12 +38,9 @@ LL | static b: RefCell<HashMap<i32, Vec<Vec<&'static Bar<'static, 'static>>> | +++++++ ++++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:33:44 + --> $DIR/missing-lifetime-specifier.rs:28:44 | LL | / thread_local! { -LL | | -LL | | -LL | | LL | | static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new()); | | ^^^^ expected 2 lifetime parameters | | | @@ -58,7 +53,7 @@ LL | | } = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 4 lifetimes it is borrowed from error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:40:47 + --> $DIR/missing-lifetime-specifier.rs:33:47 | LL | static c: RefCell<HashMap<i32, Vec<Vec<Qux<i32>>>>> = RefCell::new(HashMap::new()); | ^ expected 2 lifetime parameters @@ -70,11 +65,9 @@ LL | static c: RefCell<HashMap<i32, Vec<Vec<Qux<'static, 'static, i32>>>>> = | +++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:40:47 + --> $DIR/missing-lifetime-specifier.rs:33:47 | LL | / thread_local! { -LL | | -LL | | LL | | static c: RefCell<HashMap<i32, Vec<Vec<Qux<i32>>>>> = RefCell::new(HashMap::new()); | | ^ expected 2 lifetime parameters LL | | @@ -85,7 +78,7 @@ LL | | } = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:48:44 + --> $DIR/missing-lifetime-specifier.rs:38:44 | LL | static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new()); | ^ ^ expected 2 lifetime parameters @@ -99,12 +92,9 @@ LL | static d: RefCell<HashMap<i32, Vec<Vec<&'static Tar<'static, 'static, i | +++++++ +++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:48:44 + --> $DIR/missing-lifetime-specifier.rs:38:44 | LL | / thread_local! { -LL | | -LL | | -LL | | LL | | static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new()); | | ^ ^ expected 2 lifetime parameters | | | @@ -117,7 +107,7 @@ LL | | } = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 4 lifetimes it is borrowed from error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:58:44 + --> $DIR/missing-lifetime-specifier.rs:48:44 | LL | static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); | ^ expected named lifetime parameter @@ -129,7 +119,7 @@ LL | static f: RefCell<HashMap<i32, Vec<Vec<&'static Tar<'static, i32>>>>> = | +++++++ error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:58:44 + --> $DIR/missing-lifetime-specifier.rs:48:44 | LL | / thread_local! { LL | | static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); @@ -143,7 +133,7 @@ LL | | } = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from error[E0107]: union takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:54:44 + --> $DIR/missing-lifetime-specifier.rs:44:44 | LL | static e: RefCell<HashMap<i32, Vec<Vec<Qux<'static, i32>>>>> = RefCell::new(HashMap::new()); | ^^^ ------- supplied 1 lifetime argument @@ -161,7 +151,7 @@ LL | static e: RefCell<HashMap<i32, Vec<Vec<Qux<'static, 'static, i32>>>>> = | +++++++++ error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:58:45 + --> $DIR/missing-lifetime-specifier.rs:48:45 | LL | static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, i32>>>>> = RefCell::new(HashMap::new()); | ^^^ ------- supplied 1 lifetime argument @@ -178,199 +168,7 @@ help: add missing lifetime argument LL | static f: RefCell<HashMap<i32, Vec<Vec<&Tar<'static, 'static, i32>>>>> = RefCell::new(HashMap::new()); | +++++++++ -error: lifetime may not live long enough - --> $DIR/missing-lifetime-specifier.rs:22:1 - | -LL | / thread_local! { -LL | | -LL | | -LL | | static a: RefCell<HashMap<i32, Vec<Vec<Foo>>>> = RefCell::new(HashMap::new()); -LL | | -LL | | -LL | | } - | | ^ - | | | - | |_has type `Option<&mut Option<RefCell<HashMap<i32, Vec<Vec<Foo<'1, '_>>>>>>>` - | returning this value requires that `'1` must outlive `'static` - | - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: lifetime may not live long enough - --> $DIR/missing-lifetime-specifier.rs:22:1 - | -LL | / thread_local! { -LL | | -LL | | -LL | | static a: RefCell<HashMap<i32, Vec<Vec<Foo>>>> = RefCell::new(HashMap::new()); -LL | | -LL | | -LL | | } - | | ^ - | | | - | |_has type `Option<&mut Option<RefCell<HashMap<i32, Vec<Vec<Foo<'_, '2>>>>>>>` - | returning this value requires that `'2` must outlive `'static` - | - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: lifetime may not live long enough - --> $DIR/missing-lifetime-specifier.rs:29:1 - | -LL | / thread_local! { -LL | | -LL | | -LL | | -LL | | static b: RefCell<HashMap<i32, Vec<Vec<&Bar>>>> = RefCell::new(HashMap::new()); - | | - let's call the lifetime of this reference `'1` -LL | | -LL | | -LL | | } - | |_^ returning this value requires that `'1` must outlive `'static` - | - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) -help: to declare that the trait object captures data from argument `init`, you can add an explicit `'_` lifetime bound - | -LL | static b: RefCell<HashMap<i32, Vec<Vec<&Bar + '_>>>> = RefCell::new(HashMap::new()); - | ++++ - -error: lifetime may not live long enough - --> $DIR/missing-lifetime-specifier.rs:29:1 - | -LL | / thread_local! { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | | ^ - | | | - | |_has type `Option<&mut Option<RefCell<HashMap<i32, Vec<Vec<&dyn Bar<'2, '_>>>>>>>` - | returning this value requires that `'2` must outlive `'static` - | - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) -help: to declare that the trait object captures data from argument `init`, you can add an explicit `'_` lifetime bound - | -LL | static b: RefCell<HashMap<i32, Vec<Vec<&Bar + '_>>>> = RefCell::new(HashMap::new()); - | ++++ - -error: lifetime may not live long enough - --> $DIR/missing-lifetime-specifier.rs:29:1 - | -LL | / thread_local! { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | | ^ - | | | - | |_has type `Option<&mut Option<RefCell<HashMap<i32, Vec<Vec<&dyn Bar<'_, '3>>>>>>>` - | returning this value requires that `'3` must outlive `'static` - | - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) -help: to declare that the trait object captures data from argument `init`, you can add an explicit `'_` lifetime bound - | -LL | static b: RefCell<HashMap<i32, Vec<Vec<&Bar + '_>>>> = RefCell::new(HashMap::new()); - | ++++ - -error: lifetime may not live long enough - --> $DIR/missing-lifetime-specifier.rs:37:1 - | -LL | / thread_local! { -LL | | -LL | | -LL | | static c: RefCell<HashMap<i32, Vec<Vec<Qux<i32>>>>> = RefCell::new(HashMap::new()); -LL | | -LL | | -LL | | } - | | ^ - | | | - | |_has type `Option<&mut Option<RefCell<HashMap<i32, Vec<Vec<Qux<'1, '_, i32>>>>>>>` - | returning this value requires that `'1` must outlive `'static` - | - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: lifetime may not live long enough - --> $DIR/missing-lifetime-specifier.rs:37:1 - | -LL | / thread_local! { -LL | | -LL | | -LL | | static c: RefCell<HashMap<i32, Vec<Vec<Qux<i32>>>>> = RefCell::new(HashMap::new()); -LL | | -LL | | -LL | | } - | | ^ - | | | - | |_has type `Option<&mut Option<RefCell<HashMap<i32, Vec<Vec<Qux<'_, '2, i32>>>>>>>` - | returning this value requires that `'2` must outlive `'static` - | - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: lifetime may not live long enough - --> $DIR/missing-lifetime-specifier.rs:44:1 - | -LL | / thread_local! { -LL | | -LL | | -LL | | -LL | | static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32>>>>> = RefCell::new(HashMap::new()); - | | - let's call the lifetime of this reference `'1` -LL | | -LL | | -LL | | } - | |_^ returning this value requires that `'1` must outlive `'static` - | - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) -help: to declare that the trait object captures data from argument `init`, you can add an explicit `'_` lifetime bound - | -LL | static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32> + '_>>>> = RefCell::new(HashMap::new()); - | ++++ - -error: lifetime may not live long enough - --> $DIR/missing-lifetime-specifier.rs:44:1 - | -LL | / thread_local! { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | | ^ - | | | - | |_has type `Option<&mut Option<RefCell<HashMap<i32, Vec<Vec<&dyn Tar<'2, '_, i32>>>>>>>` - | returning this value requires that `'2` must outlive `'static` - | - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) -help: to declare that the trait object captures data from argument `init`, you can add an explicit `'_` lifetime bound - | -LL | static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32> + '_>>>> = RefCell::new(HashMap::new()); - | ++++ - -error: lifetime may not live long enough - --> $DIR/missing-lifetime-specifier.rs:44:1 - | -LL | / thread_local! { -LL | | -LL | | -LL | | -... | -LL | | -LL | | } - | | ^ - | | | - | |_has type `Option<&mut Option<RefCell<HashMap<i32, Vec<Vec<&dyn Tar<'_, '3, i32>>>>>>>` - | returning this value requires that `'3` must outlive `'static` - | - = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info) -help: to declare that the trait object captures data from argument `init`, you can add an explicit `'_` lifetime bound - | -LL | static d: RefCell<HashMap<i32, Vec<Vec<&Tar<i32> + '_>>>> = RefCell::new(HashMap::new()); - | ++++ - -error: aborting due to 22 previous errors +error: aborting due to 12 previous errors Some errors have detailed explanations: E0106, E0107. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/suggestions/types/into-inference-needs-type.rs b/tests/ui/suggestions/types/into-inference-needs-type.rs new file mode 100644 index 00000000000..4c8b6ec2113 --- /dev/null +++ b/tests/ui/suggestions/types/into-inference-needs-type.rs @@ -0,0 +1,15 @@ +#[derive(Debug)] +enum MyError { + MainError +} + +fn main() -> Result<(), MyError> { + let vec = vec!["one", "two", "three"]; + let list = vec + .iter() + .map(|s| s.strip_prefix("t")) + .filter_map(Option::Some) + .into()?; //~ ERROR type annotations needed + + return Ok(()); +} diff --git a/tests/ui/suggestions/types/into-inference-needs-type.stderr b/tests/ui/suggestions/types/into-inference-needs-type.stderr new file mode 100644 index 00000000000..dd688f90289 --- /dev/null +++ b/tests/ui/suggestions/types/into-inference-needs-type.stderr @@ -0,0 +1,19 @@ +error[E0283]: type annotations needed + --> $DIR/into-inference-needs-type.rs:12:10 + | +LL | .into()?; + | ^^^^ + | + = note: cannot satisfy `_: From<FilterMap<Map<std::slice::Iter<'_, &str>, {closure@$DIR/into-inference-needs-type.rs:10:14: 10:17}>, fn(Option<&str>) -> Option<Option<&str>> {Option::<Option<&str>>::Some}>>` + = note: required for `FilterMap<Map<std::slice::Iter<'_, &str>, {closure@$DIR/into-inference-needs-type.rs:10:14: 10:17}>, fn(Option<&str>) -> Option<Option<&str>> {Option::<Option<&str>>::Some}>` to implement `Into<_>` +help: try using a fully qualified path to specify the expected types + | +LL ~ let list = <FilterMap<Map<std::slice::Iter<'_, &str>, _>, _> as Into<T>>::into(vec +LL | .iter() +LL | .map(|s| s.strip_prefix("t")) +LL ~ .filter_map(Option::Some))?; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/trait-bounds/restrict-assoc-type-of-generic-bound.stderr b/tests/ui/trait-bounds/restrict-assoc-type-of-generic-bound.stderr index 5024ad21892..7aa32557af2 100644 --- a/tests/ui/trait-bounds/restrict-assoc-type-of-generic-bound.stderr +++ b/tests/ui/trait-bounds/restrict-assoc-type-of-generic-bound.stderr @@ -10,6 +10,7 @@ LL | return a.bar(); | = note: expected type parameter `B` found associated type `<A as MyTrait>::T` + = note: the caller chooses a type for `B` which can be different from `<A as MyTrait>::T` help: consider further restricting this bound | LL | pub fn foo<A: MyTrait<T = B>, B>(a: A) -> B { diff --git a/tests/ui/traits/alias/self-in-generics.rs b/tests/ui/traits/alias/self-in-generics.rs index dcb33b7a90a..433b741532d 100644 --- a/tests/ui/traits/alias/self-in-generics.rs +++ b/tests/ui/traits/alias/self-in-generics.rs @@ -1,4 +1,4 @@ -// astconv uses `FreshTy(0)` as a dummy `Self` type when instanciating trait objects. +// HIR ty lowering uses `FreshTy(0)` as a dummy `Self` type when instanciating trait objects. // This `FreshTy(0)` can leak into substs, causing ICEs in several places. #![feature(trait_alias)] diff --git a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs index 7d71fcdd158..cab484a120c 100644 --- a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs +++ b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.rs @@ -20,6 +20,7 @@ impl<T, S> Trait<T, S> for () {} fn func<T: Trait<u32, String>>(t: T) -> impl Trait<(), i32> { //~^ ERROR trait takes 1 generic argument but 2 generic arguments were supplied //~| ERROR trait takes 1 generic argument but 2 generic arguments were supplied +//~| ERROR trait takes 1 generic argument but 2 generic arguments were supplied //~| ERROR type annotations needed 3 } diff --git a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr index f4ede4190fc..99e81a9039e 100644 --- a/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr +++ b/tests/ui/traits/associated_type_bound/116464-invalid-assoc-type-suggestion-in-trait-impl.stderr @@ -54,6 +54,23 @@ help: replace the generic bound with the associated type LL | fn func<T: Trait<u32, String>>(t: T) -> impl Trait<(), Assoc = i32> { | +++++++ +error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:20:46 + | +LL | fn func<T: Trait<u32, String>>(t: T) -> impl Trait<(), i32> { + | ^^^^^ expected 1 generic argument + | +note: trait defined here, with 1 generic parameter: `T` + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:5:11 + | +LL | pub trait Trait<T> { + | ^^^^^ - + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: replace the generic bound with the associated type + | +LL | fn func<T: Trait<u32, String>>(t: T) -> impl Trait<(), Assoc = i32> { + | +++++++ + error[E0282]: type annotations needed --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:20:41 | @@ -61,7 +78,7 @@ LL | fn func<T: Trait<u32, String>>(t: T) -> impl Trait<(), i32> { | ^^^^^^^^^^^^^^^^^^^ cannot infer type error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:27:18 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:28:18 | LL | struct Struct<T: Trait<u32, String>> { | ^^^^^ expected 1 generic argument @@ -77,7 +94,7 @@ LL | struct Struct<T: Trait<u32, Assoc = String>> { | +++++++ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:32:23 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:33:23 | LL | trait AnotherTrait<T: Trait<T, i32>> {} | ^^^^^ expected 1 generic argument @@ -93,7 +110,7 @@ LL | trait AnotherTrait<T: Trait<T, Assoc = i32>> {} | +++++++ error[E0107]: trait takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:35:9 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:36:9 | LL | impl<T: Trait<u32, String>> Struct<T> {} | ^^^^^ expected 1 generic argument @@ -109,7 +126,7 @@ LL | impl<T: Trait<u32, Assoc = String>> Struct<T> {} | +++++++ error[E0107]: struct takes 1 generic argument but 2 generic arguments were supplied - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:41:58 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:42:58 | LL | impl<T: Trait<u32, Assoc=String>, U> YetAnotherTrait for Struct<T, U> {} | ^^^^^^ - help: remove this generic argument @@ -117,12 +134,12 @@ LL | impl<T: Trait<u32, Assoc=String>, U> YetAnotherTrait for Struct<T, U> {} | expected 1 generic argument | note: struct defined here, with 1 generic parameter: `T` - --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:27:8 + --> $DIR/116464-invalid-assoc-type-suggestion-in-trait-impl.rs:28:8 | LL | struct Struct<T: Trait<u32, String>> { | ^^^^^^ - -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors Some errors have detailed explanations: E0107, E0207, E0282. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/traits/impl-of-supertrait-has-wrong-lifetime-parameters.stderr b/tests/ui/traits/impl-of-supertrait-has-wrong-lifetime-parameters.stderr index 092776edea7..8bf8536c74e 100644 --- a/tests/ui/traits/impl-of-supertrait-has-wrong-lifetime-parameters.stderr +++ b/tests/ui/traits/impl-of-supertrait-has-wrong-lifetime-parameters.stderr @@ -4,16 +4,16 @@ error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` d LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> { | ^^^^^^^^^ | -note: first, the lifetime cannot outlive the lifetime `'a` as defined here... - --> $DIR/impl-of-supertrait-has-wrong-lifetime-parameters.rs:24:6 - | -LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> { - | ^^ -note: ...but the lifetime must also be valid for the lifetime `'b` as defined here... +note: first, the lifetime cannot outlive the lifetime `'b` as defined here... --> $DIR/impl-of-supertrait-has-wrong-lifetime-parameters.rs:24:9 | LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> { | ^^ +note: ...but the lifetime must also be valid for the lifetime `'a` as defined here... + --> $DIR/impl-of-supertrait-has-wrong-lifetime-parameters.rs:24:6 + | +LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> { + | ^^ note: ...so that the types are compatible --> $DIR/impl-of-supertrait-has-wrong-lifetime-parameters.rs:24:28 | diff --git a/tests/ui/traits/matching-lifetimes.stderr b/tests/ui/traits/matching-lifetimes.stderr index f8119ed415d..8a802d57f5c 100644 --- a/tests/ui/traits/matching-lifetimes.stderr +++ b/tests/ui/traits/matching-lifetimes.stderr @@ -6,16 +6,16 @@ LL | fn foo(x: Foo<'b,'a>) { | = note: expected signature `fn(Foo<'a, 'b>)` found signature `fn(Foo<'b, 'a>)` -note: the lifetime `'b` as defined here... - --> $DIR/matching-lifetimes.rs:13:9 - | -LL | impl<'a,'b> Tr for Foo<'a,'b> { - | ^^ -note: ...does not necessarily outlive the lifetime `'a` as defined here +note: the lifetime `'a` as defined here... --> $DIR/matching-lifetimes.rs:13:6 | LL | impl<'a,'b> Tr for Foo<'a,'b> { | ^^ +note: ...does not necessarily outlive the lifetime `'b` as defined here + --> $DIR/matching-lifetimes.rs:13:9 + | +LL | impl<'a,'b> Tr for Foo<'a,'b> { + | ^^ error[E0308]: method not compatible with trait --> $DIR/matching-lifetimes.rs:14:5 @@ -25,16 +25,16 @@ LL | fn foo(x: Foo<'b,'a>) { | = note: expected signature `fn(Foo<'a, 'b>)` found signature `fn(Foo<'b, 'a>)` -note: the lifetime `'a` as defined here... - --> $DIR/matching-lifetimes.rs:13:6 - | -LL | impl<'a,'b> Tr for Foo<'a,'b> { - | ^^ -note: ...does not necessarily outlive the lifetime `'b` as defined here +note: the lifetime `'b` as defined here... --> $DIR/matching-lifetimes.rs:13:9 | LL | impl<'a,'b> Tr for Foo<'a,'b> { | ^^ +note: ...does not necessarily outlive the lifetime `'a` as defined here + --> $DIR/matching-lifetimes.rs:13:6 + | +LL | impl<'a,'b> Tr for Foo<'a,'b> { + | ^^ error: aborting due to 2 previous errors diff --git a/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs b/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs new file mode 100644 index 00000000000..57f814bc81e --- /dev/null +++ b/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs @@ -0,0 +1,28 @@ +//@ compile-flags: -Znext-solver + +trait Tr<'a> {} + +// Fulfillment in the new solver relies on an invariant to hold: Either +// `has_changed` is true, or computing a goal's certainty is idempotent. +// This isn't true for `ReError`, which we used to pass through in the +// canonicalizer even on input mode, which can cause a goal to go from +// ambig => pass, but we don't consider `has_changed` when the response +// only contains region constraints (since we usually uniquify regions). +// +// In this test: +// Implicit negative coherence tries to prove `W<?0>: Constrain<'?1>`, +// which will then match with the impl below. This constrains `'?1` to +// `ReError`, but still bails w/ ambiguity bc we can't prove `?0: Sized`. +// Then, when we recompute the goal `W<?0>: Constrain<'error>`, when +// collecting ambiguities and overflows, we end up assembling a default +// error candidate w/o ambiguity, which causes the goal to pass, and ICE. +impl<'a, A: ?Sized> Tr<'a> for W<A> {} +struct W<A: ?Sized>(A); +impl<'a, A: ?Sized> Tr<'a> for A where A: Constrain<'a> {} +//~^ ERROR conflicting implementations of trait `Tr<'_>` for type `W<_>` + +trait Constrain<'a> {} +impl<A: Sized> Constrain<'missing> for W<A> {} +//~^ ERROR use of undeclared lifetime name `'missing` + +fn main() {} diff --git a/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr b/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr new file mode 100644 index 00000000000..cf85c52fb42 --- /dev/null +++ b/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr @@ -0,0 +1,21 @@ +error[E0261]: use of undeclared lifetime name `'missing` + --> $DIR/dont-canonicalize-re-error.rs:25:26 + | +LL | impl<A: Sized> Constrain<'missing> for W<A> {} + | - ^^^^^^^^ undeclared lifetime + | | + | help: consider introducing lifetime `'missing` here: `'missing,` + +error[E0119]: conflicting implementations of trait `Tr<'_>` for type `W<_>` + --> $DIR/dont-canonicalize-re-error.rs:21:1 + | +LL | impl<'a, A: ?Sized> Tr<'a> for W<A> {} + | ----------------------------------- first implementation here +LL | struct W<A: ?Sized>(A); +LL | impl<'a, A: ?Sized> Tr<'a> for A where A: Constrain<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<_>` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0119, E0261. +For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs new file mode 100644 index 00000000000..3af299e5b11 --- /dev/null +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs @@ -0,0 +1,15 @@ +trait FromResidual<R = <Self as Try>::Residual> { + fn from_residual(residual: R) -> Self; +} + +trait Try { + type Residual; +} + +fn w<'a, T: 'a, F: Fn(&'a T)>() { + let b: &dyn FromResidual = &(); + //~^ ERROR: the trait `FromResidual` cannot be made into an object + //~| ERROR: the trait `FromResidual` cannot be made into an object +} + +fn main() {} diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr new file mode 100644 index 00000000000..d5e9b1be63b --- /dev/null +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr @@ -0,0 +1,33 @@ +error[E0038]: the trait `FromResidual` cannot be made into an object + --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:17 + | +LL | let b: &dyn FromResidual = &(); + | ^^^^^^^^^^^^ + | + = note: it cannot use `Self` as a type parameter in a supertrait or `where`-clause + +error[E0038]: the trait `FromResidual` cannot be made into an object + --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:12 + | +LL | let b: &dyn FromResidual = &(); + | ^^^^^^^^^^^^^^^^^ `FromResidual` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:2:8 + | +LL | trait FromResidual<R = <Self as Try>::Residual> { + | ------------ this trait cannot be made into an object... +LL | fn from_residual(residual: R) -> Self; + | ^^^^^^^^^^^^^ ...because associated function `from_residual` has no `self` parameter +help: consider turning `from_residual` into a method by giving it a `&self` argument + | +LL | fn from_residual(&self, residual: R) -> Self; + | ++++++ +help: alternatively, consider constraining `from_residual` so it does not apply to trait objects + | +LL | fn from_residual(residual: R) -> Self where Self: Sized; + | +++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/span-bug-issue-121414.rs b/tests/ui/traits/span-bug-issue-121414.rs index 6fbe2c179c6..ec38d8c2de6 100644 --- a/tests/ui/traits/span-bug-issue-121414.rs +++ b/tests/ui/traits/span-bug-issue-121414.rs @@ -6,7 +6,8 @@ impl<'a> Bar for Foo<'f> { //~ ERROR undeclared lifetime type Type = u32; } -fn test() //~ ERROR implementation of `Bar` is not general enough +fn test() //~ ERROR the trait bound `for<'a> Foo<'a>: Bar` is not satisfied + //~| ERROR the trait bound `for<'a> Foo<'a>: Bar` is not satisfied where for<'a> <Foo<'a> as Bar>::Type: Sized, { diff --git a/tests/ui/traits/span-bug-issue-121414.stderr b/tests/ui/traits/span-bug-issue-121414.stderr index 3c97f64e781..e2ef6672cd5 100644 --- a/tests/ui/traits/span-bug-issue-121414.stderr +++ b/tests/ui/traits/span-bug-issue-121414.stderr @@ -6,15 +6,22 @@ LL | impl<'a> Bar for Foo<'f> { | | | help: consider introducing lifetime `'f` here: `'f,` -error: implementation of `Bar` is not general enough +error[E0277]: the trait bound `for<'a> Foo<'a>: Bar` is not satisfied + --> $DIR/span-bug-issue-121414.rs:9:1 + | +LL | / fn test() +LL | | +LL | | where +LL | | for<'a> <Foo<'a> as Bar>::Type: Sized, + | |__________________________________________^ the trait `for<'a> Bar` is not implemented for `Foo<'a>` + +error[E0277]: the trait bound `for<'a> Foo<'a>: Bar` is not satisfied --> $DIR/span-bug-issue-121414.rs:9:4 | LL | fn test() - | ^^^^ implementation of `Bar` is not general enough - | - = note: `Bar` would have to be implemented for the type `Foo<'0>`, for any lifetime `'0`... - = note: ...but `Bar` is actually implemented for the type `Foo<'1>`, for some specific lifetime `'1` + | ^^^^ the trait `for<'a> Bar` is not implemented for `Foo<'a>` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0261`. +Some errors have detailed explanations: E0261, E0277. +For more information about an error, try `rustc --explain E0261`. diff --git a/tests/ui/traits/suggest-fully-qualified-closure.rs b/tests/ui/traits/suggest-fully-qualified-closure.rs index f401a3012da..3d28a4e6bf5 100644 --- a/tests/ui/traits/suggest-fully-qualified-closure.rs +++ b/tests/ui/traits/suggest-fully-qualified-closure.rs @@ -1,7 +1,5 @@ //@ check-fail //@ known-bug: #103705 -//@ normalize-stderr-test "\{closure@.*\}" -> "{closure@}" -//@ normalize-stderr-test "\+* ~" -> "+++ ~" // The output of this currently suggests writing a closure in the qualified path. diff --git a/tests/ui/traits/suggest-fully-qualified-closure.stderr b/tests/ui/traits/suggest-fully-qualified-closure.stderr index e077bd7ac2a..a2c1115e673 100644 --- a/tests/ui/traits/suggest-fully-qualified-closure.stderr +++ b/tests/ui/traits/suggest-fully-qualified-closure.stderr @@ -1,11 +1,11 @@ error[E0283]: type annotations needed - --> $DIR/suggest-fully-qualified-closure.rs:23:7 + --> $DIR/suggest-fully-qualified-closure.rs:21:7 | LL | q.lol(||()); | ^^^ | note: multiple `impl`s satisfying `Qqq: MyTrait<_>` found - --> $DIR/suggest-fully-qualified-closure.rs:14:1 + --> $DIR/suggest-fully-qualified-closure.rs:12:1 | LL | impl MyTrait<u32> for Qqq{ | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -14,8 +14,8 @@ LL | impl MyTrait<u64> for Qqq{ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using a fully qualified path to specify the expected types | -LL | <Qqq as MyTrait<T>>::lol::<{closure@}>(&q, ||()); - | +++ ~ +LL | <Qqq as MyTrait<T>>::lol::<_>(&q, ||()); + | +++++++++++++++++++++++++++++++ ~ error: aborting due to 1 previous error diff --git a/tests/ui/transmute/transmute-zst-generics.rs b/tests/ui/transmute/transmute-zst-generics.rs new file mode 100644 index 00000000000..9aeb21923ea --- /dev/null +++ b/tests/ui/transmute/transmute-zst-generics.rs @@ -0,0 +1,34 @@ +//@ run-pass + +// Transmuting to/from ZSTs that contain generics. + +#![feature(transmute_generic_consts)] + +// Verify non-generic ZST -> generic ZST transmute +unsafe fn cast_zst0<T>(from: ()) -> [T; 0] { + ::std::mem::transmute::<(), [T; 0]>(from) +} + +// Verify generic ZST -> non-generic ZST transmute +unsafe fn cast_zst1<T>(from: [T; 0]) -> () { + ::std::mem::transmute::<[T; 0], ()>(from) +} + +// Verify transmute with generic compound types +unsafe fn cast_zst2<T>(from: ()) -> [(T, T); 0] { + ::std::mem::transmute::<(), [(T, T); 0]>(from) +} + +// Verify transmute with ZST propagation through arrays +unsafe fn cast_zst3<T>(from: ()) -> [[T; 0]; 8] { + ::std::mem::transmute::<(), [[T; 0]; 8]>(from) +} + +pub fn main() { + unsafe { + let _: [u32; 0] = cast_zst0(()); + let _ = cast_zst1::<u32>([]); + let _: [(u32, u32); 0] = cast_zst2(()); + let _: [[u32; 0]; 8] = cast_zst3(()); + }; +} diff --git a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs new file mode 100644 index 00000000000..023991c29d0 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs @@ -0,0 +1,36 @@ +// issue: rust-lang/rust#99945 +// ICE Failed to normalize + +#![feature(type_alias_impl_trait)] + +trait Widget<E> { + type State; + + fn make_state(&self) -> Self::State; +} + +impl<E> Widget<E> for () { + type State = (); + + fn make_state(&self) -> Self::State {} +} + +struct StatefulWidget<F>(F); + +type StateWidget<'a> = impl Widget<&'a ()>; + +impl<F: for<'a> Fn(&'a ()) -> StateWidget<'a>> Widget<()> for StatefulWidget<F> { + type State = (); + + fn make_state(&self) -> Self::State {} +} + +fn new_stateful_widget<F: for<'a> Fn(&'a ()) -> StateWidget<'a>>(build: F) -> impl Widget<()> { + StatefulWidget(build) + //~^ ERROR expected generic lifetime parameter, found `'a` +} + +fn main() { + new_stateful_widget(|_| ()).make_state(); + //~^ ERROR mismatched types +} diff --git a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr new file mode 100644 index 00000000000..0c76feae198 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/failed-to-normalize-ice-99945.rs:34:29 + | +LL | type StateWidget<'a> = impl Widget<&'a ()>; + | ------------------- the expected opaque type +... +LL | new_stateful_widget(|_| ()).make_state(); + | ^^ expected opaque type, found `()` + | + = note: expected opaque type `StateWidget<'_>` + found unit type `()` + +error[E0792]: expected generic lifetime parameter, found `'a` + --> $DIR/failed-to-normalize-ice-99945.rs:29:5 + | +LL | type StateWidget<'a> = impl Widget<&'a ()>; + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | StatefulWidget(build) + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0792. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs b/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs new file mode 100644 index 00000000000..ef9fe604ea7 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden4.rs @@ -0,0 +1,25 @@ +//! This test used to ICE because, while an error was emitted, +//! we still tried to remap generic params used in the hidden type +//! to the ones of the opaque type definition. + +//@ edition: 2021 + +#![feature(type_alias_impl_trait)] +use std::future::Future; + +type FutNothing<'a> = impl 'a + Future<Output = ()>; +//~^ ERROR: unconstrained opaque type + +async fn operation(_: &mut ()) -> () { + //~^ ERROR: concrete type differs from previous + call(operation).await +} + +async fn call<F>(_f: F) +where + for<'any> F: FnMut(&'any mut ()) -> FutNothing<'any>, +{ + //~^ ERROR: expected generic lifetime parameter, found `'any` +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr new file mode 100644 index 00000000000..d7a0452727e --- /dev/null +++ b/tests/ui/type-alias-impl-trait/hkl_forbidden4.stderr @@ -0,0 +1,34 @@ +error: unconstrained opaque type + --> $DIR/hkl_forbidden4.rs:10:23 + | +LL | type FutNothing<'a> = impl 'a + Future<Output = ()>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `FutNothing` must be used in combination with a concrete type within the same module + +error: concrete type differs from previous defining opaque type use + --> $DIR/hkl_forbidden4.rs:13:1 + | +LL | async fn operation(_: &mut ()) -> () { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `FutNothing<'_>`, got `{async fn body@$DIR/hkl_forbidden4.rs:13:38: 16:2}` + | +note: previous use here + --> $DIR/hkl_forbidden4.rs:15:5 + | +LL | call(operation).await + | ^^^^^^^^^^^^^^^ + +error[E0792]: expected generic lifetime parameter, found `'any` + --> $DIR/hkl_forbidden4.rs:21:1 + | +LL | type FutNothing<'a> = impl 'a + Future<Output = ()>; + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | / { +LL | | +LL | | } + | |_^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs new file mode 100644 index 00000000000..fc71d0d61ff --- /dev/null +++ b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.rs @@ -0,0 +1,52 @@ +// test for #110696 +// failed to resolve instance for <Scope<()> as MyIndex<()>>::my_index +// ignore-tidy-linelength + +#![feature(type_alias_impl_trait)] + +use std::marker::PhantomData; + + +trait MyIndex<T> { + type O; + fn my_index(self) -> Self::O; +} +trait MyFrom<T>: Sized { + type Error; + fn my_from(value: T) -> Result<Self, Self::Error>; +} + + +trait F {} +impl F for () {} +type DummyT<T> = impl F; +fn _dummy_t<T>() -> DummyT<T> {} + +struct Phantom1<T>(PhantomData<T>); +struct Phantom2<T>(PhantomData<T>); +struct Scope<T>(Phantom2<DummyT<T>>); + +impl<T> Scope<T> { + fn new() -> Self { + unimplemented!() + } +} + +impl<T> MyFrom<Phantom2<T>> for Phantom1<T> { + type Error = (); + fn my_from(_: Phantom2<T>) -> Result<Self, Self::Error> { + unimplemented!() + } +} + +impl<T: MyFrom<Phantom2<DummyT<U>>>, U> MyIndex<DummyT<T>> for Scope<U> { + //~^ ERROR the type parameter `T` is not constrained by the impl + type O = T; + fn my_index(self) -> Self::O { + MyFrom::my_from(self.0).ok().unwrap() + } +} + +fn main() { + let _pos: Phantom1<DummyT<()>> = Scope::new().my_index(); +} diff --git a/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr new file mode 100644 index 00000000000..a39f77ce17f --- /dev/null +++ b/tests/ui/type-alias-impl-trait/ice-failed-to-resolve-instance-for-110696.stderr @@ -0,0 +1,9 @@ +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/ice-failed-to-resolve-instance-for-110696.rs:42:6 + | +LL | impl<T: MyFrom<Phantom2<DummyT<U>>>, U> MyIndex<DummyT<T>> for Scope<U> { + | ^ unconstrained type parameter + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0207`. diff --git a/tests/ui/type-alias-impl-trait/not_well_formed.fixed b/tests/ui/type-alias-impl-trait/not_well_formed.fixed deleted file mode 100644 index bd45d8cddae..00000000000 --- a/tests/ui/type-alias-impl-trait/not_well_formed.fixed +++ /dev/null @@ -1,19 +0,0 @@ -//@ run-rustfix -#![feature(type_alias_impl_trait)] -#![allow(dead_code)] - -fn main() {} - -trait TraitWithAssoc { - type Assoc; -} - -type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>; //~ associated type `Assoc` not found for `V` - -trait Trait<U> {} - -impl<W> Trait<W> for () {} - -fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T> { - () -} diff --git a/tests/ui/type-alias-impl-trait/not_well_formed.rs b/tests/ui/type-alias-impl-trait/not_well_formed.rs index 7b5444ae0d3..0cf8d41c7fd 100644 --- a/tests/ui/type-alias-impl-trait/not_well_formed.rs +++ b/tests/ui/type-alias-impl-trait/not_well_formed.rs @@ -1,4 +1,4 @@ -//@ run-rustfix +// Can't rustfix because we apply the suggestion twice :^( #![feature(type_alias_impl_trait)] #![allow(dead_code)] @@ -8,7 +8,9 @@ trait TraitWithAssoc { type Assoc; } -type Foo<V> = impl Trait<V::Assoc>; //~ associated type `Assoc` not found for `V` +type Foo<V> = impl Trait<V::Assoc>; +//~^ associated type `Assoc` not found for `V` +//~| associated type `Assoc` not found for `V` trait Trait<U> {} diff --git a/tests/ui/type-alias-impl-trait/not_well_formed.stderr b/tests/ui/type-alias-impl-trait/not_well_formed.stderr index dbd80ffa4f6..a2944a1acb9 100644 --- a/tests/ui/type-alias-impl-trait/not_well_formed.stderr +++ b/tests/ui/type-alias-impl-trait/not_well_formed.stderr @@ -9,6 +9,18 @@ help: consider restricting type parameter `V` LL | type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>; | ++++++++++++++++ -error: aborting due to 1 previous error +error[E0220]: associated type `Assoc` not found for `V` + --> $DIR/not_well_formed.rs:11:29 + | +LL | type Foo<V> = impl Trait<V::Assoc>; + | ^^^^^ there is an associated type `Assoc` in the trait `TraitWithAssoc` + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider restricting type parameter `V` + | +LL | type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>; + | ++++++++++++++++ + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs new file mode 100644 index 00000000000..37e0d89efc7 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.rs @@ -0,0 +1,16 @@ +// test for ICE #121472 index out of bounds un_derefer.rs +#![feature(type_alias_impl_trait)] + +trait T {} + +type Alias<'a> = impl T; + +struct S; +impl<'a> T for &'a S {} + +fn with_positive(fun: impl Fn(Alias<'_>)) {} + +fn main() { + with_positive(|&n| ()); + //~^ ERROR mismatched types +} diff --git a/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr new file mode 100644 index 00000000000..a224bab0705 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/underef-index-out-of-bounds-121472.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/underef-index-out-of-bounds-121472.rs:14:20 + | +LL | type Alias<'a> = impl T; + | ------ the expected opaque type +... +LL | with_positive(|&n| ()); + | ^^ + | | + | expected opaque type, found `&_` + | expected due to this + | + = note: expected opaque type `Alias<'_>` + found reference `&_` +help: consider removing `&` from the pattern + | +LL - with_positive(|&n| ()); +LL + with_positive(|n| ()); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type/type-parameter-names.stderr b/tests/ui/type/type-parameter-names.stderr index 8e3e2388c6c..be9000a99e4 100644 --- a/tests/ui/type/type-parameter-names.stderr +++ b/tests/ui/type/type-parameter-names.stderr @@ -13,6 +13,7 @@ LL | x found type parameter `Foo` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + = note: the caller chooses a type for `Bar` which can be different from `Foo` error: aborting due to 1 previous error diff --git a/tests/ui/type/type-params-in-different-spaces-3.stderr b/tests/ui/type/type-params-in-different-spaces-3.stderr index 58783fe1ff0..3c0c022f112 100644 --- a/tests/ui/type/type-params-in-different-spaces-3.stderr +++ b/tests/ui/type/type-params-in-different-spaces-3.stderr @@ -14,6 +14,7 @@ LL | u found type parameter `X` = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + = note: the caller chooses a type for `Self` which can be different from `X` error: aborting due to 1 previous error diff --git a/tests/ui/typeck/escaping_bound_vars.rs b/tests/ui/typeck/escaping_bound_vars.rs index f886388bfbd..985a3fdbccf 100644 --- a/tests/ui/typeck/escaping_bound_vars.rs +++ b/tests/ui/typeck/escaping_bound_vars.rs @@ -11,9 +11,6 @@ where (): Test<{ 1 + (<() as Elide(&())>::call) }>, //~^ ERROR cannot capture late-bound lifetime in constant //~| ERROR associated type bindings are not allowed here - //~| ERROR the trait bound `(): Elide<(&(),)>` is not satisfied - //~| ERROR the trait bound `(): Elide<(&(),)>` is not satisfied - //~| ERROR cannot add { } diff --git a/tests/ui/typeck/escaping_bound_vars.stderr b/tests/ui/typeck/escaping_bound_vars.stderr index 8c7dcdb7f16..bd9c95fab97 100644 --- a/tests/ui/typeck/escaping_bound_vars.stderr +++ b/tests/ui/typeck/escaping_bound_vars.stderr @@ -12,49 +12,6 @@ error[E0229]: associated type bindings are not allowed here LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, | ^^^^^^^^^^ associated type not allowed here -error[E0277]: the trait bound `(): Elide<(&(),)>` is not satisfied - --> $DIR/escaping_bound_vars.rs:11:22 - | -LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, - | ^^ the trait `Elide<(&(),)>` is not implemented for `()` - | -help: this trait has no implementations, consider adding one - --> $DIR/escaping_bound_vars.rs:5:1 - | -LL | trait Elide<T> { - | ^^^^^^^^^^^^^^ - -error[E0277]: cannot add `fn() {<() as Elide<(&(),)>>::call}` to `{integer}` - --> $DIR/escaping_bound_vars.rs:11:18 - | -LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, - | ^ no implementation for `{integer} + fn() {<() as Elide<(&(),)>>::call}` - | - = help: the trait `Add<fn() {<() as Elide<(&(),)>>::call}>` is not implemented for `{integer}` - = help: the following other types implement trait `Add<Rhs>`: - <isize as Add> - <isize as Add<&isize>> - <i8 as Add> - <i8 as Add<&i8>> - <i16 as Add> - <i16 as Add<&i16>> - <i32 as Add> - <i32 as Add<&i32>> - and 48 others - -error[E0277]: the trait bound `(): Elide<(&(),)>` is not satisfied - --> $DIR/escaping_bound_vars.rs:11:18 - | -LL | (): Test<{ 1 + (<() as Elide(&())>::call) }>, - | ^ the trait `Elide<(&(),)>` is not implemented for `()` - | -help: this trait has no implementations, consider adding one - --> $DIR/escaping_bound_vars.rs:5:1 - | -LL | trait Elide<T> { - | ^^^^^^^^^^^^^^ - -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0229, E0277. -For more information about an error, try `rustc --explain E0229`. +For more information about this error, try `rustc --explain E0229`. diff --git a/tests/ui/typeck/issue-13853.stderr b/tests/ui/typeck/issue-13853.stderr index 0683c782933..45363c87d29 100644 --- a/tests/ui/typeck/issue-13853.stderr +++ b/tests/ui/typeck/issue-13853.stderr @@ -9,6 +9,7 @@ LL | self.iter() | = note: expected type parameter `I` found struct `std::slice::Iter<'_, N>` + = note: the caller chooses a type for `I` which can be different from `std::slice::Iter<'_, N>` error[E0599]: no method named `iter` found for reference `&G` in the current scope --> $DIR/issue-13853.rs:27:23 diff --git a/tests/ui/typeck/issue-91267.stderr b/tests/ui/typeck/issue-91267.stderr index 7e48b251980..399309d0ec4 100644 --- a/tests/ui/typeck/issue-91267.stderr +++ b/tests/ui/typeck/issue-91267.stderr @@ -17,6 +17,8 @@ LL | fn main() { | - expected `()` because of default return type LL | type_ascribe!(0, u8<e<5>=e>) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `u8` + | + = note: this error originates in the macro `type_ascribe` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/unboxed-closures/unboxed-closure-sugar-region.rs b/tests/ui/unboxed-closures/unboxed-closure-sugar-region.rs index 8537eb71774..ea73b8b3c4a 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-sugar-region.rs +++ b/tests/ui/unboxed-closures/unboxed-closure-sugar-region.rs @@ -33,7 +33,6 @@ fn test2(x: &dyn Foo<(isize,),Output=()>, y: &dyn Foo(isize)) { //~^ ERROR trait takes 1 lifetime argument but 0 lifetime arguments were supplied // Here, the omitted lifetimes are expanded to distinct things. same_type(x, y) - //~^ ERROR borrowed data escapes outside of function } fn main() { } diff --git a/tests/ui/unboxed-closures/unboxed-closure-sugar-region.stderr b/tests/ui/unboxed-closures/unboxed-closure-sugar-region.stderr index 6e6c649ca3d..d73aef851fd 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-sugar-region.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-sugar-region.stderr @@ -34,22 +34,6 @@ note: trait defined here, with 1 lifetime parameter: `'a` LL | trait Foo<'a,T> { | ^^^ -- -error[E0521]: borrowed data escapes outside of function - --> $DIR/unboxed-closure-sugar-region.rs:35:5 - | -LL | fn test2(x: &dyn Foo<(isize,),Output=()>, y: &dyn Foo(isize)) { - | - - `y` declared here, outside of the function body - | | - | `x` is a reference that is only valid in the function body - | has type `&dyn Foo<'1, (isize,), Output = ()>` -... -LL | same_type(x, y) - | ^^^^^^^^^^^^^^^ - | | - | `x` escapes the function body here - | argument requires that `'1` must outlive `'static` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0107, E0521. -For more information about an error, try `rustc --explain E0107`. +For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/unboxed-closures/unboxed-closures-type-mismatch-closure-from-another-scope.stderr b/tests/ui/unboxed-closures/unboxed-closures-type-mismatch-closure-from-another-scope.stderr index 1470c32d7de..5f22c781345 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-type-mismatch-closure-from-another-scope.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-type-mismatch-closure-from-another-scope.stderr @@ -26,11 +26,11 @@ note: closure parameter defined here LL | let mut closure = expect_sig(|p, y| *p = y); | ^ -error[E0425]: cannot find function `deref` in this scope +error[E0423]: expected function, found macro `deref` --> $DIR/unboxed-closures-type-mismatch-closure-from-another-scope.rs:13:5 | LL | deref(p); - | ^^^^^ not found in this scope + | ^^^^^ not a function | help: use the `.` operator to call the method `Deref::deref` on `&&()` | @@ -40,5 +40,5 @@ LL + p.deref(); error: aborting due to 4 previous errors -Some errors have detailed explanations: E0308, E0425. +Some errors have detailed explanations: E0308, E0423, E0425. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/underscore-lifetime/underscore-lifetime-binders.rs b/tests/ui/underscore-lifetime/underscore-lifetime-binders.rs index ebc38798544..3d049cc5639 100644 --- a/tests/ui/underscore-lifetime/underscore-lifetime-binders.rs +++ b/tests/ui/underscore-lifetime/underscore-lifetime-binders.rs @@ -14,7 +14,6 @@ fn meh() -> Box<dyn for<'_> Meh<'_>> //~ ERROR cannot be used here } fn foo2(_: &'_ u8, y: &'_ u8) -> &'_ u8 { y } //~ ERROR missing lifetime specifier -//~^ ERROR lifetime may not live long enough fn main() { let x = 5; diff --git a/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr b/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr index 5d2954d1d71..cd74d27dcb5 100644 --- a/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr +++ b/tests/ui/underscore-lifetime/underscore-lifetime-binders.stderr @@ -45,15 +45,7 @@ help: consider introducing a named lifetime parameter LL | fn foo2<'a>(_: &'a u8, y: &'a u8) -> &'a u8 { y } | ++++ ~~ ~~ ~~ -error: lifetime may not live long enough - --> $DIR/underscore-lifetime-binders.rs:16:43 - | -LL | fn foo2(_: &'_ u8, y: &'_ u8) -> &'_ u8 { y } - | - ^ returning this value requires that `'1` must outlive `'static` - | | - | let's call the lifetime of this reference `'1` - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0106, E0637. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/union/union-macro.rs b/tests/ui/union/union-macro.rs index 01cba85deb3..729f56de7a0 100644 --- a/tests/ui/union/union-macro.rs +++ b/tests/ui/union/union-macro.rs @@ -15,6 +15,7 @@ macro_rules! duplicate { duplicate! { pub union U { + #[allow(dead_code)] pub a: u8 } } diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs new file mode 100644 index 00000000000..db155f4fa3c --- /dev/null +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs @@ -0,0 +1,31 @@ +trait Trait<const N: Trait = bar> { + //~^ ERROR cannot find value `bar` in this scope + //~| ERROR cycle detected when computing type of `Trait::N` + //~| ERROR cycle detected when computing type of `Trait::N` + //~| ERROR the trait `Trait` cannot be made into an object + //~| ERROR the trait `Trait` cannot be made into an object + //~| ERROR the trait `Trait` cannot be made into an object + //~| ERROR `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + fn fnc<const N: Trait = u32>(&self) -> Trait { + //~^ ERROR the name `N` is already used for a generic parameter in this item's generic parameters + //~| ERROR expected value, found builtin type `u32` + //~| ERROR defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + //~| ERROR associated item referring to unboxed trait object for its own trait + //~| ERROR the trait `Trait` cannot be made into an object + //~| ERROR `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + bar + //~^ ERROR cannot find value `bar` in this scope + } +} + +fn main() {} diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr new file mode 100644 index 00000000000..cf985d9d1fd --- /dev/null +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr @@ -0,0 +1,228 @@ +error[E0403]: the name `N` is already used for a generic parameter in this item's generic parameters + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:18 + | +LL | trait Trait<const N: Trait = bar> { + | - first use of `N` +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^ already used + +error[E0425]: cannot find value `bar` in this scope + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:30 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^ not found in this scope + +error[E0423]: expected value, found builtin type `u32` + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:29 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^ not a value + +error[E0425]: cannot find value `bar` in this scope + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:26:9 + | +LL | bar + | ^^^ not found in this scope + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:22 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> + = note: `#[warn(bare_trait_objects)]` on by default +help: if this is an object-safe trait, use `dyn` + | +LL | trait Trait<const N: dyn Trait = bar> { + | +++ + +error[E0391]: cycle detected when computing type of `Trait::N` + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:22 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^ + | + = note: ...which immediately requires computing type of `Trait::N` again +note: cycle used when computing explicit predicates of trait `Trait` + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:1 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error[E0391]: cycle detected when computing type of `Trait::N` + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:13 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: ...which immediately requires computing type of `Trait::N` again +note: cycle used when computing explicit predicates of trait `Trait` + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:1 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:12 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^^^^^^^^^^^^^^^^ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> +help: if this is an object-safe trait, use `dyn` + | +LL | fn fnc<const N: dyn Trait = u32>(&self) -> Trait { + | +++ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:44 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> +help: if this is an object-safe trait, use `dyn` + | +LL | fn fnc<const N: Trait = u32>(&self) -> dyn Trait { + | +++ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:22 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: if this is an object-safe trait, use `dyn` + | +LL | trait Trait<const N: dyn Trait = bar> { + | +++ + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:22 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + | +LL | trait Trait<const N: Trait = bar> { + | ----- this trait cannot be made into an object... +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^ ...because method `fnc` has generic type parameters + = help: consider moving `fnc` to another trait + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:13 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + | +LL | trait Trait<const N: Trait = bar> { + | ----- this trait cannot be made into an object... +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^ ...because method `fnc` has generic type parameters + = help: consider moving `fnc` to another trait + +error: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:22 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error: associated item referring to unboxed trait object for its own trait + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:44 + | +LL | trait Trait<const N: Trait = bar> { + | ----- in this trait +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL | fn fnc<const N: Trait = u32>(&self) -> Self { + | ~~~~ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: if this is an object-safe trait, use `dyn` + | +LL | fn fnc<const N: dyn Trait = u32>(&self) -> Trait { + | +++ + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + | +LL | trait Trait<const N: Trait = bar> { + | ----- this trait cannot be made into an object... +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^ ...because method `fnc` has generic type parameters + = help: consider moving `fnc` to another trait + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:13 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + | +LL | trait Trait<const N: Trait = bar> { + | ----- this trait cannot be made into an object... +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^ ...because method `fnc` has generic type parameters + = help: consider moving `fnc` to another trait + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error: aborting due to 14 previous errors; 5 warnings emitted + +Some errors have detailed explanations: E0038, E0391, E0403, E0423, E0425. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs index 0be5127dcc4..e6d4e2ee01a 100644 --- a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs +++ b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.rs @@ -14,4 +14,5 @@ impl Trait for Ref {} //~ ERROR: implicit elided lifetime not allowed here extern "C" { pub fn repro(_: Wrapper<Ref>); + //~^ ERROR the trait bound `Ref<'_>: Trait` is not satisfied } diff --git a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr index 0af4ab022e1..59b55b2732d 100644 --- a/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr +++ b/tests/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr @@ -9,6 +9,19 @@ help: indicate the anonymous lifetime LL | impl Trait for Ref<'_> {} | ++++ -error: aborting due to 1 previous error +error[E0277]: the trait bound `Ref<'_>: Trait` is not satisfied + --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:21 + | +LL | pub fn repro(_: Wrapper<Ref>); + | ^^^^^^^^^^^^ the trait `Trait` is not implemented for `Ref<'_>` + | +note: required by a bound in `Wrapper` + --> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:8:23 + | +LL | pub struct Wrapper<T: Trait>(T); + | ^^^^^ required by this bound in `Wrapper` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0726`. +Some errors have detailed explanations: E0277, E0726. +For more information about an error, try `rustc --explain E0277`. diff --git a/triagebot.toml b/triagebot.toml index 0a36eab7b87..2bcc77ae433 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -499,6 +499,10 @@ of `ObligationCtxt`. """ cc = ["@lcnr", "@compiler-errors"] +[mentions."compiler/rustc_hir_analysis/src/hir_ty_lowering"] +message = "HIR ty lowering was modified" +cc = ["@fmease"] + [mentions."compiler/rustc_error_codes/src/lib.rs"] message = "Some changes occurred in diagnostic error codes" cc = ["@GuillaumeGomez"] @@ -619,7 +623,7 @@ cc = ["@davidtwco", "@compiler-errors", "@TaKO8Ki"] [mentions."compiler/stable_mir"] message = "This PR changes Stable MIR" -cc = ["@oli-obk", "@celinval", "@spastorino", "@ouz-a"] +cc = ["@oli-obk", "@celinval", "@ouz-a"] [mentions."compiler/rustc_target/src/spec"] message = """ @@ -829,6 +833,7 @@ parser = [ "@estebank", "@nnethercote", "@petrochenkov", + "@spastorino", ] lexer = [ "@nnethercote", @@ -837,6 +842,7 @@ lexer = [ ] arena = [ "@nnethercote", + "@spastorino", ] mir = [ "@davidtwco", |
