diff --git a/.gitignore b/.gitignore index aabec0988a99..a36cb51de33f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,9 @@ Session.vim *.iml .vscode .project +.vim/ +.helix/ +.zed/ .favorites.json .settings/ .vs/ diff --git a/Cargo.lock b/Cargo.lock index a4b4e49f82c2..4bd99b7fbe7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,11 +205,11 @@ dependencies = [ [[package]] name = "ar_archive_writer" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8412a2d690663356cba5a2532f3ed55d1e8090743bc6695b88403b27df67733" +checksum = "3f2bcb7cf51decfbbfc7ef476e28b0775b13e5eb1190f8b7df145cd53d4f4374" dependencies = [ - "object 0.35.0", + "object 0.36.2", ] [[package]] @@ -564,7 +564,7 @@ dependencies = [ "termize", "tokio", "toml 0.7.8", - "ui_test 0.24.0", + "ui_test 0.25.0", "walkdir", ] @@ -572,6 +572,7 @@ dependencies = [ name = "clippy_config" version = "0.1.82" dependencies = [ + "itertools", "rustc-semver", "serde", "toml 0.7.8", @@ -1406,8 +1407,11 @@ name = "generate-copyright" version = "0.1.0" dependencies = [ "anyhow", + "cargo_metadata 0.18.1", + "rinja", "serde", "serde_json", + "thiserror", ] [[package]] @@ -2454,15 +2458,6 @@ dependencies = [ "ruzstd 0.5.0", ] -[[package]] -name = "object" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" -dependencies = [ - "memchr", -] - [[package]] name = "object" version = "0.36.2" @@ -3103,7 +3098,10 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d3762e3740cdbf2fd2be465cc2c26d643ad17353cc2e0223d211c1b096118bd" dependencies = [ + "humansize", "itoa", + "num-traits", + "percent-encoding", "rinja_derive", ] @@ -4939,9 +4937,9 @@ dependencies = [ [[package]] name = "spanned" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed14ba8b4b82241bd5daba2c49185d4a0581a0058355fe96537338f002b8605d" +checksum = "86af297923fbcfd107c20a189a6e9c872160df71a7190ae4a7a6c5dce4b2feb6" dependencies = [ "bstr", "color-eyre", @@ -5565,9 +5563,9 @@ dependencies = [ [[package]] name = "ui_test" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc1c6c78d55482388711c8d417b8e547263046a607512278fed274c54633bbe4" +checksum = "f7e4f339f62edc873975c47115f9e71c5454ddaa37c1142b42fc0b2672c8dacb" dependencies = [ "annotate-snippets 0.11.4", "anyhow", diff --git a/REUSE.toml b/REUSE.toml index 1a30d8016c9e..efd705552478 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -163,7 +163,7 @@ SPDX-License-Identifier = "MIT OR Apache-2.0" path = "src/llvm-project/**" precedence = "override" SPDX-FileCopyrightText = [ - "2003-2019 by the contributors listed in [CREDITS.TXT](https://github.com/rust-lang/llvm-project/blob/7738295178045041669876bf32b0543ec8319a5c/llvm/CREDITS.TXT)", + "2003-2019 by the contributors listed in CREDITS.TXT (https://github.com/rust-lang/llvm-project/blob/7738295178045041669876bf32b0543ec8319a5c/llvm/CREDITS.TXT)", "2010 Apple Inc", "2003-2019 University of Illinois at Urbana-Champaign.", ] diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs index 29766fc9d87c..e9a7397557eb 100644 --- a/compiler/rustc/src/main.rs +++ b/compiler/rustc/src/main.rs @@ -1,3 +1,6 @@ +// We need this feature as it changes `dylib` linking behavior and allows us to link to `rustc_driver`. +#![feature(rustc_private)] + // A note about jemalloc: rustc uses jemalloc when built for CI and // distribution. The obvious way to do this is with the `#[global_allocator]` // mechanism. However, for complicated reasons (see diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fc1af3fc3dd1..a44ed8285048 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -585,7 +585,9 @@ impl Pat { } // A slice/array pattern `[P]` can be reparsed as `[T]`, an unsized array, // when `P` can be reparsed as a type `T`. - PatKind::Slice(pats) if pats.len() == 1 => pats[0].to_ty().map(TyKind::Slice)?, + PatKind::Slice(pats) if let [pat] = pats.as_slice() => { + pat.to_ty().map(TyKind::Slice)? + } // A tuple pattern `(P0, .., Pn)` can be reparsed as `(T0, .., Tn)` // assuming `T0` to `Tn` are all syntactically valid as types. PatKind::Tuple(pats) => { @@ -1187,8 +1189,8 @@ impl Expr { /// Does not ensure that the path resolves to a const param, the caller should check this. pub fn is_potential_trivial_const_arg(&self) -> bool { let this = if let ExprKind::Block(block, None) = &self.kind - && block.stmts.len() == 1 - && let StmtKind::Expr(expr) = &block.stmts[0].kind + && let [stmt] = block.stmts.as_slice() + && let StmtKind::Expr(expr) = &stmt.kind { expr } else { @@ -1248,7 +1250,9 @@ impl Expr { expr.to_ty().map(|ty| TyKind::Array(ty, expr_len.clone()))? } - ExprKind::Array(exprs) if exprs.len() == 1 => exprs[0].to_ty().map(TyKind::Slice)?, + ExprKind::Array(exprs) if let [expr] = exprs.as_slice() => { + expr.to_ty().map(TyKind::Slice)? + } ExprKind::Tup(exprs) => { let tys = exprs.iter().map(|expr| expr.to_ty()).collect::>>()?; diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 6e8ff72cf875..300bfa101c6b 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -275,8 +275,8 @@ impl<'hir> LoweringContext<'_, 'hir> { // FIXME(fn_delegation): Alternatives for target expression lowering: // https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600. fn lower_target_expr(&mut self, block: &Block) -> hir::Expr<'hir> { - if block.stmts.len() == 1 - && let StmtKind::Expr(expr) = &block.stmts[0].kind + if let [stmt] = block.stmts.as_slice() + && let StmtKind::Expr(expr) = &stmt.kind { return self.lower_expr_mut(expr); } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 1456890a0a23..7af3945d3f99 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1668,7 +1668,6 @@ impl<'hir> LoweringContext<'_, 'hir> { }), )), )), - // FIXME(effects) we might not need a default. default: Some(default_ct), is_host_effect: true, synthetic: true, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index ee4514758c21..c7ff39d23ed2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -502,8 +502,8 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere if !self.is_beginning_of_line() { self.word(" "); } - if cmnt.lines.len() == 1 { - self.word(cmnt.lines[0].clone()); + if let [line] = cmnt.lines.as_slice() { + self.word(line.clone()); self.hardbreak() } else { self.visual_align(); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 56204d8835a3..85a0b3b20229 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -783,8 +783,8 @@ impl<'a> State<'a> { } if items.is_empty() { self.word("{}"); - } else if items.len() == 1 { - self.print_use_tree(&items[0].0); + } else if let [(item, _)] = items.as_slice() { + self.print_use_tree(item); } else { self.cbox(INDENT_UNIT); self.word("{"); diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 12a19ae5c3db..cf86a6942da7 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -665,12 +665,12 @@ pub fn eval_condition( res & eval_condition(mi.meta_item().unwrap(), sess, features, eval) }), sym::not => { - if mis.len() != 1 { + let [mi] = mis.as_slice() else { dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); return false; - } + }; - !eval_condition(mis[0].meta_item().unwrap(), sess, features, eval) + !eval_condition(mi.meta_item().unwrap(), sess, features, eval) } sym::target => { if let Some(features) = features @@ -1051,10 +1051,10 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec { MetaItemKind::List(nested_items) => { if meta_item.has_name(sym::align) { recognised = true; - if nested_items.len() == 1 { + if let [nested_item] = nested_items.as_slice() { sess.dcx().emit_err( session_diagnostics::IncorrectReprFormatExpectInteger { - span: nested_items[0].span(), + span: nested_item.span(), }, ); } else { @@ -1066,10 +1066,10 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec { } } else if meta_item.has_name(sym::packed) { recognised = true; - if nested_items.len() == 1 { + if let [nested_item] = nested_items.as_slice() { sess.dcx().emit_err( session_diagnostics::IncorrectReprFormatPackedExpectInteger { - span: nested_items[0].span(), + span: nested_item.span(), }, ); } else { diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7a925705806f..a58c7c43246b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1124,8 +1124,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { err.multipart_suggestion( "consider moving the expression out of the loop so it is only moved once", vec![ - (parent.span, "value".to_string()), (span.shrink_to_lo(), format!("let mut value = {value};{indent}")), + (parent.span, "value".to_string()), ], Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index 9356c24d018f..de0df3474299 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -206,8 +206,8 @@ impl OutlivesSuggestionBuilder { // If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a // list of diagnostics. - let mut diag = if suggested.len() == 1 { - mbcx.dcx().struct_help(match suggested.last().unwrap() { + let mut diag = if let [constraint] = suggested.as_slice() { + mbcx.dcx().struct_help(match constraint { SuggestedConstraint::Outlives(a, bs) => { let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect(); format!("add bound `{a}: {}`", bs.join(" + ")) diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 8c9de5210cd3..1073ea406948 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -225,21 +225,26 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Find something that we can name let upper_bound = self.approx_universal_upper_bound(vid); - let upper_bound = &self.definitions[upper_bound]; - match upper_bound.external_name { - Some(reg) => reg, - None => { - // Nothing exact found, so we pick the first one that we find. - let scc = self.constraint_sccs.scc(vid); - for vid in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) { - match self.definitions[vid].external_name { - None => {} - Some(region) if region.is_static() => {} - Some(region) => return region, - } - } - region - } + if let Some(universal_region) = self.definitions[upper_bound].external_name { + return universal_region; + } + + // Nothing exact found, so we pick a named upper bound, if there's only one. + // If there's >1 universal region, then we probably are dealing w/ an intersection + // region which cannot be mapped back to a universal. + // FIXME: We could probably compute the LUB if there is one. + let scc = self.constraint_sccs.scc(vid); + let upper_bounds: Vec<_> = self + .rev_scc_graph + .as_ref() + .unwrap() + .upper_bounds(scc) + .filter_map(|vid| self.definitions[vid].external_name) + .filter(|r| !r.is_static()) + .collect(); + match &upper_bounds[..] { + [universal_region] => *universal_region, + _ => region, } } _ => region, diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 17439dd3e3ed..ed54c0c86626 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -745,10 +745,9 @@ fn expand_preparsed_asm( unused_operands.push((args.operands[idx].1, msg)); } } - match unused_operands.len() { - 0 => {} - 1 => { - let (sp, msg) = unused_operands.into_iter().next().unwrap(); + match unused_operands[..] { + [] => {} + [(sp, msg)] => { ecx.dcx() .struct_span_err(sp, msg) .with_span_label(sp, msg) diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index c8ab8ed681c1..c90a91648860 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -378,8 +378,8 @@ impl BlockOrExpr { None => cx.expr_block(cx.block(span, ThinVec::new())), Some(expr) => expr, } - } else if self.0.len() == 1 - && let ast::StmtKind::Expr(expr) = &self.0[0].kind + } else if let [stmt] = self.0.as_slice() + && let ast::StmtKind::Expr(expr) = &stmt.kind && self.1.is_none() { // There's only a single statement expression. Pull it out. @@ -1273,7 +1273,7 @@ impl<'a> MethodDef<'a> { } FieldlessVariantsStrategy::Default => (), } - } else if variants.len() == 1 { + } else if let [variant] = variants.as_slice() { // If there is a single variant, we don't need an operation on // the discriminant(s). Just use the most degenerate result. return self.call_substructure_method( @@ -1281,7 +1281,7 @@ impl<'a> MethodDef<'a> { trait_, type_ident, nonselflike_args, - &EnumMatching(0, &variants[0], Vec::new()), + &EnumMatching(0, variant, Vec::new()), ); } } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 9c70f7ede8c7..f99530cad18f 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -180,8 +180,8 @@ fn make_format_args( Ok((mut err, suggested)) => { if !suggested { if let ExprKind::Block(block, None) = &efmt.kind - && block.stmts.len() == 1 - && let StmtKind::Expr(expr) = &block.stmts[0].kind + && let [stmt] = block.stmts.as_slice() + && let StmtKind::Expr(expr) = &stmt.kind && let ExprKind::Path(None, path) = &expr.kind && path.is_potential_trivial_const_arg() { @@ -196,8 +196,8 @@ fn make_format_args( } else { let sugg_fmt = match args.explicit_args().len() { 0 => "{}".to_string(), - _ => { - format!("{}{{}}", "{} ".repeat(args.explicit_args().len())) + count => { + format!("{}{{}}", "{} ".repeat(count)) } }; err.span_suggestion( @@ -555,7 +555,7 @@ fn make_format_args( }; let arg_name = args.explicit_args()[index].kind.ident().unwrap(); ecx.buffered_early_lint.push(BufferedEarlyLint { - span: arg_name.span.into(), + span: Some(arg_name.span.into()), node_id: rustc_ast::CRATE_NODE_ID, lint_id: LintId::of(NAMED_ARGUMENTS_USED_POSITIONALLY), diagnostic: BuiltinLintDiag::NamedArgumentUsedPositionally { diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml index 1ed6f8fc359d..30dc5cb16154 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml @@ -2,13 +2,14 @@ name: Abi-cafe on: - push + - pull_request permissions: {} jobs: abi_cafe: runs-on: ${{ matrix.os }} - timeout-minutes: 60 + timeout-minutes: 30 concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os }}-${{ matrix.env.TARGET_TRIPLE }} cancel-in-progress: true @@ -27,12 +28,16 @@ jobs: - os: macos-latest env: TARGET_TRIPLE: x86_64-apple-darwin + - os: macos-latest + env: + TARGET_TRIPLE: aarch64-apple-darwin - os: windows-latest env: TARGET_TRIPLE: x86_64-pc-windows-msvc - - os: windows-latest - env: - TARGET_TRIPLE: x86_64-pc-windows-gnu + # FIXME Currently hangs. Re-enable once this is fixed or abi-cafe adds a timeout. + #- os: windows-latest + # env: + # TARGET_TRIPLE: x86_64-pc-windows-gnu steps: - uses: actions/checkout@v4 diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml b/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml index b4f8ce0f5329..27c95572ef87 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/audit.yml @@ -13,7 +13,6 @@ jobs: - uses: actions/checkout@v4 - run: | sed -i 's/components.*/components = []/' rust-toolchain - echo 'profile = "minimal"' >> rust-toolchain - uses: rustsec/audit-check@v1.4.1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index a2ae3d63fb90..896a5c34c3ef 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -29,7 +29,6 @@ jobs: - name: Avoid installing rustc-dev run: | sed -i 's/components.*/components = ["rustfmt"]/' rust-toolchain - echo 'profile = "minimal"' >> rust-toolchain rustfmt -v - name: Rustfmt diff --git a/compiler/rustc_codegen_cranelift/.zed/settings.json b/compiler/rustc_codegen_cranelift/.zed/settings.json new file mode 100644 index 000000000000..e93bed369492 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/.zed/settings.json @@ -0,0 +1,68 @@ +{ + "format_on_save": "on", + "lsp": { + "rust-analyzer": { + "initialization_options": { + "diagnostics": { + // in case rustc.source is disabled for performance reasons; disable the errors about this + "disabled": ["unresolved-extern-crate", "unresolved-macro-call"] + }, + "rustc": { + "source": "discover" + }, + "imports": { + "granularity": { + "enforce": true, + "group": "module" + }, + "prefix": "crate" + }, + "cargo": { + "features": ["unstable-features"] + }, + "linkedProjects": [ + "./Cargo.toml", + "./build_system/Cargo.toml", + { + "crates": [ + { + "root_module": "./example/mini_core.rs", + "edition": "2018", + "deps": [], + "cfg": [] + }, + { + "root_module": "./example/mini_core_hello_world.rs", + "edition": "2018", + "deps": [ + { + "crate": 0, + "name": "mini_core" + } + ], + "cfg": [] + }, + { + "root_module": "./example/mod_bench.rs", + "edition": "2018", + "deps": [], + "cfg": [] + } + ] + }, + { + "sysroot_src": "./build/stdlib/library", + "crates": [ + { + "root_module": "./example/std_example.rs", + "edition": "2015", + "deps": [], + "cfg": [] + } + ] + } + ] + } + } + } +} diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index efec5db836bb..02d4d98dc421 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -46,21 +46,28 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6b33d7e757a887989eb18b35712b2a67d96171ec3149d1bfb657b29b7b367c" +checksum = "effa84ab2023f7138045ece6b326588c17447ca22e66db71ec15cb0a6c0c4ad2" dependencies = [ "cranelift-entity", ] [[package]] -name = "cranelift-codegen" -version = "0.109.0" +name = "cranelift-bitset" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9acf15cb22be42d07c3b57d7856329cb228b7315d385346149df2566ad5e4aa" +checksum = "38a1dfc50dca188a15d938867c4400589530bcb0138f7022aae6d059d1d8c309" + +[[package]] +name = "cranelift-codegen" +version = "0.110.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "821c20c639350158ecca928dc2a244d0d1c9cef2377a378fc62a445a286eb1ca" dependencies = [ "bumpalo", "cranelift-bforest", + "cranelift-bitset", "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-control", @@ -77,39 +84,42 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e934d301392b73b3f8b0540391fb82465a0f179a3cee7c726482ac4727efcc97" +checksum = "064473f2fd59b44fa2c9aaa60de1f9c44db5e13521e28bc85d2b92ee535ef625" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb2a2566b3d54b854dfb288b3b187f6d3d17d6f762c92898207eba302931da" +checksum = "d0f39b9ebfd2febdc2acfb9a0fca110665bcd5a6839502576307735ed07b2177" [[package]] name = "cranelift-control" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0100f33b704cdacd01ad66ff41f8c5030d57cbff078e2a4e49ab1822591299fa" +checksum = "94e125c189c3a1ca8dfe209fc6f46edba058a6d24e0b92aff69459a15f4711e7" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8cfdc315e5d18997093e040a8d234bea1ac1e118a716d3e30f40d449e78207b" +checksum = "ea62eb109baec2247e1a6fa7b74c0f584b1e76e289cfd7017385b4b031fc8450" +dependencies = [ + "cranelift-bitset", +] [[package]] name = "cranelift-frontend" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f74b84f16af2e982b0c0c72233503d9d55cbfe3865dbe807ca28dc6642a28b5" +checksum = "722b089357aacb6c7528b2e59a5fe00917d61ce63448b25a3e477a5b7819fac8" dependencies = [ "cranelift-codegen", "log", @@ -119,15 +129,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adf306d3dde705fb94bd48082f01d38c4ededc74293a4c007805f610bf08bc6e" +checksum = "c4b5005a48288e7fc2a2991a377831c534e26929b063c379c018060727785a9b" [[package]] name = "cranelift-jit" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5c5cfb8bbd3339cd25cca30e7516ff8fe5cb1feeddde6980cc4d5ef34df97bb" +checksum = "f843932baf8d1025c5f114b929eda172d74b7163d058e0de2597c308b567c7e9" dependencies = [ "anyhow", "cranelift-codegen", @@ -145,9 +155,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9b0d4269b36fd858e6d8f20cd4938941186fb831488c361888cb2d6b33a9a6" +checksum = "449819ef1c4af139cf1b9717916fcaea0e23248853d3e95135139773a842d3eb" dependencies = [ "anyhow", "cranelift-codegen", @@ -156,9 +166,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea0ebdef7aff4a79bcbc8b6495f31315f16b3bf311152f472eaa8d679352581" +checksum = "3ae2d48f38081a9e679ad795bd36bb29bedeb5552fc1c195185bf9885fa1b16e" dependencies = [ "cranelift-codegen", "libc", @@ -167,9 +177,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.109.0" +version = "0.110.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e33439ec20db058bc7cc3410f9748ab1ad90a35cef713d625c736f43e3820d" +checksum = "3a39ee2cfd0ec485eca76f6b4dc17701a280fa406bc05137bb43f1635ed12c9f" dependencies = [ "anyhow", "cranelift-codegen", @@ -279,9 +289,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "object" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ "crc32fast", "hashbrown 0.14.5", @@ -411,9 +421,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasmtime-jit-icache-coherence" -version = "22.0.0" +version = "23.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5afe2f0499542f9a4bcfa1b55bfdda803b6ade4e7c93c6b99e0f39dba44b0a91" +checksum = "7fddf3e2980fb1d123d1fcac55189e417fdd3dba4f62139b5a0a1f9efe5669d5" dependencies = [ "anyhow", "cfg-if", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index 2969a6cf6eca..a0df502dadc4 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,12 +8,12 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.109.0", default-features = false, features = ["std", "unwind", "all-arch"] } -cranelift-frontend = { version = "0.109.0" } -cranelift-module = { version = "0.109.0" } -cranelift-native = { version = "0.109.0" } -cranelift-jit = { version = "0.109.0", optional = true } -cranelift-object = { version = "0.109.0" } +cranelift-codegen = { version = "0.110.1", default-features = false, features = ["std", "unwind", "all-arch"] } +cranelift-frontend = { version = "0.110.1" } +cranelift-module = { version = "0.110.1" } +cranelift-native = { version = "0.110.1" } +cranelift-jit = { version = "0.110.1", optional = true } +cranelift-object = { version = "0.110.1" } target-lexicon = "0.12.0" gimli = { version = "0.28", default-features = false, features = ["write"]} object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index 3b3c86a1bd17..6766e2f844d6 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -16,7 +16,6 @@ $ rustup component add rustc-codegen-cranelift-preview --toolchain nightly Once it is installed, you can enable it with one of the following approaches: - `CARGO_PROFILE_DEV_CODEGEN_BACKEND=cranelift cargo +nightly build -Zcodegen-backend` -- `RUSTFLAGS="-Zcodegen-backend=cranelift" cargo +nightly build` - Add the following to `.cargo/config.toml`: ```toml [unstable] diff --git a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs index 75f9f233cb33..e3f1162445b3 100644 --- a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs +++ b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs @@ -1,4 +1,4 @@ -use crate::path::Dirs; +use crate::path::{Dirs, RelPath}; use crate::prepare::GitRepo; use crate::utils::{spawn_and_wait, CargoProject, Compiler}; use crate::{build_sysroot, CodegenBackend, SysrootKind}; @@ -6,8 +6,8 @@ use crate::{build_sysroot, CodegenBackend, SysrootKind}; static ABI_CAFE_REPO: GitRepo = GitRepo::github( "Gankra", "abi-cafe", - "4c6dc8c9c687e2b3a760ff2176ce236872b37212", - "588df6d66abbe105", + "f1220cfd13b57f5c0082c26529163865ee25e115", + "fe93a9acd461425d", "abi-cafe", ); @@ -21,6 +21,7 @@ pub(crate) fn run( rustup_toolchain_name: Option<&str>, bootstrap_host_compiler: &Compiler, ) { + RelPath::DOWNLOAD.ensure_exists(dirs); ABI_CAFE_REPO.fetch(dirs); ABI_CAFE_REPO.patch(dirs); @@ -38,17 +39,23 @@ pub(crate) fn run( eprintln!("Running abi-cafe"); let pairs = ["rustc_calls_cgclif", "cgclif_calls_rustc", "cgclif_calls_cc", "cc_calls_cgclif"]; - - let mut cmd = ABI_CAFE.run(bootstrap_host_compiler, dirs); - cmd.arg("--"); - cmd.arg("--pairs"); - cmd.args( + let pairs = if cfg!(not(any(target_os = "macos", all(target_os = "windows", target_env = "msvc")))) { &pairs[..] } else { &pairs[..2] - }, - ); + }; + + let mut cmd = ABI_CAFE.run(bootstrap_host_compiler, dirs); + cmd.arg("--"); + + // stdcall, vectorcall and such don't work yet + cmd.arg("--conventions").arg("c").arg("--conventions").arg("rust"); + + for pair in pairs { + cmd.arg("--pairs").arg(pair); + } + cmd.arg("--add-rustc-codegen-backend"); match cg_clif_dylib { CodegenBackend::Local(path) => { @@ -58,6 +65,7 @@ pub(crate) fn run( cmd.arg(format!("cgclif:{name}")); } } + cmd.current_dir(ABI_CAFE.source_dir(dirs)); spawn_and_wait(cmd); diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index ed8b5b906d28..e41f6c5e709e 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -312,7 +312,6 @@ fn build_rtstartup(dirs: &Dirs, compiler: &Compiler) -> Option { let obj = RTSTARTUP_SYSROOT.to_path(dirs).join(format!("{file}.o")); let mut build_rtstartup_cmd = Command::new(&compiler.rustc); build_rtstartup_cmd - .arg("-Ainternal_features") // Missing #[allow(internal_features)] .arg("--target") .arg(&compiler.triple) .arg("--emit=obj") diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index 5525a5f63e93..be0bed0f4e63 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -22,36 +22,6 @@ pub(crate) fn prepare_stdlib(dirs: &Dirs, rustc: &Path) { assert!(sysroot_src_orig.exists()); apply_patches(dirs, "stdlib", &sysroot_src_orig, &STDLIB_SRC.to_path(dirs)); - - std::fs::write( - STDLIB_SRC.to_path(dirs).join("Cargo.toml"), - r#" -[workspace] -resolver = "1" -members = ["./library/sysroot"] - -[patch.crates-io] -rustc-std-workspace-core = { path = "./library/rustc-std-workspace-core" } -rustc-std-workspace-alloc = { path = "./library/rustc-std-workspace-alloc" } -rustc-std-workspace-std = { path = "./library/rustc-std-workspace-std" } - -# Mandatory for correctly compiling compiler-builtins -[profile.dev.package.compiler_builtins] -debug-assertions = false -overflow-checks = false -codegen-units = 10000 - -[profile.release.package.compiler_builtins] -debug-assertions = false -overflow-checks = false -codegen-units = 10000 -"#, - ) - .unwrap(); - - let source_lockfile = RelPath::PATCHES.to_path(dirs).join("stdlib-lock.toml"); - let target_lockfile = STDLIB_SRC.to_path(dirs).join("Cargo.lock"); - fs::copy(source_lockfile, target_lockfile).unwrap(); } pub(crate) struct GitRepo { diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index afc8a923863a..38c3786a94a4 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -106,6 +106,7 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ ]); runner.run_out_command("gen_block_iterate", &[]); }), + TestCase::build_bin_and_run("aot.raw-dylib", "example/raw-dylib.rs", &[]), ]; pub(crate) static RAND_REPO: GitRepo = GitRepo::github( @@ -437,7 +438,7 @@ impl<'a> TestRunner<'a> { cmd.arg("-L"); cmd.arg(format!("crate={}", BUILD_EXAMPLE_OUT_DIR.to_path(&self.dirs).display())); cmd.arg("--out-dir"); - cmd.arg(format!("{}", BUILD_EXAMPLE_OUT_DIR.to_path(&self.dirs).display())); + cmd.arg(BUILD_EXAMPLE_OUT_DIR.to_path(&self.dirs)); cmd.arg("-Cdebuginfo=2"); cmd.arg("--target"); cmd.arg(&self.target_compiler.triple); diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index 0b7cac188376..527ec5303b60 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -45,6 +45,7 @@ aot.issue-59326 aot.polymorphize_coroutine aot.neon aot.gen_block_iterate +aot.raw-dylib testsuite.extended_sysroot test.rust-random/rand diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 7d361a9ab2bb..e603ac566f4e 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -585,6 +585,7 @@ pub enum E2 { V4, } +#[allow(unreachable_patterns)] fn check_niche_behavior() { if let E1::V2 { .. } = (E1::V1 { f: true }) { intrinsics::abort(); diff --git a/compiler/rustc_codegen_cranelift/example/raw-dylib.rs b/compiler/rustc_codegen_cranelift/example/raw-dylib.rs new file mode 100644 index 000000000000..4711884f76af --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/raw-dylib.rs @@ -0,0 +1,31 @@ +// Tests the raw-dylib feature for Windows. +// https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute + +fn main() { + #[cfg(windows)] + { + #[link(name = "kernel32", kind = "raw-dylib")] + extern "C" { + fn GetModuleFileNameA( + module: *mut std::ffi::c_void, + filename: *mut u8, + size: u32, + ) -> u32; + } + + // Get the filename of the current executable.... + let mut buffer = [0u8; 1024]; + let size = unsafe { + GetModuleFileNameA(core::ptr::null_mut(), buffer.as_mut_ptr(), buffer.len() as u32) + }; + if size == 0 { + eprintln!("failed to get module file name: {}", std::io::Error::last_os_error()); + return; + } else { + // ...and make sure that it matches the test name. + let filename = + std::ffi::CStr::from_bytes_with_nul(&buffer[..size as usize + 1]).unwrap(); + assert!(filename.to_str().unwrap().ends_with("raw-dylib.exe")); + } + } +} diff --git a/compiler/rustc_codegen_cranelift/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch b/compiler/rustc_codegen_cranelift/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch deleted file mode 100644 index 77716c513997..000000000000 --- a/compiler/rustc_codegen_cranelift/patches/0001-abi-cafe-Disable-some-test-on-x86_64-pc-windows-gnu.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 2b15fee2bb5fd14e34c7e17e44d99cb34f4c555d Mon Sep 17 00:00:00 2001 -From: Afonso Bordado -Date: Tue, 27 Sep 2022 07:55:17 +0100 -Subject: [PATCH] Disable some test on x86_64-pc-windows-gnu - ---- - src/report.rs | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/src/report.rs b/src/report.rs -index eeec614..f582867 100644 ---- a/src/report.rs -+++ b/src/report.rs -@@ -48,6 +48,15 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn AbiImpl, callee: &dyn AbiImpl - // - // THIS AREA RESERVED FOR VENDORS TO APPLY PATCHES - -+ // x86_64-pc-windows-gnu has some broken i128 tests that aren't disabled by default -+ if cfg!(all(target_os = "windows", target_env = "gnu")) && test.test_name == "ui128" { -+ result.run = Link; -+ result.check = Pass(Link); -+ } else if test.test_name == "ui128" { -+ result.run == Check; -+ result.check = Pass(Check); -+ } -+ - // END OF VENDOR RESERVED AREA - // - // --- -2.30.1.windows.1 - diff --git a/compiler/rustc_codegen_cranelift/patches/0002-abi-cafe-Disable-broken-tests.patch b/compiler/rustc_codegen_cranelift/patches/0002-abi-cafe-Disable-broken-tests.patch new file mode 100644 index 000000000000..8a2565f1668a --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0002-abi-cafe-Disable-broken-tests.patch @@ -0,0 +1,75 @@ +From 236df390f3bc4ed69c26f4d51d584bea246da886 Mon Sep 17 00:00:00 2001 +From: bjorn3 <17426603+bjorn3@users.noreply.github.com> +Date: Tue, 9 Jul 2024 11:25:14 +0000 +Subject: [PATCH] Disable broken tests + +--- + src/report.rs | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/src/report.rs b/src/report.rs +index 958ab43..dcf1044 100644 +--- a/src/report.rs ++++ b/src/report.rs +@@ -48,6 +48,58 @@ pub fn get_test_rules(test: &TestKey, caller: &dyn Toolchain, callee: &dyn Toolc + // + // THIS AREA RESERVED FOR VENDORS TO APPLY PATCHES + ++ if cfg!(all(target_arch = "aarch64", target_os = "linux")) { ++ if test.test == "F32Array" && test.options.convention == CallingConvention::C { ++ result.check = Busted(Check); ++ } ++ ++ if test.test == "OptionU128" && test.options.convention == CallingConvention::Rust && test.options.repr == LangRepr::C { ++ result.check = Busted(Check); ++ } ++ } ++ ++ if cfg!(all(target_arch = "aarch64", target_os = "macos")) { ++ if test.test == "SingleVariantUnion" && test.options.convention == CallingConvention::C && test.options.repr == LangRepr::C { ++ result.check = Busted(Check); ++ } ++ ++ if test.test == "OptionU128" && test.caller == "rustc" && test.options.convention == CallingConvention::Rust && test.options.repr == LangRepr::C { ++ result.check = Busted(Run); ++ } ++ ++ if test.test == "OptionU128" && test.caller == "cgclif" && test.options.convention == CallingConvention::Rust && test.options.repr == LangRepr::C { ++ result.check = Busted(Check); ++ } ++ } ++ ++ if cfg!(all(target_arch = "x86_64", unix)) { ++ if test.test == "OptionU128" && test.options.convention == CallingConvention::Rust && test.options.repr == LangRepr::Rust { ++ result.check = Busted(Run); ++ } ++ } ++ ++ if cfg!(all(target_arch = "x86_64", windows)) { ++ if test.test == "OptionU128" && test.options.convention == CallingConvention::Rust { ++ result.check = Busted(Check); ++ } ++ ++ if test.test == "OptionU128" && test.options.convention == CallingConvention::Rust && (test.caller == "rustc" || test.options.repr == LangRepr::Rust) { ++ result.check = Busted(Run); ++ } ++ ++ if test.test == "simple" && test.options.convention == CallingConvention::Rust { ++ result.check = Busted(Check); ++ } ++ ++ if test.test == "simple" && test.options.convention == CallingConvention::Rust && test.caller == "rustc" { ++ result.check = Busted(Run); ++ } ++ } ++ ++ if test.test == "f16" || test.test == "f128" { ++ result.run = Skip; ++ } ++ + // END OF VENDOR RESERVED AREA + // + // +-- +2.34.1 + diff --git a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch index 7cf7f86700ea..8c404956bcc2 100644 --- a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch +++ b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch @@ -37,8 +37,8 @@ index 42a26ae..5ac1042 100644 +++ b/lib.rs @@ -1,3 +1,4 @@ +#![cfg(test)] - #![feature(alloc_layout_extra)] - #![feature(array_chunks)] - #![feature(array_ptr_get)] + // tidy-alphabetical-start + #![cfg_attr(bootstrap, feature(offset_of_nested))] + #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] -- 2.21.0 (Apple Git-122) diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch index 271ca12eabbc..d579c9588f08 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch @@ -11,17 +11,17 @@ Cranelift doesn't support them yet 4 files changed, 4 insertions(+), 50 deletions(-) diff --git a/lib.rs b/lib.rs -index 897a5e9..331f66f 100644 +index 1e336bf..35e6f54 100644 --- a/lib.rs +++ b/lib.rs -@@ -93,7 +93,6 @@ - #![feature(const_option)] - #![feature(const_option_ext)] - #![feature(const_result)] +@@ -1,7 +1,6 @@ + #![cfg(test)] + // tidy-alphabetical-start + #![cfg_attr(bootstrap, feature(offset_of_nested))] -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] - #![feature(int_roundings)] - #![feature(split_array)] + #![feature(alloc_layout_extra)] + #![feature(array_chunks)] diff --git a/atomic.rs b/atomic.rs index b735957..ea728b6 100644 --- a/atomic.rs diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch new file mode 100644 index 000000000000..ada35145e293 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch @@ -0,0 +1,25 @@ +From 175d52c5e1779764b66777db1e6f172c2dc365ff Mon Sep 17 00:00:00 2001 +From: bjorn3 <17426603+bjorn3@users.noreply.github.com> +Date: Fri, 9 Aug 2024 15:44:51 +0000 +Subject: [PATCH] Disable f16 and f128 in compiler-builtins + +--- + library/sysroot/Cargo.toml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml +index 7165c3e48af..968552ad435 100644 +--- a/library/sysroot/Cargo.toml ++++ b/library/sysroot/Cargo.toml +@@ -11,7 +11,7 @@ test = { path = "../test" } + + # Forward features to the `std` crate as necessary + [features] +-default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] ++default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind", "compiler-builtins-no-f16-f128"] + backtrace = ["std/backtrace"] + compiler-builtins-c = ["std/compiler-builtins-c"] + compiler-builtins-mem = ["std/compiler-builtins-mem"] +-- +2.34.1 + diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-rawdylib-processprng.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-rawdylib-processprng.patch deleted file mode 100644 index 584dbdb647f6..000000000000 --- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-rawdylib-processprng.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 9f65e742ba3e41474e6126c6c4469c48eaa6ca7e Mon Sep 17 00:00:00 2001 -From: Chris Denton -Date: Tue, 20 Feb 2024 16:01:40 -0300 -Subject: [PATCH] Don't use raw-dylib in std - ---- - library/std/src/sys/pal/windows/c.rs | 2 +- - library/std/src/sys/pal/windows/rand.rs | 3 +-- - 2 files changed, 2 insertions(+), 3 deletions(-) - -diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs -index ad8e01bfa9b..9ca8e4c16ce 100644 ---- a/library/std/src/sys/pal/windows/c.rs -+++ b/library/std/src/sys/pal/windows/c.rs -@@ -312,7 +312,7 @@ pub unsafe fn NtWriteFile( - - // Use raw-dylib to import ProcessPrng as we can't rely on there being an import library. - cfg_if::cfg_if! { --if #[cfg(not(target_vendor = "win7"))] { -+if #[cfg(any())] { - #[cfg(target_arch = "x86")] - #[link(name = "bcryptprimitives", kind = "raw-dylib", import_name_type = "undecorated")] - extern "system" { -diff --git a/library/std/src/sys/pal/windows/rand.rs b/library/std/src/sys/pal/windows/rand.rs -index e427546222a..f2fe42a4d51 100644 ---- a/library/std/src/sys/pal/windows/rand.rs -+++ b/library/std/src/sys/pal/windows/rand.rs -@@ -2,7 +2,7 @@ - - use crate::sys::c; - --#[cfg(not(target_vendor = "win7"))] -+#[cfg(any())] - #[inline] - pub fn hashmap_random_keys() -> (u64, u64) { - let mut v = (0, 0); -@@ -13,7 +13,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { - v - } - --#[cfg(target_vendor = "win7")] - pub fn hashmap_random_keys() -> (u64, u64) { - use crate::ffi::c_void; - use crate::io; --- -2.42.0.windows.2 - diff --git a/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch b/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch deleted file mode 100644 index 21f5ee9cc6ea..000000000000 --- a/compiler/rustc_codegen_cranelift/patches/0030-stdlib-Revert-use-raw-dylib-for-Windows-futex-APIs.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0d741cf82c3c908616abd39dc84ebf7d8702e0c3 Mon Sep 17 00:00:00 2001 -From: Chris Denton -Date: Tue, 16 Apr 2024 15:51:34 +0000 -Subject: [PATCH] Revert use raw-dylib for Windows futex APIs - ---- - library/std/src/sys/pal/windows/c.rs | 14 +------------- - 1 file changed, 1 insertion(+), 13 deletions(-) - -diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs -index 9d58ce05f01..1c828bac4b6 100644 ---- a/library/std/src/sys/pal/windows/c.rs -+++ b/library/std/src/sys/pal/windows/c.rs -@@ -357,19 +357,7 @@ pub fn GetTempPath2W(bufferlength: u32, buffer: PWSTR) -> u32 { - } - - #[cfg(not(target_vendor = "win7"))] --// Use raw-dylib to import synchronization functions to workaround issues with the older mingw import library. --#[cfg_attr( -- target_arch = "x86", -- link( -- name = "api-ms-win-core-synch-l1-2-0", -- kind = "raw-dylib", -- import_name_type = "undecorated" -- ) --)] --#[cfg_attr( -- not(target_arch = "x86"), -- link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib") --)] -+#[link(name = "synchronization")] - extern "system" { - pub fn WaitOnAddress( - address: *const c_void, --- -2.42.0.windows.2 - diff --git a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml deleted file mode 100644 index 9ea53e8f848d..000000000000 --- a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml +++ /dev/null @@ -1,455 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" -dependencies = [ - "compiler_builtins", - "gimli 0.29.0", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "alloc" -version = "0.0.0" -dependencies = [ - "compiler_builtins", - "core", - "rand", - "rand_xorshift", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - -[[package]] -name = "cc" -version = "1.0.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "compiler_builtins" -version = "0.1.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ab134a739bafec76aa91ccb15d519a54e569350644a1fea6528d5a0d407e22" -dependencies = [ - "cc", - "rustc-std-workspace-core", -] - -[[package]] -name = "core" -version = "0.0.0" -dependencies = [ - "rand", - "rand_xorshift", -] - -[[package]] -name = "cupid" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bad352a84b567cc38a5854e3aa8ee903cb8519a25d0b799b739bafffd1f91a1" -dependencies = [ - "gcc", - "rustc_version", -] - -[[package]] -name = "dlmalloc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203540e710bfadb90e5e29930baf5d10270cec1f43ab34f46f78b147b2de715a" -dependencies = [ - "compiler_builtins", - "libc", - "rustc-std-workspace-core", -] - -[[package]] -name = "fortanix-sgx-abi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" - -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "rustc-std-workspace-core", - "rustc-std-workspace-std", - "unicode-width", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "gimli" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -dependencies = [ - "allocator-api2", - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -dependencies = [ - "rustc-std-workspace-core", -] - -[[package]] -name = "memchr" -version = "2.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "object" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" -dependencies = [ - "compiler_builtins", - "memchr", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "panic_abort" -version = "0.0.0" -dependencies = [ - "alloc", - "cfg-if", - "compiler_builtins", - "core", - "libc", -] - -[[package]] -name = "panic_unwind" -version = "0.0.0" -dependencies = [ - "alloc", - "cfg-if", - "compiler_builtins", - "core", - "libc", - "unwind", -] - -[[package]] -name = "proc_macro" -version = "0.0.0" -dependencies = [ - "core", - "std", -] - -[[package]] -name = "profiler_builtins" -version = "0.0.0" -dependencies = [ - "cc", - "compiler_builtins", - "core", -] - -[[package]] -name = "r-efi" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e244f96e03a3067f9e521d3167bd42657594cb8588c8d3a2db01545dc1af2e0" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "r-efi-alloc" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7" -dependencies = [ - "compiler_builtins", - "r-efi", - "rustc-std-workspace-core", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", -] - -[[package]] -name = "rustc-std-workspace-alloc" -version = "1.99.0" -dependencies = [ - "alloc", -] - -[[package]] -name = "rustc-std-workspace-core" -version = "1.99.0" -dependencies = [ - "core", -] - -[[package]] -name = "rustc-std-workspace-std" -version = "1.99.0" -dependencies = [ - "std", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "std" -version = "0.0.0" -dependencies = [ - "addr2line", - "alloc", - "cfg-if", - "compiler_builtins", - "core", - "dlmalloc", - "fortanix-sgx-abi", - "hashbrown", - "hermit-abi", - "libc", - "miniz_oxide", - "object", - "panic_abort", - "panic_unwind", - "profiler_builtins", - "r-efi", - "r-efi-alloc", - "rand", - "rand_xorshift", - "rustc-demangle", - "std_detect", - "unwind", - "wasi", -] - -[[package]] -name = "std_detect" -version = "0.1.5" -dependencies = [ - "cfg-if", - "compiler_builtins", - "cupid", - "libc", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] - -[[package]] -name = "sysroot" -version = "0.0.0" -dependencies = [ - "proc_macro", - "std", - "test", -] - -[[package]] -name = "test" -version = "0.0.0" -dependencies = [ - "core", - "getopts", - "libc", - "std", -] - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-core", - "rustc-std-workspace-std", -] - -[[package]] -name = "unwind" -version = "0.0.0" -dependencies = [ - "cfg-if", - "compiler_builtins", - "core", - "libc", - "unwinding", -] - -[[package]] -name = "unwinding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b" -dependencies = [ - "compiler_builtins", - "gimli 0.28.1", - "rustc-std-workspace-core", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -dependencies = [ - "compiler_builtins", - "rustc-std-workspace-alloc", - "rustc-std-workspace-core", -] diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index db9b551bd2a2..96c467e091cf 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,4 @@ [toolchain] -channel = "nightly-2024-07-13" +channel = "nightly-2024-08-09" components = ["rust-src", "rustc-dev", "llvm-tools"] +profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index f0550c23b177..bb5af9127b96 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -39,6 +39,7 @@ rm tests/ui/simd/dont-invalid-bitcast-x86_64.rs # unimplemented llvm.x86.sse41.r # exotic linkages rm tests/incremental/hashes/function_interfaces.rs rm tests/incremental/hashes/statics.rs +rm -r tests/run-make/naked-symbol-visibility # variadic arguments rm tests/ui/abi/mir/mir_codegen_calls_variadic.rs # requires float varargs @@ -118,6 +119,7 @@ rm tests/ui/mir/mir_misc_casts.rs # depends on deduplication of constants rm tests/ui/mir/mir_raw_fat_ptr.rs # same rm tests/ui/consts/issue-33537.rs # same rm tests/ui/consts/const-mut-refs-crate.rs # same +rm tests/ui/abi/large-byval-align.rs # exceeds implementation limit of Cranelift # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended @@ -134,6 +136,8 @@ rm tests/ui/deprecation/deprecated_inline_threshold.rs # missing deprecation war # bugs in the test suite # ====================== rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue +rm tests/ui/backtrace/synchronized-panic-handler.rs # missing needs-unwind annotation +rm -r tests/ui/codegen/equal-pointers-unequal # make incorrect assumptions about the location of stack variables rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd @@ -157,8 +161,8 @@ index ea06b620c4c..b969d0009c6 100644 RUSTDOC := \$(RUSTDOC) -Clinker='\$(RUSTC_LINKER)' diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs index 9607ff02f96..b7d97caf9a2 100644 ---- a/src/tools/run-make-support/src/rustdoc.rs -+++ b/src/tools/run-make-support/src/rustdoc.rs +--- a/src/tools/run-make-support/src/external_deps/rustdoc.rs ++++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs @@ -34,8 +34,6 @@ pub fn bare() -> Self { #[track_caller] pub fn new() -> Self { diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs index 1935005a08c5..5eedab4f2cba 100644 --- a/compiler/rustc_codegen_cranelift/src/archive.rs +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -1,8 +1,13 @@ -use std::path::{Path, PathBuf}; +use std::borrow::Borrow; +use std::fs; +use std::path::Path; +use ar_archive_writer::{COFFShortExport, MachineTypes}; use rustc_codegen_ssa::back::archive::{ - ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, + create_mingw_dll_import_lib, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, + DEFAULT_OBJECT_READER, }; +use rustc_codegen_ssa::common::is_mingw_gnu_toolchain; use rustc_session::Session; pub(crate) struct ArArchiveBuilderBuilder; @@ -15,11 +20,74 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { fn create_dll_import_lib( &self, sess: &Session, - _lib_name: &str, - _dll_imports: &[rustc_session::cstore::DllImport], - _tmpdir: &Path, - _is_direct_dependency: bool, - ) -> PathBuf { - sess.dcx().fatal("raw-dylib is not yet supported by rustc_codegen_cranelift"); + lib_name: &str, + import_name_and_ordinal_vector: Vec<(String, Option)>, + output_path: &Path, + ) { + if is_mingw_gnu_toolchain(&sess.target) { + // The binutils linker used on -windows-gnu targets cannot read the import + // libraries generated by LLVM: in our attempts, the linker produced an .EXE + // that loaded but crashed with an AV upon calling one of the imported + // functions. Therefore, use binutils to create the import library instead, + // by writing a .DEF file to the temp dir and calling binutils's dlltool. + create_mingw_dll_import_lib( + sess, + lib_name, + import_name_and_ordinal_vector, + output_path, + ); + } else { + let mut file = + match fs::OpenOptions::new().write(true).create_new(true).open(&output_path) { + Ok(file) => file, + Err(error) => { + sess.dcx().fatal(format!( + "failed to create import library file `{path}`: {error}", + path = output_path.display(), + )); + } + }; + + let machine = match sess.target.arch.borrow() { + "x86" => MachineTypes::I386, + "x86_64" => MachineTypes::AMD64, + "arm" => MachineTypes::ARMNT, + "aarch64" => MachineTypes::ARM64, + _ => { + sess.dcx().fatal(format!( + "unsupported target architecture `{arch}`", + arch = sess.target.arch, + )); + } + }; + + let exports = import_name_and_ordinal_vector + .iter() + .map(|(name, ordinal)| COFFShortExport { + name: name.to_string(), + ext_name: None, + symbol_name: None, + alias_target: None, + ordinal: ordinal.unwrap_or(0), + noname: ordinal.is_some(), + data: false, + private: false, + constant: false, + }) + .collect::>(); + + if let Err(error) = ar_archive_writer::write_import_library( + &mut file, + lib_name, + &exports, + machine, + !sess.target.is_like_msvc, + ) { + sess.dcx().fatal(format!( + "failed to create import library `{path}`: `{error}`", + path = output_path.display(), + )); + } + } } } diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index e16b77648d12..b6a4769e0311 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -23,19 +23,7 @@ pub(crate) fn maybe_codegen<'tcx>( match bin_op { BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => None, BinOp::Add | BinOp::AddUnchecked | BinOp::Sub | BinOp::SubUnchecked => None, - BinOp::Mul | BinOp::MulUnchecked => { - let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let ret_val = fx.lib_call( - "__multi3", - vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], - vec![AbiParam::new(types::I128)], - &args, - )[0]; - Some(CValue::by_val( - ret_val, - fx.layout_of(if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 }), - )) - } + BinOp::Mul | BinOp::MulUnchecked => None, BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), BinOp::Div | BinOp::Rem => { let name = match (bin_op, is_signed) { @@ -92,6 +80,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( match bin_op { BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(), + BinOp::Add | BinOp::Sub => None, BinOp::Mul if is_signed => { let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); let oflow = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32)); @@ -112,7 +101,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( let oflow = fx.bcx.ins().ireduce(types::I8, oflow); Some(CValue::by_val_pair(res, oflow, fx.layout_of(out_ty))) } - BinOp::Add | BinOp::Sub | BinOp::Mul => { + BinOp::Mul => { let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]); let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty)); let param_types = vec![ @@ -121,15 +110,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>( AbiParam::new(types::I128), ]; let args = [out_place.to_ptr().get_addr(fx), lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let name = match (bin_op, is_signed) { - (BinOp::Add, false) => "__rust_u128_addo", - (BinOp::Add, true) => "__rust_i128_addo", - (BinOp::Sub, false) => "__rust_u128_subo", - (BinOp::Sub, true) => "__rust_i128_subo", - (BinOp::Mul, false) => "__rust_u128_mulo", - _ => unreachable!(), - }; - fx.lib_call(name, param_types, vec![], &args); + fx.lib_call("__rust_u128_mulo", param_types, vec![], &args); Some(out_place.to_cvalue(fx)) } BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(), diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 093171399369..a1b29a422257 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -107,7 +107,7 @@ pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { return false; } - let tail = tcx.struct_tail_erasing_lifetimes(ty, ParamEnv::reveal_all()); + let tail = tcx.struct_tail_for_codegen(ty, ParamEnv::reveal_all()); match tail.kind() { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, diff --git a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs index f3b963200a0f..4154a62234c1 100644 --- a/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs +++ b/compiler/rustc_codegen_cranelift/src/compiler_builtins.rs @@ -38,18 +38,12 @@ builtin_functions! { register_functions_for_jit; // integers - fn __multi3(a: i128, b: i128) -> i128; fn __muloti4(n: i128, d: i128, oflow: &mut i32) -> i128; fn __udivti3(n: u128, d: u128) -> u128; fn __divti3(n: i128, d: i128) -> i128; fn __umodti3(n: u128, d: u128) -> u128; fn __modti3(n: i128, d: i128) -> i128; - fn __rust_u128_addo(a: u128, b: u128) -> (u128, bool); - fn __rust_i128_addo(a: i128, b: i128) -> (i128, bool); - fn __rust_u128_subo(a: u128, b: u128) -> (u128, bool); - fn __rust_i128_subo(a: i128, b: i128) -> (i128, bool); fn __rust_u128_mulo(a: u128, b: u128) -> (u128, bool); - fn __rust_i128_mulo(a: i128, b: i128) -> (i128, bool); // floats fn __floattisf(i: i128) -> f32; diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index a20faa2cad3a..cb003037c265 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -169,39 +169,6 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( } } - "llvm.x86.sse.add.ss" => { - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_add_ss&ig_expand=171 - intrinsic_args!(fx, args => (a, b); intrinsic); - - assert_eq!(a.layout(), b.layout()); - assert_eq!(a.layout(), ret.layout()); - let layout = a.layout(); - - let (_, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); - assert!(lane_ty.is_floating_point()); - let ret_lane_layout = fx.layout_of(lane_ty); - - ret.write_cvalue(fx, a); - - let a_lane = a.value_lane(fx, 0).load_scalar(fx); - let b_lane = b.value_lane(fx, 0).load_scalar(fx); - - let res = fx.bcx.ins().fadd(a_lane, b_lane); - - let res_lane = CValue::by_val(res, ret_lane_layout); - ret.place_lane(fx, 0).write_cvalue(fx, res_lane); - } - - "llvm.x86.sse.sqrt.ps" => { - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sqrt_ps&ig_expand=6245 - intrinsic_args!(fx, args => (a); intrinsic); - - // FIXME use vector instructions when possible - simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| { - fx.bcx.ins().sqrt(lane) - }); - } - "llvm.x86.sse.max.ps" => { // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_max_ps&ig_expand=4357 intrinsic_args!(fx, args => (a, b); intrinsic); @@ -744,117 +711,6 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( pack_instruction(fx, a, b, ret, PackSize::S16, PackWidth::Avx); } - "llvm.x86.fma.vfmaddsub.ps" - | "llvm.x86.fma.vfmaddsub.pd" - | "llvm.x86.fma.vfmaddsub.ps.256" - | "llvm.x86.fma.vfmaddsub.pd.256" => { - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fmaddsub_ps&ig_expand=3205 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fmaddsub_pd&ig_expand=3181 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fmaddsub_ps&ig_expand=3209 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fmaddsub_pd&ig_expand=3185 - intrinsic_args!(fx, args => (a, b, c); intrinsic); - - assert_eq!(a.layout(), b.layout()); - assert_eq!(a.layout(), c.layout()); - let layout = a.layout(); - - let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); - let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); - assert!(lane_ty.is_floating_point()); - assert!(ret_lane_ty.is_floating_point()); - assert_eq!(lane_count, ret_lane_count); - let ret_lane_layout = fx.layout_of(ret_lane_ty); - - for idx in 0..lane_count { - let a_lane = a.value_lane(fx, idx).load_scalar(fx); - let b_lane = b.value_lane(fx, idx).load_scalar(fx); - let c_lane = c.value_lane(fx, idx).load_scalar(fx); - - let mul = fx.bcx.ins().fmul(a_lane, b_lane); - let res = if idx & 1 == 0 { - fx.bcx.ins().fsub(mul, c_lane) - } else { - fx.bcx.ins().fadd(mul, c_lane) - }; - - let res_lane = CValue::by_val(res, ret_lane_layout); - ret.place_lane(fx, idx).write_cvalue(fx, res_lane); - } - } - - "llvm.x86.fma.vfmsubadd.ps" - | "llvm.x86.fma.vfmsubadd.pd" - | "llvm.x86.fma.vfmsubadd.ps.256" - | "llvm.x86.fma.vfmsubadd.pd.256" => { - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fmsubadd_ps&ig_expand=3325 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fmsubadd_pd&ig_expand=3301 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fmsubadd_ps&ig_expand=3329 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fmsubadd_pd&ig_expand=3305 - intrinsic_args!(fx, args => (a, b, c); intrinsic); - - assert_eq!(a.layout(), b.layout()); - assert_eq!(a.layout(), c.layout()); - let layout = a.layout(); - - let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); - let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); - assert!(lane_ty.is_floating_point()); - assert!(ret_lane_ty.is_floating_point()); - assert_eq!(lane_count, ret_lane_count); - let ret_lane_layout = fx.layout_of(ret_lane_ty); - - for idx in 0..lane_count { - let a_lane = a.value_lane(fx, idx).load_scalar(fx); - let b_lane = b.value_lane(fx, idx).load_scalar(fx); - let c_lane = c.value_lane(fx, idx).load_scalar(fx); - - let mul = fx.bcx.ins().fmul(a_lane, b_lane); - let res = if idx & 1 == 0 { - fx.bcx.ins().fadd(mul, c_lane) - } else { - fx.bcx.ins().fsub(mul, c_lane) - }; - - let res_lane = CValue::by_val(res, ret_lane_layout); - ret.place_lane(fx, idx).write_cvalue(fx, res_lane); - } - } - - "llvm.x86.fma.vfnmadd.ps" - | "llvm.x86.fma.vfnmadd.pd" - | "llvm.x86.fma.vfnmadd.ps.256" - | "llvm.x86.fma.vfnmadd.pd.256" => { - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fnmadd_ps&ig_expand=3391 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_fnmadd_pd&ig_expand=3367 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fnmadd_ps&ig_expand=3395 - // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_fnmadd_pd&ig_expand=3371 - intrinsic_args!(fx, args => (a, b, c); intrinsic); - - assert_eq!(a.layout(), b.layout()); - assert_eq!(a.layout(), c.layout()); - let layout = a.layout(); - - let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); - let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx); - assert!(lane_ty.is_floating_point()); - assert!(ret_lane_ty.is_floating_point()); - assert_eq!(lane_count, ret_lane_count); - let ret_lane_layout = fx.layout_of(ret_lane_ty); - - for idx in 0..lane_count { - let a_lane = a.value_lane(fx, idx).load_scalar(fx); - let b_lane = b.value_lane(fx, idx).load_scalar(fx); - let c_lane = c.value_lane(fx, idx).load_scalar(fx); - - let mul = fx.bcx.ins().fmul(a_lane, b_lane); - let neg_mul = fx.bcx.ins().fneg(mul); - let res = fx.bcx.ins().fadd(neg_mul, c_lane); - - let res_lane = CValue::by_val(res, ret_lane_layout); - ret.place_lane(fx, idx).write_cvalue(fx, res_lane); - } - } - "llvm.x86.sse42.crc32.32.8" | "llvm.x86.sse42.crc32.32.16" | "llvm.x86.sse42.crc32.32.32" diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 9d46d8d6dac3..21930fa2ddb4 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -12,6 +12,7 @@ #![warn(unused_lifetimes)] // tidy-alphabetical-end +extern crate ar_archive_writer; extern crate jobserver; #[macro_use] extern crate rustc_middle; @@ -190,9 +191,20 @@ impl CodegenBackend for CraneliftCodegenBackend { if sess.target.arch == "x86_64" && sess.target.os != "none" { // x86_64 mandates SSE2 support vec![Symbol::intern("fxsr"), sym::sse, Symbol::intern("sse2")] - } else if sess.target.arch == "aarch64" && sess.target.os != "none" { - // AArch64 mandates Neon support - vec![sym::neon] + } else if sess.target.arch == "aarch64" { + match &*sess.target.os { + "none" => vec![], + // On macOS the aes, sha2 and sha3 features are enabled by default and ring + // fails to compile on macOS when they are not present. + "macos" => vec![ + sym::neon, + Symbol::intern("aes"), + Symbol::intern("sha2"), + Symbol::intern("sha3"), + ], + // AArch64 mandates Neon support + _ => vec![sym::neon], + } } else { vec![] } diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 967aa53abbda..e09cd16e89a6 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -22,7 +22,7 @@ pub(crate) fn unsized_info<'tcx>( old_info: Option, ) -> Value { let (source, target) = - fx.tcx.struct_lockstep_tails_erasing_lifetimes(source, target, ParamEnv::reveal_all()); + fx.tcx.struct_lockstep_tails_for_codegen(source, target, ParamEnv::reveal_all()); match (&source.kind(), &target.kind()) { (&ty::Array(_, len), &ty::Slice(_)) => fx .bcx diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 1aa28daeafc7..8eb2095e5234 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -677,8 +677,10 @@ impl<'tcx> CPlace<'tcx> { let to_addr = to_ptr.get_addr(fx); let src_layout = from.1; let size = dst_layout.size.bytes(); - let src_align = src_layout.align.abi.bytes() as u8; - let dst_align = dst_layout.align.abi.bytes() as u8; + // `emit_small_memory_copy` uses `u8` for alignments, just use the maximum + // alignment that fits in a `u8` if the actual alignment is larger. + let src_align = src_layout.align.abi.bytes().try_into().unwrap_or(128); + let dst_align = dst_layout.align.abi.bytes().try_into().unwrap_or(128); fx.bcx.emit_small_memory_copy( fx.target_config, to_addr, diff --git a/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.lock b/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.lock index d6ec1f87d010..771f2f18dce7 100644 --- a/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.lock +++ b/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.lock @@ -50,7 +50,7 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.109" +version = "0.1.118" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f11973008a8cf741fe6d22f339eba21fd0ca81e2760a769ba8243ed6c21edd7e" dependencies = [ diff --git a/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml b/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml index e46699236238..05503128f2af 100644 --- a/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml +++ b/compiler/rustc_codegen_gcc/build_system/build_sysroot/Cargo.toml @@ -6,9 +6,7 @@ resolver = "2" [dependencies] core = { path = "./sysroot_src/library/core" } -# TODO: after the sync, revert to using version 0.1. -# compiler_builtins = "0.1" -compiler_builtins = "=0.1.109" +compiler_builtins = "0.1" alloc = { path = "./sysroot_src/library/alloc" } std = { path = "./sysroot_src/library/std", features = ["panic_unwind", "backtrace"] } test = { path = "./sysroot_src/library/test" } diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index 5a7ddc4cd7fa..9f096e902201 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -430,6 +430,7 @@ pub enum E2 { V4, } +#[allow(unreachable_patterns)] fn check_niche_behavior () { if let E1::V2 { .. } = (E1::V1 { f: true }) { intrinsics::abort(); diff --git a/compiler/rustc_codegen_gcc/src/archive.rs b/compiler/rustc_codegen_gcc/src/archive.rs index 36f5e0f8094e..0cee05f1cea3 100644 --- a/compiler/rustc_codegen_gcc/src/archive.rs +++ b/compiler/rustc_codegen_gcc/src/archive.rs @@ -1,9 +1,8 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use rustc_codegen_ssa::back::archive::{ ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, }; -use rustc_session::cstore::DllImport; use rustc_session::Session; pub(crate) struct ArArchiveBuilderBuilder; @@ -17,10 +16,9 @@ impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { &self, _sess: &Session, _lib_name: &str, - _dll_imports: &[DllImport], - _tmpdir: &Path, - _is_direct_dependency: bool, - ) -> PathBuf { + _import_name_and_ordinal_vector: Vec<(String, Option)>, + _output_path: &Path, + ) { unimplemented!("creating dll imports is not yet supported"); } } diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index e521551304ef..5fdf2680aac8 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -75,7 +75,7 @@ pub fn from_fn_attrs<'gcc, 'tcx>( let function_features = codegen_fn_attrs .target_features .iter() - .map(|features| features.as_str()) + .map(|features| features.name.as_str()) .collect::>(); if let Some(features) = check_tied_features( diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 8bb90efe6fb7..5308ccdb6146 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -65,8 +65,8 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec { fn llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type; fn llvm_cconv(&self) -> llvm::CallConv; - fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value); + + /// Apply attributes to a function declaration/definition. + fn apply_attrs_llfn( + &self, + cx: &CodegenCx<'ll, 'tcx>, + llfn: &'ll Value, + instance: Option>, + ); + + /// Apply attributes to a function call. fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value); } @@ -396,7 +406,12 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { self.conv.into() } - fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value) { + fn apply_attrs_llfn( + &self, + cx: &CodegenCx<'ll, 'tcx>, + llfn: &'ll Value, + instance: Option>, + ) { let mut func_attrs = SmallVec::<[_; 3]>::new(); if self.ret.layout.abi.is_uninhabited() { func_attrs.push(llvm::AttributeKind::NoReturn.create_attr(cx.llcx)); @@ -477,6 +492,11 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } } } + + // If the declaration has an associated instance, compute extra attributes based on that. + if let Some(instance) = instance { + llfn_attrs_from_instance(cx, llfn, instance); + } } fn apply_attrs_callsite(&self, bx: &mut Builder<'_, 'll, 'tcx>, callsite: &'ll Value) { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index ad38814a68b6..fde95104093e 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -324,9 +324,10 @@ fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute { llvm::CreateAttrStringValue(llcx, "alloc-family", "__rust_alloc") } +/// Helper for `FnAbi::apply_attrs_llfn`: /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// attributes. -pub fn from_fn_attrs<'ll, 'tcx>( +pub fn llfn_attrs_from_instance<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::Instance<'tcx>, @@ -496,7 +497,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( to_add.extend(tune_cpu_attr(cx)); let function_features = - codegen_fn_attrs.target_features.iter().map(|f| f.as_str()).collect::>(); + codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::>(); if let Some(f) = llvm_util::check_tied_features( cx.tcx.sess, diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 2f5a864d70ea..a2ab19ac800b 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -1,21 +1,19 @@ //! A helper class for dealing with static archives -use std::ffi::{c_char, c_void, CStr, CString, OsString}; +use std::ffi::{c_char, c_void, CStr, CString}; use std::path::{Path, PathBuf}; -use std::{env, io, mem, ptr, str}; +use std::{io, mem, ptr, str}; use rustc_codegen_ssa::back::archive::{ - try_extract_macho_fat_archive, ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, - ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, DEFAULT_OBJECT_READER, + create_mingw_dll_import_lib, try_extract_macho_fat_archive, ArArchiveBuilder, + ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, + DEFAULT_OBJECT_READER, }; -use rustc_session::cstore::DllImport; +use rustc_codegen_ssa::common; use rustc_session::Session; use tracing::trace; -use crate::common; -use crate::errors::{ - DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, ErrorWritingDEFFile, -}; +use crate::errors::ErrorCreatingImportLibrary; use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; @@ -121,116 +119,21 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { &self, sess: &Session, lib_name: &str, - dll_imports: &[DllImport], - tmpdir: &Path, - is_direct_dependency: bool, - ) -> PathBuf { - let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; - let output_path = tmpdir.join(format!("{lib_name}{name_suffix}.lib")); - - let target = &sess.target; - let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(target); - - let import_name_and_ordinal_vector: Vec<(String, Option)> = dll_imports - .iter() - .map(|import: &DllImport| { - if sess.target.arch == "x86" { - ( - common::i686_decorated_name(import, mingw_gnu_toolchain, false), - import.ordinal(), - ) - } else { - (import.name.to_string(), import.ordinal()) - } - }) - .collect(); - - if mingw_gnu_toolchain { + import_name_and_ordinal_vector: Vec<(String, Option)>, + output_path: &Path, + ) { + if common::is_mingw_gnu_toolchain(&sess.target) { // The binutils linker used on -windows-gnu targets cannot read the import // libraries generated by LLVM: in our attempts, the linker produced an .EXE // that loaded but crashed with an AV upon calling one of the imported // functions. Therefore, use binutils to create the import library instead, // by writing a .DEF file to the temp dir and calling binutils's dlltool. - let def_file_path = tmpdir.join(format!("{lib_name}{name_suffix}.def")); - - let def_file_content = format!( - "EXPORTS\n{}", - import_name_and_ordinal_vector - .into_iter() - .map(|(name, ordinal)| { - match ordinal { - Some(n) => format!("{name} @{n} NONAME"), - None => name, - } - }) - .collect::>() - .join("\n") + create_mingw_dll_import_lib( + sess, + lib_name, + import_name_and_ordinal_vector, + output_path, ); - - match std::fs::write(&def_file_path, def_file_content) { - Ok(_) => {} - Err(e) => { - sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e }); - } - }; - - // --no-leading-underscore: For the `import_name_type` feature to work, we need to be - // able to control the *exact* spelling of each of the symbols that are being imported: - // hence we don't want `dlltool` adding leading underscores automatically. - let dlltool = find_binutils_dlltool(sess); - let temp_prefix = { - let mut path = PathBuf::from(&output_path); - path.pop(); - path.push(lib_name); - path - }; - // dlltool target architecture args from: - // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69 - let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() { - "x86_64" => ("i386:x86-64", "--64"), - "x86" => ("i386", "--32"), - "aarch64" => ("arm64", "--64"), - "arm" => ("arm", "--32"), - _ => panic!("unsupported arch {}", sess.target.arch), - }; - let mut dlltool_cmd = std::process::Command::new(&dlltool); - dlltool_cmd - .arg("-d") - .arg(def_file_path) - .arg("-D") - .arg(lib_name) - .arg("-l") - .arg(&output_path) - .arg("-m") - .arg(dlltool_target_arch) - .arg("-f") - .arg(dlltool_target_bitness) - .arg("--no-leading-underscore") - .arg("--temp-prefix") - .arg(temp_prefix); - - match dlltool_cmd.output() { - Err(e) => { - sess.dcx().emit_fatal(ErrorCallingDllTool { - dlltool_path: dlltool.to_string_lossy(), - error: e, - }); - } - // dlltool returns '0' on failure, so check for error output instead. - Ok(output) if !output.stderr.is_empty() => { - sess.dcx().emit_fatal(DlltoolFailImportLibrary { - dlltool_path: dlltool.to_string_lossy(), - dlltool_args: dlltool_cmd - .get_args() - .map(|arg| arg.to_string_lossy()) - .collect::>() - .join(" "), - stdout: String::from_utf8_lossy(&output.stdout), - stderr: String::from_utf8_lossy(&output.stderr), - }) - } - _ => {} - } } else { // we've checked for \0 characters in the library name already let dll_name_z = CString::new(lib_name).unwrap(); @@ -242,9 +145,9 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { trace!(" output_path {}", output_path.display()); trace!( " import names: {}", - dll_imports + import_name_and_ordinal_vector .iter() - .map(|import| import.name.to_string()) + .map(|(name, _ordinal)| name.clone()) .collect::>() .join(", "), ); @@ -281,9 +184,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { error: llvm::last_error().unwrap_or("unknown LLVM error".to_string()), }); } - }; - - output_path + } } } @@ -457,39 +358,3 @@ impl<'a> LlvmArchiveBuilder<'a> { fn string_to_io_error(s: String) -> io::Error { io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}")) } - -fn find_binutils_dlltool(sess: &Session) -> OsString { - assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); - if let Some(dlltool_path) = &sess.opts.cg.dlltool { - return dlltool_path.clone().into_os_string(); - } - - let tool_name: OsString = if sess.host.options.is_like_windows { - // If we're compiling on Windows, always use "dlltool.exe". - "dlltool.exe" - } else { - // On other platforms, use the architecture-specific name. - match sess.target.arch.as_ref() { - "x86_64" => "x86_64-w64-mingw32-dlltool", - "x86" => "i686-w64-mingw32-dlltool", - "aarch64" => "aarch64-w64-mingw32-dlltool", - - // For non-standard architectures (e.g., aarch32) fallback to "dlltool". - _ => "dlltool", - } - } - .into(); - - // NOTE: it's not clear how useful it is to explicitly search PATH. - for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { - let full_path = dir.join(&tool_name); - if full_path.is_file() { - return full_path.into_os_string(); - } - } - - // The user didn't specify the location of the dlltool binary, and we weren't able - // to find the appropriate one on the PATH. Just return the name of the tool - // and let the invocation fail with a hopefully useful error message. - tool_name -} diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 5a7909d15113..a1f2433ab6f3 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -95,11 +95,14 @@ pub fn write_output_file<'ll>( } } -pub fn create_informational_target_machine(sess: &Session) -> OwnedTargetMachine { +pub fn create_informational_target_machine( + sess: &Session, + only_base_features: bool, +) -> OwnedTargetMachine { let config = TargetMachineFactoryConfig { split_dwarf_file: None, output_obj_file: None }; // Can't use query system here quite yet because this function is invoked before the query // system/tcx is set up. - let features = llvm_util::global_llvm_features(sess, false); + let features = llvm_util::global_llvm_features(sess, false, only_base_features); target_machine_factory(sess, config::OptLevel::No, &features)(config) .unwrap_or_else(|err| llvm_err(sess.dcx(), err).raise()) } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 77e51f941508..0f44c5dba5e5 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -12,7 +12,8 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ - FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout, + FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, LayoutError, LayoutOfHelpers, + TyAndLayout, }; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_sanitizers::{cfi, kcfi}; @@ -531,7 +532,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { #[instrument(level = "trace", skip(self))] fn load_operand(&mut self, place: PlaceRef<'tcx, &'ll Value>) -> OperandRef<'tcx, &'ll Value> { if place.layout.is_unsized() { - let tail = self.tcx.struct_tail_with_normalize(place.layout.ty, |ty| ty, || {}); + let tail = self.tcx.struct_tail_for_codegen(place.layout.ty, self.param_env()); if matches!(tail.kind(), ty::Foreign(..)) { // Unsized locals and, at least conceptually, even unsized arguments must be copied // around, which requires dynamically determining their size. Therefore, we cannot diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index c913bdebaaa7..663c5be46e5e 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -4,13 +4,14 @@ //! and methods are represented as just a fn ptr and not a full //! closure. +use rustc_codegen_ssa::common; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; use tracing::debug; use crate::context::CodegenCx; +use crate::llvm; use crate::value::Value; -use crate::{attributes, common, llvm}; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -46,7 +47,7 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> } else { let instance_def_id = instance.def_id(); let llfn = if tcx.sess.target.arch == "x86" - && let Some(dllimport) = common::get_dllimport(tcx, instance_def_id, sym) + && let Some(dllimport) = crate::common::get_dllimport(tcx, instance_def_id, sym) { // Fix for https://github.com/rust-lang/rust/issues/104453 // On x86 Windows, LLVM uses 'L' as the prefix for any private @@ -77,8 +78,6 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> }; debug!("get_fn: not casting pointer!"); - attributes::from_fn_attrs(cx, llfn, instance); - // Apply an appropriate linkage/visibility value to our item that we // just declared. // diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 197bbb9ddbf9..65f974c56893 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -1,7 +1,5 @@ //! Code that is useful in various codegen modules. -use std::fmt::Write; - use libc::{c_char, c_uint}; use rustc_ast::Mutability; use rustc_codegen_ssa::traits::*; @@ -10,9 +8,8 @@ use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; use rustc_middle::ty::TyCtxt; -use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType}; +use rustc_session::cstore::DllImport; use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer}; -use rustc_target::spec::Target; use tracing::debug; use crate::consts::const_alloc_to_llvm; @@ -379,64 +376,3 @@ pub(crate) fn get_dllimport<'tcx>( tcx.native_library(id) .and_then(|lib| lib.dll_imports.iter().find(|di| di.name.as_str() == name)) } - -pub(crate) fn is_mingw_gnu_toolchain(target: &Target) -> bool { - target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty() -} - -pub(crate) fn i686_decorated_name( - dll_import: &DllImport, - mingw: bool, - disable_name_mangling: bool, -) -> String { - let name = dll_import.name.as_str(); - - let (add_prefix, add_suffix) = match dll_import.import_name_type { - Some(PeImportNameType::NoPrefix) => (false, true), - Some(PeImportNameType::Undecorated) => (false, false), - _ => (true, true), - }; - - // Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__). - let mut decorated_name = String::with_capacity(name.len() + 6); - - if disable_name_mangling { - // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled. - decorated_name.push('\x01'); - } - - let prefix = if add_prefix && dll_import.is_fn { - match dll_import.calling_convention { - DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None, - DllCallingConvention::Stdcall(_) => (!mingw - || dll_import.import_name_type == Some(PeImportNameType::Decorated)) - .then_some('_'), - DllCallingConvention::Fastcall(_) => Some('@'), - } - } else if !dll_import.is_fn && !mingw { - // For static variables, prefix with '_' on MSVC. - Some('_') - } else { - None - }; - if let Some(prefix) = prefix { - decorated_name.push(prefix); - } - - decorated_name.push_str(name); - - if add_suffix && dll_import.is_fn { - match dll_import.calling_convention { - DllCallingConvention::C => {} - DllCallingConvention::Stdcall(arg_list_size) - | DllCallingConvention::Fastcall(arg_list_size) => { - write!(&mut decorated_name, "@{arg_list_size}").unwrap(); - } - DllCallingConvention::Vectorcall(arg_list_size) => { - write!(&mut decorated_name, "@@{arg_list_size}").unwrap(); - } - } - } - - decorated_name -} diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index c3ea4a18a71b..75b298f14ca1 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -1,5 +1,6 @@ use std::ops::Range; +use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -18,7 +19,7 @@ use rustc_target::abi::{ }; use tracing::{debug, instrument, trace}; -use crate::common::{self, CodegenCx}; +use crate::common::CodegenCx; use crate::errors::{ InvalidMinimumAlignmentNotPowerOfTwo, InvalidMinimumAlignmentTooLarge, SymbolAlreadyDefined, }; @@ -195,7 +196,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( g2 } } else if cx.tcx.sess.target.arch == "x86" - && let Some(dllimport) = common::get_dllimport(cx.tcx, def_id, sym) + && let Some(dllimport) = crate::common::get_dllimport(cx.tcx, def_id, sym) { cx.declare_global( &common::i686_decorated_name( diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ea930421b586..dd3f39eceadb 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -149,7 +149,7 @@ pub unsafe fn create_module<'ll>( // Ensure the data-layout values hardcoded remain the defaults. { - let tm = crate::back::write::create_informational_target_machine(tcx.sess); + let tm = crate::back::write::create_informational_target_machine(tcx.sess, false); unsafe { llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, &tm); } @@ -775,10 +775,10 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> ptr); - ifn!("llvm.powi.f16", fn(t_f16, t_i32) -> t_f16); - ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.powi.f128", fn(t_f128, t_i32) -> t_f128); + ifn!("llvm.powi.f16.i32", fn(t_f16, t_i32) -> t_f16); + ifn!("llvm.powi.f32.i32", fn(t_f32, t_i32) -> t_f32); + ifn!("llvm.powi.f64.i32", fn(t_f64, t_i32) -> t_f64); + ifn!("llvm.powi.f128.i32", fn(t_f128, t_i32) -> t_f128); ifn!("llvm.pow.f16", fn(t_f16, t_f16) -> t_f16); ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index d51e15d12e2f..e542aa96e8a2 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -62,7 +62,7 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, pointee_ty: Ty<'tcx>, ) -> Option { - let pointee_tail_ty = cx.tcx.struct_tail_erasing_lifetimes(pointee_ty, cx.param_env()); + let pointee_tail_ty = cx.tcx.struct_tail_for_codegen(pointee_ty, cx.param_env()); let layout = cx.layout_of(pointee_tail_ty); trace!( "fat_pointer_kind: {:?} has layout {:?} (is_unsized? {})", diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index c4887464e19c..2aa349b27823 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -137,7 +137,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::Visibility::Default, fn_abi.llvm_type(self), ); - fn_abi.apply_attrs_llfn(self, llfn); + fn_abi.apply_attrs_llfn(self, llfn, instance); if self.tcx.sess.is_sanitizer_cfi_enabled() { if let Some(instance) = instance { diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index a3957bc52a50..7e53d32ce8cd 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::ffi::CString; use std::path::Path; @@ -71,28 +70,6 @@ pub(crate) struct InvalidMinimumAlignmentTooLarge { #[diag(codegen_llvm_sanitizer_memtag_requires_mte)] pub(crate) struct SanitizerMemtagRequiresMte; -#[derive(Diagnostic)] -#[diag(codegen_llvm_error_writing_def_file)] -pub(crate) struct ErrorWritingDEFFile { - pub error: std::io::Error, -} - -#[derive(Diagnostic)] -#[diag(codegen_llvm_error_calling_dlltool)] -pub(crate) struct ErrorCallingDllTool<'a> { - pub dlltool_path: Cow<'a, str>, - pub error: std::io::Error, -} - -#[derive(Diagnostic)] -#[diag(codegen_llvm_dlltool_fail_import_library)] -pub(crate) struct DlltoolFailImportLibrary<'a> { - pub dlltool_path: Cow<'a, str>, - pub dlltool_args: String, - pub stdout: Cow<'a, str>, - pub stderr: Cow<'a, str>, -} - #[derive(Diagnostic)] #[diag(codegen_llvm_dynamic_linking_with_lto)] #[note] diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 040de1c7dd71..57d5f6fdf503 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -35,10 +35,10 @@ fn get_simple_intrinsic<'ll>( sym::sqrtf64 => "llvm.sqrt.f64", sym::sqrtf128 => "llvm.sqrt.f128", - sym::powif16 => "llvm.powi.f16", - sym::powif32 => "llvm.powi.f32", - sym::powif64 => "llvm.powi.f64", - sym::powif128 => "llvm.powi.f128", + sym::powif16 => "llvm.powi.f16.i32", + sym::powif32 => "llvm.powi.f32.i32", + sym::powif64 => "llvm.powi.f64.i32", + sym::powif128 => "llvm.powi.f128.i32", sym::sinf16 => "llvm.sin.f16", sym::sinf32 => "llvm.sin.f32", diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 41e9cfd1066b..518a86e0cb06 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -269,7 +269,7 @@ impl CodegenBackend for LlvmCodegenBackend { fn provide(&self, providers: &mut Providers) { providers.global_backend_features = - |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true) + |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true, false) } fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { @@ -434,7 +434,7 @@ impl ModuleLlvm { ModuleLlvm { llmod_raw, llcx, - tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)), + tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess, false)), } } } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index dc21b92a95f7..9fd8ca43789d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -8,6 +8,7 @@ use libc::c_int; use rustc_codegen_ssa::base::wants_wasm_eh; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; +use rustc_data_structures::unord::UnordSet; use rustc_fs_util::path_to_c_string; use rustc_middle::bug; use rustc_session::config::{PrintKind, PrintRequest}; @@ -239,40 +240,8 @@ pub fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> LLVMFeature<'a> { } // In LLVM neon implicitly enables fp, but we manually enable // neon when a feature only implicitly enables fp - ("aarch64", "f32mm") => { - LLVMFeature::with_dependency("f32mm", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "f64mm") => { - LLVMFeature::with_dependency("f64mm", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "fhm") => { - LLVMFeature::with_dependency("fp16fml", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "fp16") => { - LLVMFeature::with_dependency("fullfp16", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "jsconv") => { - LLVMFeature::with_dependency("jsconv", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve") => { - LLVMFeature::with_dependency("sve", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2") => { - LLVMFeature::with_dependency("sve2", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-aes") => { - LLVMFeature::with_dependency("sve2-aes", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-sm4") => { - LLVMFeature::with_dependency("sve2-sm4", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-sha3") => { - LLVMFeature::with_dependency("sve2-sha3", TargetFeatureFoldStrength::EnableOnly("neon")) - } - ("aarch64", "sve2-bitperm") => LLVMFeature::with_dependency( - "sve2-bitperm", - TargetFeatureFoldStrength::EnableOnly("neon"), - ), + ("aarch64", "fhm") => LLVMFeature::new("fp16fml"), + ("aarch64", "fp16") => LLVMFeature::new("fullfp16"), // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single feature called // `fast-unaligned-access`. In LLVM 19, it was split back out. ("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => { @@ -308,11 +277,53 @@ pub fn check_tied_features( /// Used to generate cfg variables and apply features /// Must express features in the way Rust understands them pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { - let target_machine = create_informational_target_machine(sess); + let mut features = vec![]; + + // Add base features for the target + let target_machine = create_informational_target_machine(sess, true); + features.extend( + sess.target + .supported_target_features() + .iter() + .filter(|(feature, _, _)| { + // skip checking special features, as LLVM may not understands them + if RUSTC_SPECIAL_FEATURES.contains(feature) { + return true; + } + // check that all features in a given smallvec are enabled + for llvm_feature in to_llvm_features(sess, feature) { + let cstr = SmallCStr::new(llvm_feature); + if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } { + return false; + } + } + true + }) + .map(|(feature, _, _)| Symbol::intern(feature)), + ); + + // Add enabled features + for (enabled, feature) in + sess.opts.cg.target_feature.split(',').filter_map(|s| match s.chars().next() { + Some('+') => Some((true, Symbol::intern(&s[1..]))), + Some('-') => Some((false, Symbol::intern(&s[1..]))), + _ => None, + }) + { + if enabled { + features.extend(sess.target.implied_target_features(std::iter::once(feature))); + } else { + features.retain(|f| { + !sess.target.implied_target_features(std::iter::once(*f)).contains(&feature) + }); + } + } + + // Filter enabled features based on feature gates sess.target .supported_target_features() .iter() - .filter_map(|&(feature, gate)| { + .filter_map(|&(feature, gate, _)| { if sess.is_nightly_build() || allow_unstable || gate.is_stable() { Some(feature) } else { @@ -320,18 +331,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec { } }) .filter(|feature| { - // skip checking special features, as LLVM may not understands them - if RUSTC_SPECIAL_FEATURES.contains(feature) { - return true; - } - // check that all features in a given smallvec are enabled - for llvm_feature in to_llvm_features(sess, feature) { - let cstr = SmallCStr::new(llvm_feature); - if !unsafe { llvm::LLVMRustHasFeature(&target_machine, cstr.as_ptr()) } { - return false; - } - } - true + RUSTC_SPECIAL_FEATURES.contains(feature) || features.contains(&Symbol::intern(feature)) }) .map(|feature| Symbol::intern(feature)) .collect() @@ -386,7 +386,7 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach .target .supported_target_features() .iter() - .map(|(feature, _gate)| { + .map(|(feature, _gate, _implied)| { // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings. let llvm_feature = to_llvm_features(sess, *feature).llvm_feature_name; let desc = @@ -440,7 +440,7 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach pub(crate) fn print(req: &PrintRequest, mut out: &mut String, sess: &Session) { require_inited(); - let tm = create_informational_target_machine(sess); + let tm = create_informational_target_machine(sess, false); match req.kind { PrintKind::TargetCPUs => { // SAFETY generate a C compatible string from a byte slice to pass @@ -488,7 +488,11 @@ pub fn target_cpu(sess: &Session) -> &str { /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`, /// `--target` and similar). -pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec { +pub(crate) fn global_llvm_features( + sess: &Session, + diagnostics: bool, + only_base_features: bool, +) -> Vec { // Features that come earlier are overridden by conflicting features later in the string. // Typically we'll want more explicit settings to override the implicit ones, so: // @@ -548,94 +552,124 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec return None, - Some(c @ ('+' | '-')) => c, - Some(_) => { - if diagnostics { - sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); + if !only_base_features { + let supported_features = sess.target.supported_target_features(); + let (llvm_major, _, _) = get_version(); + let mut featsmap = FxHashMap::default(); + + // insert implied features + let mut all_rust_features = vec![]; + for feature in sess.opts.cg.target_feature.split(',') { + match feature.strip_prefix('+') { + Some(feature) => all_rust_features.extend( + UnordSet::from( + sess.target + .implied_target_features(std::iter::once(Symbol::intern(feature))), + ) + .to_sorted_stable_ord() + .iter() + .map(|s| format!("+{}", s.as_str())), + ), + _ => all_rust_features.push(feature.to_string()), + } + } + + let feats = all_rust_features + .iter() + .filter_map(|s| { + let enable_disable = match s.chars().next() { + None => return None, + Some(c @ ('+' | '-')) => c, + Some(_) => { + if diagnostics { + sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); + } + return None; } + }; + + let feature = backend_feature_name(sess, s)?; + // Warn against use of LLVM specific feature names and unstable features on the CLI. + if diagnostics { + let feature_state = supported_features.iter().find(|&&(v, _, _)| v == feature); + if feature_state.is_none() { + let rust_feature = + supported_features.iter().find_map(|&(rust_feature, _, _)| { + let llvm_features = to_llvm_features(sess, rust_feature); + if llvm_features.contains(feature) + && !llvm_features.contains(rust_feature) + { + Some(rust_feature) + } else { + None + } + }); + let unknown_feature = if let Some(rust_feature) = rust_feature { + UnknownCTargetFeature { + feature, + rust_feature: PossibleFeature::Some { rust_feature }, + } + } else { + UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } + }; + sess.dcx().emit_warn(unknown_feature); + } else if feature_state + .is_some_and(|(_name, feature_gate, _implied)| !feature_gate.is_stable()) + { + // An unstable feature. Warn about using it. + sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + } + } + + if diagnostics { + // FIXME(nagisa): figure out how to not allocate a full hashset here. + featsmap.insert(feature, enable_disable == '+'); + } + + // rustc-specific features do not get passed down to LLVM… + if RUSTC_SPECIFIC_FEATURES.contains(&feature) { return None; } - }; - let feature = backend_feature_name(sess, s)?; - // Warn against use of LLVM specific feature names and unstable features on the CLI. - if diagnostics { - let feature_state = supported_features.iter().find(|&&(v, _)| v == feature); - if feature_state.is_none() { - let rust_feature = supported_features.iter().find_map(|&(rust_feature, _)| { - let llvm_features = to_llvm_features(sess, rust_feature); - if llvm_features.contains(feature) && !llvm_features.contains(rust_feature) - { - Some(rust_feature) - } else { - None - } - }); - let unknown_feature = if let Some(rust_feature) = rust_feature { - UnknownCTargetFeature { - feature, - rust_feature: PossibleFeature::Some { rust_feature }, - } - } else { - UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } - }; - sess.dcx().emit_warn(unknown_feature); - } else if feature_state - .is_some_and(|(_name, feature_gate)| !feature_gate.is_stable()) - { - // An unstable feature. Warn about using it. - sess.dcx().emit_warn(UnstableCTargetFeature { feature }); + // if the target-feature is "backchain" and LLVM version is greater than 18 + // then we also need to add "+backchain" to the target-features attribute. + // otherwise, we will only add the naked `backchain` attribute to the attribute-group. + if feature == "backchain" && llvm_major < 18 { + return None; } - } + // ... otherwise though we run through `to_llvm_features` when + // passing requests down to LLVM. This means that all in-language + // features also work on the command line instead of having two + // different names when the LLVM name and the Rust name differ. + let llvm_feature = to_llvm_features(sess, feature); - if diagnostics { - // FIXME(nagisa): figure out how to not allocate a full hashset here. - featsmap.insert(feature, enable_disable == '+'); - } - - // rustc-specific features do not get passed down to LLVM… - if RUSTC_SPECIFIC_FEATURES.contains(&feature) { - return None; - } - - // if the target-feature is "backchain" and LLVM version is greater than 18 - // then we also need to add "+backchain" to the target-features attribute. - // otherwise, we will only add the naked `backchain` attribute to the attribute-group. - if feature == "backchain" && llvm_major < 18 { - return None; - } - // ... otherwise though we run through `to_llvm_features` when - // passing requests down to LLVM. This means that all in-language - // features also work on the command line instead of having two - // different names when the LLVM name and the Rust name differ. - let llvm_feature = to_llvm_features(sess, feature); - - Some( - std::iter::once(format!("{}{}", enable_disable, llvm_feature.llvm_feature_name)) - .chain(llvm_feature.dependency.into_iter().filter_map(move |feat| { - match (enable_disable, feat) { + Some( + std::iter::once(format!( + "{}{}", + enable_disable, llvm_feature.llvm_feature_name + )) + .chain(llvm_feature.dependency.into_iter().filter_map( + move |feat| match (enable_disable, feat) { ('-' | '+', TargetFeatureFoldStrength::Both(f)) | ('+', TargetFeatureFoldStrength::EnableOnly(f)) => { Some(format!("{enable_disable}{f}")) } _ => None, - } - })), - ) - }) - .flatten(); - features.extend(feats); + }, + )), + ) + }) + .flatten(); + features.extend(feats); + + if diagnostics && let Some(f) = check_tied_features(sess, &featsmap) { + sess.dcx().emit_err(TargetFeatureDisableOrEnable { + features: f, + span: None, + missing_features: None, + }); + } + } // -Zfixed-x18 if sess.opts.unstable_opts.fixed_x18 { @@ -646,14 +680,6 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { fn predefine_static( @@ -87,8 +87,6 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { debug!("predefine_fn: instance = {:?}", instance); - attributes::from_fn_attrs(self, lldecl, instance); - unsafe { if self.should_assume_dso_local(lldecl, false) { llvm::LLVMRustSetDSOLocal(lldecl, true); diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 2767ad5ec9ce..6e1c323cbd06 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start -ar_archive_writer = "0.3.0" +ar_archive_writer = "0.3.3" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" cc = "1.0.90" diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 57d789aef80c..80f25d42a085 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -24,8 +24,19 @@ codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$e codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error} +codegen_ssa_dlltool_fail_import_library = + Dlltool could not create import library with {$dlltool_path} {$dlltool_args}: + {$stdout} + {$stderr} + +codegen_ssa_error_calling_dlltool = + Error calling dlltool '{$dlltool_path}': {$error} + codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error} +codegen_ssa_error_writing_def_file = + Error writing .DEF file: {$error} + codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 4f93b7b23c6e..ce55d99f506d 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -1,4 +1,6 @@ +use std::env; use std::error::Error; +use std::ffi::OsString; use std::fs::{self, File}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; @@ -9,7 +11,6 @@ use object::read::archive::ArchiveFile; use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; -use rustc_session::cstore::DllImport; use rustc_session::Session; use rustc_span::symbol::Symbol; use tempfile::Builder as TempFileBuilder; @@ -17,6 +18,7 @@ use tempfile::Builder as TempFileBuilder; use super::metadata::search_for_section; // Re-exporting for rustc_codegen_llvm::back::archive pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind}; +use crate::errors::{DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorWritingDEFFile}; pub trait ArchiveBuilderBuilder { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box; @@ -30,10 +32,9 @@ pub trait ArchiveBuilderBuilder { &self, sess: &Session, lib_name: &str, - dll_imports: &[DllImport], - tmpdir: &Path, - is_direct_dependency: bool, - ) -> PathBuf; + import_name_and_ordinal_vector: Vec<(String, Option)>, + output_path: &Path, + ); fn extract_bundled_libs<'a>( &'a self, @@ -72,6 +73,130 @@ pub trait ArchiveBuilderBuilder { } } +pub fn create_mingw_dll_import_lib( + sess: &Session, + lib_name: &str, + import_name_and_ordinal_vector: Vec<(String, Option)>, + output_path: &Path, +) { + let def_file_path = output_path.with_extension("def"); + + let def_file_content = format!( + "EXPORTS\n{}", + import_name_and_ordinal_vector + .into_iter() + .map(|(name, ordinal)| { + match ordinal { + Some(n) => format!("{name} @{n} NONAME"), + None => name, + } + }) + .collect::>() + .join("\n") + ); + + match std::fs::write(&def_file_path, def_file_content) { + Ok(_) => {} + Err(e) => { + sess.dcx().emit_fatal(ErrorWritingDEFFile { error: e }); + } + }; + + // --no-leading-underscore: For the `import_name_type` feature to work, we need to be + // able to control the *exact* spelling of each of the symbols that are being imported: + // hence we don't want `dlltool` adding leading underscores automatically. + let dlltool = find_binutils_dlltool(sess); + let temp_prefix = { + let mut path = PathBuf::from(&output_path); + path.pop(); + path.push(lib_name); + path + }; + // dlltool target architecture args from: + // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69 + let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch.as_ref() { + "x86_64" => ("i386:x86-64", "--64"), + "x86" => ("i386", "--32"), + "aarch64" => ("arm64", "--64"), + "arm" => ("arm", "--32"), + _ => panic!("unsupported arch {}", sess.target.arch), + }; + let mut dlltool_cmd = std::process::Command::new(&dlltool); + dlltool_cmd + .arg("-d") + .arg(def_file_path) + .arg("-D") + .arg(lib_name) + .arg("-l") + .arg(&output_path) + .arg("-m") + .arg(dlltool_target_arch) + .arg("-f") + .arg(dlltool_target_bitness) + .arg("--no-leading-underscore") + .arg("--temp-prefix") + .arg(temp_prefix); + + match dlltool_cmd.output() { + Err(e) => { + sess.dcx().emit_fatal(ErrorCallingDllTool { + dlltool_path: dlltool.to_string_lossy(), + error: e, + }); + } + // dlltool returns '0' on failure, so check for error output instead. + Ok(output) if !output.stderr.is_empty() => { + sess.dcx().emit_fatal(DlltoolFailImportLibrary { + dlltool_path: dlltool.to_string_lossy(), + dlltool_args: dlltool_cmd + .get_args() + .map(|arg| arg.to_string_lossy()) + .collect::>() + .join(" "), + stdout: String::from_utf8_lossy(&output.stdout), + stderr: String::from_utf8_lossy(&output.stderr), + }) + } + _ => {} + } +} + +fn find_binutils_dlltool(sess: &Session) -> OsString { + assert!(sess.target.options.is_like_windows && !sess.target.options.is_like_msvc); + if let Some(dlltool_path) = &sess.opts.cg.dlltool { + return dlltool_path.clone().into_os_string(); + } + + let tool_name: OsString = if sess.host.options.is_like_windows { + // If we're compiling on Windows, always use "dlltool.exe". + "dlltool.exe" + } else { + // On other platforms, use the architecture-specific name. + match sess.target.arch.as_ref() { + "x86_64" => "x86_64-w64-mingw32-dlltool", + "x86" => "i686-w64-mingw32-dlltool", + "aarch64" => "aarch64-w64-mingw32-dlltool", + + // For non-standard architectures (e.g., aarch32) fallback to "dlltool". + _ => "dlltool", + } + } + .into(); + + // NOTE: it's not clear how useful it is to explicitly search PATH. + for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) { + let full_path = dir.join(&tool_name); + if full_path.is_file() { + return full_path.into_os_string(); + } + } + + // The user didn't specify the location of the dlltool binary, and we weren't able + // to find the appropriate one on the PATH. Just return the name of the tool + // and let the invocation fail with a hopefully useful error message. + tool_name +} + pub trait ArchiveBuilder { fn add_file(&mut self, path: &Path); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 653895380beb..7bad9d33e7d3 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -51,7 +51,8 @@ use super::linker::{self, Linker}; use super::metadata::{create_wrapper_file, MetadataPosition}; use super::rpath::{self, RPathConfig}; use crate::{ - errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, + common, errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, + NativeLib, }; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { @@ -390,17 +391,13 @@ fn link_rlib<'a>( } } - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())? - { - let output_path = archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir.as_ref(), - true, - ); - + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + codegen_results.crate_info.used_libraries.iter(), + tmpdir.as_ref(), + true, + )? { ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); }); @@ -488,6 +485,47 @@ fn collate_raw_dylibs<'a>( .collect()) } +fn create_dll_import_libs<'a>( + sess: &Session, + archive_builder_builder: &dyn ArchiveBuilderBuilder, + used_libraries: impl IntoIterator, + tmpdir: &Path, + is_direct_dependency: bool, +) -> Result, ErrorGuaranteed> { + Ok(collate_raw_dylibs(sess, used_libraries)? + .into_iter() + .map(|(raw_dylib_name, raw_dylib_imports)| { + let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; + let output_path = tmpdir.join(format!("{raw_dylib_name}{name_suffix}.lib")); + + let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(&sess.target); + + let import_name_and_ordinal_vector: Vec<(String, Option)> = raw_dylib_imports + .iter() + .map(|import: &DllImport| { + if sess.target.arch == "x86" { + ( + common::i686_decorated_name(import, mingw_gnu_toolchain, false), + import.ordinal(), + ) + } else { + (import.name.to_string(), import.ordinal()) + } + }) + .collect(); + + archive_builder_builder.create_dll_import_lib( + sess, + &raw_dylib_name, + import_name_and_ordinal_vector, + &output_path, + ); + + output_path + }) + .collect()) +} + /// Create a static archive. /// /// This is essentially the same thing as an rlib, but it also involves adding all of the upstream @@ -2305,16 +2343,14 @@ fn linker_with_args( ); // Link with the import library generated for any raw-dylib functions. - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())? - { - cmd.add_object(&archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir, - true, - )); + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + codegen_results.crate_info.used_libraries.iter(), + tmpdir, + true, + )? { + cmd.add_object(&output_path); } // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case // they are used within inlined functions or instantiated generic functions. We do this *after* @@ -2339,16 +2375,14 @@ fn linker_with_args( .flatten() .collect::>(); native_libraries_from_nonstatics.sort_unstable_by(|a, b| a.name.as_str().cmp(b.name.as_str())); - for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, native_libraries_from_nonstatics)? - { - cmd.add_object(&archive_builder_builder.create_dll_import_lib( - sess, - &raw_dylib_name, - &raw_dylib_imports, - tmpdir, - false, - )); + for output_path in create_dll_import_libs( + sess, + archive_builder_builder, + native_libraries_from_nonstatics, + tmpdir, + false, + )? { + cmd.add_object(&output_path); } // Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make @@ -2612,16 +2646,7 @@ fn add_native_libs_from_crate( NativeLibKind::Static { bundle, whole_archive } => { if link_static { let bundle = bundle.unwrap_or(true); - let whole_archive = whole_archive == Some(true) - // Backward compatibility case: this can be a rlib (so `+whole-archive` - // cannot be added explicitly if necessary, see the error in `fn link_rlib`) - // compiled as an executable due to `--test`. Use whole-archive implicitly, - // like before the introduction of native lib modifiers. - || (whole_archive == None - && bundle - && cnum == LOCAL_CRATE - && sess.is_test_crate()); - + let whole_archive = whole_archive == Some(true); if bundle && cnum != LOCAL_CRATE { if let Some(filename) = lib.filename { // If rlib contains native libs as archives, they are unpacked to tmpdir. diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 19394029c949..9b5a797ad510 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -208,6 +208,7 @@ pub(crate) fn create_object_file(sess: &Session) -> Option (Architecture::PowerPc64, None), "riscv32" => (Architecture::Riscv32, None), "riscv64" => (Architecture::Riscv64, None), + "sparc" => (Architecture::Sparc32Plus, None), "sparc64" => (Architecture::Sparc64, None), "avr" => (Architecture::Avr, None), "msp430" => (Architecture::Msp430, None), diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index f4e44e63d73c..f1e7f87f5676 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -143,7 +143,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> Bx::Value { let cx = bx.cx(); let (source, target) = - cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, bx.param_env()); + cx.tcx().struct_lockstep_tails_for_codegen(source, target, bx.param_env()); match (source.kind(), target.kind()) { (&ty::Array(_, len), &ty::Slice(_)) => { cx.const_usize(len.eval_target_usize(cx.tcx(), ty::ParamEnv::reveal_all())) diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index a972c0cd99d1..bfb1d217eae8 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -4,7 +4,9 @@ use rustc_hir::LangItem; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; +use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType}; use rustc_span::Span; +use rustc_target::spec::Target; use crate::traits::*; @@ -176,3 +178,66 @@ pub fn asm_const_to_str<'tcx>( _ => span_bug!(sp, "asm const has bad type {}", ty_and_layout.ty), } } + +pub fn is_mingw_gnu_toolchain(target: &Target) -> bool { + target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty() +} + +pub fn i686_decorated_name( + dll_import: &DllImport, + mingw: bool, + disable_name_mangling: bool, +) -> String { + let name = dll_import.name.as_str(); + + let (add_prefix, add_suffix) = match dll_import.import_name_type { + Some(PeImportNameType::NoPrefix) => (false, true), + Some(PeImportNameType::Undecorated) => (false, false), + _ => (true, true), + }; + + // Worst case: +1 for disable name mangling, +1 for prefix, +4 for suffix (@@__). + let mut decorated_name = String::with_capacity(name.len() + 6); + + if disable_name_mangling { + // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled. + decorated_name.push('\x01'); + } + + let prefix = if add_prefix && dll_import.is_fn { + match dll_import.calling_convention { + DllCallingConvention::C | DllCallingConvention::Vectorcall(_) => None, + DllCallingConvention::Stdcall(_) => (!mingw + || dll_import.import_name_type == Some(PeImportNameType::Decorated)) + .then_some('_'), + DllCallingConvention::Fastcall(_) => Some('@'), + } + } else if !dll_import.is_fn && !mingw { + // For static variables, prefix with '_' on MSVC. + Some('_') + } else { + None + }; + if let Some(prefix) = prefix { + decorated_name.push(prefix); + } + + decorated_name.push_str(name); + + if add_suffix && dll_import.is_fn { + use std::fmt::Write; + + match dll_import.calling_convention { + DllCallingConvention::C => {} + DllCallingConvention::Stdcall(arg_list_size) + | DllCallingConvention::Fastcall(arg_list_size) => { + write!(&mut decorated_name, "@{arg_list_size}").unwrap(); + } + DllCallingConvention::Vectorcall(arg_list_size) => { + write!(&mut decorated_name, "@@{arg_list_size}").unwrap(); + } + } + } + + decorated_name +} diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 2d1eae630cf1..94bf0ab34e21 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1026,6 +1026,28 @@ pub struct FailedToGetLayout<'tcx> { pub err: LayoutError<'tcx>, } +#[derive(Diagnostic)] +#[diag(codegen_ssa_dlltool_fail_import_library)] +pub(crate) struct DlltoolFailImportLibrary<'a> { + pub dlltool_path: Cow<'a, str>, + pub dlltool_args: String, + pub stdout: Cow<'a, str>, + pub stderr: Cow<'a, str>, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_error_writing_def_file)] +pub(crate) struct ErrorWritingDEFFile { + pub error: std::io::Error, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_error_calling_dlltool)] +pub(crate) struct ErrorCallingDllTool<'a> { + pub dlltool_path: Cow<'a, str>, + pub error: std::io::Error, +} + #[derive(Diagnostic)] #[diag(codegen_ssa_error_creating_remark_dir)] pub struct ErrorCreatingRemarkDir { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 24b2c9c51c6e..cf8f7fa25d85 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,11 +1,12 @@ use rustc_ast::ast; use rustc_attr::InstructionSetAttr; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; +use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_middle::bug; +use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::parse::feature_err; @@ -18,7 +19,7 @@ pub fn from_target_feature( tcx: TyCtxt<'_>, attr: &ast::Attribute, supported_target_features: &UnordMap>, - target_features: &mut Vec, + target_features: &mut Vec, ) { let Some(list) = attr.meta_item_list() else { return }; let bad_item = |span| { @@ -99,14 +100,26 @@ pub fn from_target_feature( })); } - // Add both explicit and implied target features, using a set to deduplicate - let mut target_features_set = UnordSet::new(); + // Add explicit features + target_features.extend( + added_target_features.iter().copied().map(|name| TargetFeature { name, implied: false }), + ); + + // Add implied features + let mut implied_target_features = UnordSet::new(); for feature in added_target_features.iter() { - target_features_set - .extend_unord(tcx.implied_target_features(*feature).clone().into_items()); + implied_target_features.extend(tcx.implied_target_features(*feature).clone()); } - target_features_set.extend(added_target_features); - target_features.extend(target_features_set.into_sorted_stable_ord()) + for feature in added_target_features.iter() { + implied_target_features.remove(feature); + } + target_features.extend( + implied_target_features + .into_sorted_stable_ord() + .iter() + .copied() + .map(|name| TargetFeature { name, implied: true }), + ) } /// Computes the set of target features used in a function for the purposes of @@ -115,7 +128,7 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet { let mut target_features = tcx.sess.unstable_target_features.clone(); if tcx.def_kind(did).has_codegen_attrs() { let attrs = tcx.codegen_fn_attrs(did); - target_features.extend(&attrs.target_features); + target_features.extend(attrs.target_features.iter().map(|feature| feature.name)); match attrs.instruction_set { None => {} Some(InstructionSetAttr::ArmA32) => { @@ -160,31 +173,13 @@ pub(crate) fn provide(providers: &mut Providers) { .target .supported_target_features() .iter() - .map(|&(a, b)| (a.to_string(), b.as_feature_name())) + .map(|&(a, b, _)| (a.to_string(), b.as_feature_name())) .collect() } }, implied_target_features: |tcx, feature| { - let implied_features = tcx - .sess - .target - .implied_target_features() - .iter() - .map(|(f, i)| (Symbol::intern(f), i)) - .collect::>(); - - // implied target features have their own implied target features, so we traverse the - // map until there are no more features to add - let mut features = UnordSet::new(); - let mut new_features = vec![feature]; - while let Some(new_feature) = new_features.pop() { - if features.insert(new_feature) { - if let Some(implied_features) = implied_features.get(&new_feature) { - new_features.extend(implied_features.iter().copied().map(Symbol::intern)) - } - } - } - features + UnordSet::from(tcx.sess.target.implied_target_features(std::iter::once(feature))) + .into_sorted_stable_ord() }, asm_target_features, ..*providers diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 80dad79179a3..7c042c0c6219 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -91,7 +91,7 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { return false; } - let tail = self.tcx().struct_tail_erasing_lifetimes(ty, param_env); + let tail = self.tcx().struct_tail_for_codegen(ty, param_env); match tail.kind() { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 2c5147678e8c..fdbdfc7e1b8b 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -317,19 +317,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { && attrs .target_features .iter() - .any(|feature| !self.tcx.sess.target_features.contains(feature)) + .any(|feature| !self.tcx.sess.target_features.contains(&feature.name)) { throw_ub_custom!( fluent::const_eval_unavailable_target_features_for_fn, unavailable_feats = attrs .target_features .iter() - .filter(|&feature| !self.tcx.sess.target_features.contains(feature)) + .filter(|&feature| !feature.implied + && !self.tcx.sess.target_features.contains(&feature.name)) .fold(String::new(), |mut s, feature| { if !s.is_empty() { s.push_str(", "); } - s.push_str(feature.as_str()); + s.push_str(feature.name.as_str()); s }), ); @@ -677,9 +678,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } else { // Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`. // (For that reason we also cannot use `unpack_dyn_trait`.) - let receiver_tail = self - .tcx - .struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env); + let receiver_tail = + self.tcx.struct_tail_for_codegen(receiver_place.layout.ty, self.param_env); let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else { span_bug!( self.cur_span(), diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index b2f07de0ac4e..c3d7f2234ed9 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -386,7 +386,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx> { // A -> A conversion let (src_pointee_ty, dest_pointee_ty) = - self.tcx.struct_lockstep_tails_erasing_lifetimes(source_ty, cast_ty, self.param_env); + self.tcx.struct_lockstep_tails_for_codegen(source_ty, cast_ty, self.param_env); match (&src_pointee_ty.kind(), &dest_pointee_ty.kind()) { (&ty::Array(_, length), &ty::Slice(_)) => { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 460f5448634b..fadc4ee6c8a0 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -343,7 +343,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { meta: MemPlaceMeta, pointee: TyAndLayout<'tcx>, ) -> InterpResult<'tcx> { - let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); + let tail = self.ecx.tcx.struct_tail_for_codegen(pointee.ty, self.ecx.param_env); match tail.kind() { ty::Dynamic(data, _, ty::Dyn) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; diff --git a/compiler/rustc_const_eval/src/util/alignment.rs b/compiler/rustc_const_eval/src/util/alignment.rs index 528274e6abac..5ad55968398e 100644 --- a/compiler/rustc_const_eval/src/util/alignment.rs +++ b/compiler/rustc_const_eval/src/util/alignment.rs @@ -22,7 +22,7 @@ where }; let ty = place.ty(local_decls, tcx).ty; - let unsized_tail = || tcx.struct_tail_with_normalize(ty, |ty| ty, || {}); + let unsized_tail = || tcx.struct_tail_for_codegen(ty, param_env); match tcx.layout_of(param_env.and(ty)) { Ok(layout) if layout.align.abi <= pack diff --git a/compiler/rustc_data_structures/src/steal.rs b/compiler/rustc_data_structures/src/steal.rs index 9a0fd52677d1..0f2c0eee27d2 100644 --- a/compiler/rustc_data_structures/src/steal.rs +++ b/compiler/rustc_data_structures/src/steal.rs @@ -51,6 +51,15 @@ impl Steal { let value = value_ref.take(); value.expect("attempt to steal from stolen value") } + + /// Writers of rustc drivers often encounter stealing issues. This function makes it possible to + /// handle these errors gracefully. + /// + /// This should not be used within rustc as it leaks information not tracked + /// by the query system, breaking incremental compilation. + pub fn is_stolen(&self) -> bool { + self.value.borrow().is_none() + } } impl> HashStable for Steal { diff --git a/compiler/rustc_data_structures/src/transitive_relation.rs b/compiler/rustc_data_structures/src/transitive_relation.rs index 26b00e0af3a8..e81ebb9a4be2 100644 --- a/compiler/rustc_data_structures/src/transitive_relation.rs +++ b/compiler/rustc_data_structures/src/transitive_relation.rs @@ -203,9 +203,9 @@ impl TransitiveRelation { /// exists). See `postdom_upper_bound` for details. pub fn mutual_immediate_postdominator(&self, mut mubs: Vec) -> Option { loop { - match mubs.len() { - 0 => return None, - 1 => return Some(mubs[0]), + match mubs[..] { + [] => return None, + [mub] => return Some(mub), _ => { let m = mubs.pop().unwrap(); let n = mubs.pop().unwrap(); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 627a0ebb4e5e..2b7dc040f641 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -51,7 +51,8 @@ use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{ - nightly_options, ErrorOutputType, Input, OutFileName, OutputType, CG_OPTIONS, Z_OPTIONS, + nightly_options, ErrorOutputType, Input, OutFileName, OutputType, UnstableOptions, CG_OPTIONS, + Z_OPTIONS, }; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; @@ -301,6 +302,8 @@ fn run_compiler( let Some(matches) = handle_options(&default_early_dcx, &args) else { return Ok(()) }; let sopts = config::build_session_options(&mut default_early_dcx, &matches); + // fully initialize ice path static once unstable options are available as context + let ice_file = ice_path_with_config(Some(&sopts.unstable_opts)).clone(); if let Some(ref code) = matches.opt_str("explain") { handle_explain(&default_early_dcx, diagnostics_registry(), code, sopts.color); @@ -315,7 +318,7 @@ fn run_compiler( input: Input::File(PathBuf::new()), output_file: ofile, output_dir: odir, - ice_file: ice_path().clone(), + ice_file, file_loader, locale_resources: DEFAULT_LOCALE_RESOURCES, lint_caps: Default::default(), @@ -335,12 +338,11 @@ fn run_compiler( config.input = input; true // has input: normal compilation } - Ok(None) => match matches.free.len() { - 0 => false, // no input: we will exit early - 1 => panic!("make_input should have provided valid inputs"), - _ => default_early_dcx.early_fatal(format!( - "multiple input filenames provided (first two filenames are `{}` and `{}`)", - matches.free[0], matches.free[1], + Ok(None) => match matches.free.as_slice() { + [] => false, // no input: we will exit early + [_] => panic!("make_input should have provided valid inputs"), + [fst, snd, ..] => default_early_dcx.early_fatal(format!( + "multiple input filenames provided (first two filenames are `{fst}` and `{snd}`)" )), }, }; @@ -488,34 +490,30 @@ fn make_input( early_dcx: &EarlyDiagCtxt, free_matches: &[String], ) -> Result, ErrorGuaranteed> { - if free_matches.len() == 1 { - let ifile = &free_matches[0]; - if ifile == "-" { - let mut src = String::new(); - if io::stdin().read_to_string(&mut src).is_err() { - // Immediately stop compilation if there was an issue reading - // the input (for example if the input stream is not UTF-8). - let reported = early_dcx - .early_err("couldn't read from stdin, as it did not contain valid UTF-8"); - return Err(reported); - } - if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") { - let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect( - "when UNSTABLE_RUSTDOC_TEST_PATH is set \ + let [ifile] = free_matches else { return Ok(None) }; + if ifile == "-" { + let mut src = String::new(); + if io::stdin().read_to_string(&mut src).is_err() { + // Immediately stop compilation if there was an issue reading + // the input (for example if the input stream is not UTF-8). + let reported = + early_dcx.early_err("couldn't read from stdin, as it did not contain valid UTF-8"); + return Err(reported); + } + if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") { + let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect( + "when UNSTABLE_RUSTDOC_TEST_PATH is set \ UNSTABLE_RUSTDOC_TEST_LINE also needs to be set", - ); - let line = isize::from_str_radix(&line, 10) - .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number"); - let file_name = FileName::doc_test_source_code(PathBuf::from(path), line); - Ok(Some(Input::Str { name: file_name, input: src })) - } else { - Ok(Some(Input::Str { name: FileName::anon_source_code(&src), input: src })) - } + ); + let line = isize::from_str_radix(&line, 10) + .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number"); + let file_name = FileName::doc_test_source_code(PathBuf::from(path), line); + Ok(Some(Input::Str { name: file_name, input: src })) } else { - Ok(Some(Input::File(PathBuf::from(ifile)))) + Ok(Some(Input::Str { name: FileName::anon_source_code(&src), input: src })) } } else { - Ok(None) + Ok(Some(Input::File(PathBuf::from(ifile)))) } } @@ -1306,25 +1304,43 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { static ICE_PATH: OnceLock> = OnceLock::new(); +// This function should only be called from the ICE hook. +// +// The intended behavior is that `run_compiler` will invoke `ice_path_with_config` early in the +// initialization process to properly initialize the ICE_PATH static based on parsed CLI flags. +// +// Subsequent calls to either function will then return the proper ICE path as configured by +// the environment and cli flags fn ice_path() -> &'static Option { + ice_path_with_config(None) +} + +fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option { + if ICE_PATH.get().is_some() && config.is_some() && cfg!(debug_assertions) { + tracing::warn!( + "ICE_PATH has already been initialized -- files may be emitted at unintended paths" + ) + } + ICE_PATH.get_or_init(|| { if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() { return None; } - if let Some(s) = std::env::var_os("RUST_BACKTRACE") - && s == "0" - { - return None; - } let mut path = match std::env::var_os("RUSTC_ICE") { Some(s) => { if s == "0" { // Explicitly opting out of writing ICEs to disk. return None; } + if let Some(unstable_opts) = config && unstable_opts.metrics_dir.is_some() { + tracing::warn!("ignoring -Zerror-metrics in favor of RUSTC_ICE for destination of ICE report files"); + } PathBuf::from(s) } - None => std::env::current_dir().unwrap_or_default(), + None => config + .and_then(|unstable_opts| unstable_opts.metrics_dir.to_owned()) + .or_else(|| std::env::current_dir().ok()) + .unwrap_or_default(), }; let now: OffsetDateTime = SystemTime::now().into(); let file_now = now diff --git a/compiler/rustc_error_codes/src/error_codes/E0517.md b/compiler/rustc_error_codes/src/error_codes/E0517.md index ae802245bd1d..5354a08bf31a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0517.md +++ b/compiler/rustc_error_codes/src/error_codes/E0517.md @@ -25,14 +25,17 @@ impl Foo { These attributes do not work on typedefs, since typedefs are just aliases. Representations like `#[repr(u8)]`, `#[repr(i64)]` are for selecting the -discriminant size for enums with no data fields on any of the variants, e.g. -`enum Color {Red, Blue, Green}`, effectively setting the size of the enum to -the size of the provided type. Such an enum can be cast to a value of the same -type as well. In short, `#[repr(u8)]` makes the enum behave like an integer -with a constrained set of allowed values. +discriminant size for enums. For enums with no data fields on any of the +variants, e.g. `enum Color {Red, Blue, Green}`, this effectively sets the size +of the enum to the size of the provided type. Such an enum can be cast to a +value of the same type as well. In short, `#[repr(u8)]` makes a field-less enum +behave like an integer with a constrained set of allowed values. -Only field-less enums can be cast to numerical primitives, so this attribute -will not apply to structs. +For a description of how `#[repr(C)]` and representations like `#[repr(u8)]` +affect the layout of enums with data fields, see [RFC 2195][rfc2195]. + +Only field-less enums can be cast to numerical primitives. Representations like +`#[repr(u8)]` will not apply to structs. `#[repr(packed)]` reduces padding to make the struct size smaller. The representation of enums isn't strictly defined in Rust, and this attribute @@ -42,3 +45,5 @@ won't work on enums. types (i.e., `u8`, `i32`, etc) a representation that permits vectorization via SIMD. This doesn't make much sense for enums since they don't consist of a single list of data. + +[rfc2195]: https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 67ca6d50cca4..fae8b5647fc9 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -920,8 +920,8 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { applicability: Applicability, style: SuggestionStyle, ) -> &mut Self { - suggestion.sort_unstable(); - suggestion.dedup_by(|(s1, m1), (s2, m2)| s1.source_equal(*s2) && m1 == m2); + let mut seen = crate::FxHashSet::default(); + suggestion.retain(|(span, msg)| seen.insert((span.lo(), span.hi(), msg.clone()))); let parts = suggestion .into_iter() diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 3e22786a01f7..9e3bc3e60b12 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -66,6 +66,7 @@ macro_rules! into_diag_arg_for_number { impl IntoDiagArg for $ty { fn into_diag_arg(self) -> DiagArgValue { // Convert to a string if it won't fit into `Number`. + #[allow(irrefutable_let_patterns)] if let Ok(n) = TryInto::::try_into(self) { DiagArgValue::Number(n) } else { diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 88ed3128164d..9ce5d77ef6c0 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -43,19 +43,14 @@ const DEFAULT_COLUMN_WIDTH: usize = 140; /// Describes the way the content of the `rendered` field of the json output is generated #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HumanReadableErrorType { - Default(ColorConfig), - AnnotateSnippet(ColorConfig), - Short(ColorConfig), + Default, + AnnotateSnippet, + Short, } impl HumanReadableErrorType { - /// Returns a (`short`, `color`) tuple - pub fn unzip(self) -> (bool, ColorConfig) { - match self { - HumanReadableErrorType::Default(cc) => (false, cc), - HumanReadableErrorType::Short(cc) => (true, cc), - HumanReadableErrorType::AnnotateSnippet(cc) => (false, cc), - } + pub fn short(&self) -> bool { + *self == HumanReadableErrorType::Short } } @@ -231,17 +226,17 @@ pub trait Emitter: Translate { ) { if let Some((sugg, rest)) = suggestions.split_first() { let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap(); - if rest.is_empty() && + if rest.is_empty() // ^ if there is only one suggestion // don't display multi-suggestions as labels - sugg.substitutions.len() == 1 && + && let [substitution] = sugg.substitutions.as_slice() // don't display multipart suggestions as labels - sugg.substitutions[0].parts.len() == 1 && + && let [part] = substitution.parts.as_slice() // don't display long messages as labels - msg.split_whitespace().count() < 10 && + && msg.split_whitespace().count() < 10 // don't display multiline suggestions as labels - !sugg.substitutions[0].parts[0].snippet.contains('\n') && - ![ + && !part.snippet.contains('\n') + && ![ // when this style is set we want the suggestion to be a message, not inline SuggestionStyle::HideCodeAlways, // trivial suggestion for tooling's sake, never shown @@ -250,8 +245,8 @@ pub trait Emitter: Translate { SuggestionStyle::ShowAlways, ].contains(&sugg.style) { - let substitution = &sugg.substitutions[0].parts[0].snippet.trim(); - let msg = if substitution.is_empty() || sugg.style.hide_inline() { + let snippet = part.snippet.trim(); + let msg = if snippet.is_empty() || sugg.style.hide_inline() { // This substitution is only removal OR we explicitly don't want to show the // code inline (`hide_inline`). Therefore, we don't show the substitution. format!("help: {msg}") @@ -260,19 +255,18 @@ pub trait Emitter: Translate { format!( "help: {}{}: `{}`", msg, - if self.source_map().is_some_and(|sm| is_case_difference( - sm, - substitution, - sugg.substitutions[0].parts[0].span, - )) { + if self + .source_map() + .is_some_and(|sm| is_case_difference(sm, snippet, part.span,)) + { " (notice the capitalization)" } else { "" }, - substitution, + snippet, ) }; - primary_span.push_span_label(sugg.substitutions[0].parts[0].span, msg); + primary_span.push_span_label(part.span, msg); // We return only the modified primary_span suggestions.clear(); @@ -2595,9 +2589,7 @@ fn num_decimal_digits(num: usize) -> usize { // We replace some characters so the CLI output is always consistent and underlines aligned. // Keep the following list in sync with `rustc_span::char_width`. -// ATTENTION: keep lexicografically sorted so that the binary search will work const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ - // tidy-alphabetical-start // In terminals without Unicode support the following will be garbled, but in *all* terminals // the underlying codepoint will be as well. We could gate this replacement behind a "unicode // support" gate. @@ -2610,7 +2602,7 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\u{0006}', "␆"), ('\u{0007}', "␇"), ('\u{0008}', "␈"), - ('\u{0009}', " "), // We do our own tab replacement + ('\t', " "), // We do our own tab replacement ('\u{000b}', "␋"), ('\u{000c}', "␌"), ('\u{000d}', "␍"), @@ -2643,13 +2635,23 @@ const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[ ('\u{2067}', "�"), ('\u{2068}', "�"), ('\u{2069}', "�"), - // tidy-alphabetical-end ]; fn normalize_whitespace(s: &str) -> String { - // Scan the input string for a character in the ordered table above. If it's present, replace - // it with it's alternative string (it can be more than 1 char!). Otherwise, retain the input - // char. At the end, allocate all chars into a string in one operation. + const { + let mut i = 1; + while i < OUTPUT_REPLACEMENTS.len() { + assert!( + OUTPUT_REPLACEMENTS[i - 1].0 < OUTPUT_REPLACEMENTS[i].0, + "The OUTPUT_REPLACEMENTS array must be sorted (for binary search to work) \ + and must contain no duplicate entries" + ); + i += 1; + } + } + // Scan the input string for a character in the ordered table above. + // If it's present, replace it with its alternative string (it can be more than 1 char!). + // Otherwise, retain the input char. s.chars().fold(String::with_capacity(s.len()), |mut s, c| { match OUTPUT_REPLACEMENTS.binary_search_by_key(&c, |(k, _)| *k) { Ok(i) => s.push_str(OUTPUT_REPLACEMENTS[i].1), diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 42a28bc78901..32e59f9ab036 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -55,6 +55,7 @@ pub struct JsonEmitter { ignored_directories_in_source_blocks: Vec, #[setters(skip)] json_rendered: HumanReadableErrorType, + color_config: ColorConfig, diagnostic_width: Option, macro_backtrace: bool, track_diagnostics: bool, @@ -68,6 +69,7 @@ impl JsonEmitter { fallback_bundle: LazyFallbackBundle, pretty: bool, json_rendered: HumanReadableErrorType, + color_config: ColorConfig, ) -> JsonEmitter { JsonEmitter { dst: IntoDynSyncSend(dst), @@ -79,6 +81,7 @@ impl JsonEmitter { ui_testing: false, ignored_directories_in_source_blocks: Vec::new(), json_rendered, + color_config, diagnostic_width: None, macro_backtrace: false, track_diagnostics: false, @@ -173,7 +176,7 @@ impl Emitter for JsonEmitter { } fn should_show_explain(&self) -> bool { - !matches!(self.json_rendered, HumanReadableErrorType::Short(_)) + !self.json_rendered.short() } } @@ -353,8 +356,8 @@ impl Diagnostic { let buf = BufWriter::default(); let mut dst: Destination = Box::new(buf.clone()); - let (short, color_config) = je.json_rendered.unzip(); - match color_config { + let short = je.json_rendered.short(); + match je.color_config { ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)), ColorConfig::Never => {} } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index e3549fc3aa53..6af376d7afdb 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -50,7 +50,8 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { sm, fallback_bundle, true, // pretty - HumanReadableErrorType::Short(ColorConfig::Never), + HumanReadableErrorType::Short, + ColorConfig::Never, ); let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index ceebcd46a6f7..aefbf05a1fcc 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -2024,11 +2024,11 @@ pub fn a_or_an(s: &str) -> &'static str { /// /// Take a list ["a", "b", "c"] and output a display friendly version "a, b and c" pub fn display_list_with_comma_and(v: &[T]) -> String { - match v.len() { - 0 => "".to_string(), - 1 => v[0].to_string(), - 2 => format!("{} and {}", v[0], v[1]), - _ => format!("{}, {}", v[0], display_list_with_comma_and(&v[1..])), + match v { + [] => "".to_string(), + [a] => a.to_string(), + [a, b] => format!("{a} and {b}"), + [a, v @ ..] => format!("{a}, {}", display_list_with_comma_and(v)), } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index c195d6925889..7712915d490d 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1306,12 +1306,12 @@ pub fn parse_macro_name_and_helper_attrs( // that it's of the form `#[proc_macro_derive(Foo)]` or // `#[proc_macro_derive(Foo, attributes(A, ..))]` let list = attr.meta_item_list()?; - if list.len() != 1 && list.len() != 2 { + let ([trait_attr] | [trait_attr, _]) = list.as_slice() else { dcx.emit_err(errors::AttrNoArguments { span: attr.span }); return None; - } - let Some(trait_attr) = list[0].meta_item() else { - dcx.emit_err(errors::NotAMetaItem { span: list[0].span() }); + }; + let Some(trait_attr) = trait_attr.meta_item() else { + dcx.emit_err(errors::NotAMetaItem { span: trait_attr.span() }); return None; }; let trait_ident = match trait_attr.ident() { diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 6ce9ff18c279..628c6bfeb793 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -3,34 +3,32 @@ use std::borrow::Cow; use rustc_ast::token::{self, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, Diag, DiagMessage}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage}; use rustc_macros::Subdiagnostic; use rustc_parse::parser::{Parser, Recovery}; +use rustc_session::parse::ParseSess; use rustc_span::source_map::SourceMap; use rustc_span::symbol::Ident; use rustc_span::{ErrorGuaranteed, Span}; use tracing::debug; use super::macro_rules::{parser_from_cx, NoopTracker}; -use crate::base::{DummyResult, ExtCtxt, MacResult}; use crate::expand::{parse_ast_fragment, AstFragmentKind}; use crate::mbe::macro_parser::ParseResult::*; use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser}; use crate::mbe::macro_rules::{try_match_macro, Tracker}; -pub(super) fn failed_to_match_macro<'cx>( - cx: &'cx mut ExtCtxt<'_>, +pub(super) fn failed_to_match_macro( + psess: &ParseSess, sp: Span, def_span: Span, name: Ident, arg: TokenStream, lhses: &[Vec], -) -> Box { - let psess = &cx.sess.psess; - +) -> (Span, ErrorGuaranteed) { // An error occurred, try the expansion again, tracking the expansion closely for better // diagnostics. - let mut tracker = CollectTrackerAndEmitter::new(cx, sp); + let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp); let try_success_result = try_match_macro(psess, name, &arg, lhses, &mut tracker); @@ -38,7 +36,7 @@ pub(super) fn failed_to_match_macro<'cx>( // Nonterminal parser recovery might turn failed matches into successful ones, // but for that it must have emitted an error already assert!( - tracker.cx.dcx().has_errors().is_some(), + tracker.dcx.has_errors().is_some(), "Macro matching returned a success on the second try" ); } @@ -50,15 +48,15 @@ pub(super) fn failed_to_match_macro<'cx>( let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure else { - return DummyResult::any(sp, cx.dcx().span_delayed_bug(sp, "failed to match a macro")); + return (sp, psess.dcx().span_delayed_bug(sp, "failed to match a macro")); }; let span = token.span.substitute_dummy(sp); - let mut err = cx.dcx().struct_span_err(span, parse_failure_msg(&token, None)); + let mut err = psess.dcx().struct_span_err(span, parse_failure_msg(&token, None)); err.span_label(span, label); - if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { - err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro"); + if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) { + err.span_label(psess.source_map().guess_head_span(def_span), "when calling this macro"); } annotate_doc_comment(&mut err, psess.source_map(), span); @@ -76,7 +74,7 @@ pub(super) fn failed_to_match_macro<'cx>( err.note("captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens"); err.note("see for more information"); - if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { + if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) { err.help("try using `:tt` instead in the macro definition"); } } @@ -104,18 +102,17 @@ pub(super) fn failed_to_match_macro<'cx>( } } let guar = err.emit(); - cx.trace_macros_diag(); - DummyResult::any(sp, guar) + (sp, guar) } /// The tracker used for the slow error path that collects useful info for diagnostics. -struct CollectTrackerAndEmitter<'a, 'cx, 'matcher> { - cx: &'a mut ExtCtxt<'cx>, +struct CollectTrackerAndEmitter<'dcx, 'matcher> { + dcx: DiagCtxtHandle<'dcx>, remaining_matcher: Option<&'matcher MatcherLoc>, /// Which arm's failure should we report? (the one furthest along) best_failure: Option, root_span: Span, - result: Option>, + result: Option<(Span, ErrorGuaranteed)>, } struct BestFailure { @@ -131,7 +128,7 @@ impl BestFailure { } } -impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> { +impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'matcher> { type Failure = (Token, u32, &'static str); fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure { @@ -151,7 +148,7 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, Success(_) => { // Nonterminal parser recovery might turn failed matches into successful ones, // but for that it must have emitted an error already - self.cx.dcx().span_delayed_bug( + self.dcx.span_delayed_bug( self.root_span, "should not collect detailed info for successful macro match", ); @@ -177,10 +174,10 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, } Error(err_sp, msg) => { let span = err_sp.substitute_dummy(self.root_span); - let guar = self.cx.dcx().span_err(span, msg.clone()); - self.result = Some(DummyResult::any(span, guar)); + let guar = self.dcx.span_err(span, msg.clone()); + self.result = Some((span, guar)); } - ErrorReported(guar) => self.result = Some(DummyResult::any(self.root_span, *guar)), + ErrorReported(guar) => self.result = Some((self.root_span, *guar)), } } @@ -193,9 +190,9 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, } } -impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> { - fn new(cx: &'a mut ExtCtxt<'cx>, root_span: Span) -> Self { - Self { cx, remaining_matcher: None, best_failure: None, root_span, result: None } +impl<'dcx> CollectTrackerAndEmitter<'dcx, '_> { + fn new(dcx: DiagCtxtHandle<'dcx>, root_span: Span) -> Self { + Self { dcx, remaining_matcher: None, best_failure: None, root_span, result: None } } } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 1502177563d9..6f177107e702 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -268,7 +268,10 @@ fn expand_macro<'cx>( } Err(CanRetry::Yes) => { // Retry and emit a better error. - diagnostics::failed_to_match_macro(cx, sp, def_span, name, arg, lhses) + let (span, guar) = + diagnostics::failed_to_match_macro(cx.psess(), sp, def_span, name, arg, lhses); + cx.trace_macros_diag(); + DummyResult::any(span, guar) } } } diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index e42a655531b5..44286cfeeefa 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -267,6 +267,8 @@ declare_features! ( (accepted, min_const_generics, "1.51.0", Some(74878)), /// Allows calling `const unsafe fn` inside `unsafe` blocks in `const fn` functions. (accepted, min_const_unsafe_fn, "1.33.0", Some(55607)), + /// Allows exhaustive pattern matching on uninhabited types when matched by value. + (accepted, min_exhaustive_patterns, "CURRENT_RUSTC_VERSION", Some(119612)), /// Allows using `Self` and associated types in struct expressions and patterns. (accepted, more_struct_aliases, "1.16.0", Some(37544)), /// Allows using the MOVBE target feature. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 88a4b5a83824..47810bc9165e 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -519,9 +519,6 @@ declare_features! ( (unstable, macro_metavar_expr_concat, "1.81.0", Some(124225)), /// Allows `#[marker]` on certain traits allowing overlapping implementations. (unstable, marker_trait_attr, "1.30.0", Some(29864)), - /// Allows exhaustive pattern matching on types that contain uninhabited types in cases that are - /// unambiguously sound. - (unstable, min_exhaustive_patterns, "1.77.0", Some(119612)), /// A minimal, sound subset of specialization intended to be used by the /// standard library until the soundness issues with specialization /// are fixed. 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 53cde14f337b..35577613800b 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1117,7 +1117,7 @@ fn check_region_bounds_on_impl_item<'tcx>( .dcx() .create_err(LifetimesOrBoundsMismatchOnTrait { span, - item_kind: assoc_item_kind_str(&impl_m), + item_kind: impl_m.descr(), ident: impl_m.ident(tcx), generics_span, bounds_span, @@ -1294,7 +1294,7 @@ fn compare_number_of_generics<'tcx>( ("const", trait_own_counts.consts, impl_own_counts.consts), ]; - let item_kind = assoc_item_kind_str(&impl_); + let item_kind = impl_.descr(); let mut err_occurred = None; for (kind, trait_count, impl_count) in matchings { @@ -1676,7 +1676,7 @@ fn compare_generic_param_kinds<'tcx>( param_impl_span, E0053, "{} `{}` has an incompatible generic parameter for trait `{}`", - assoc_item_kind_str(&impl_item), + impl_item.descr(), trait_item.name, &tcx.def_path_str(tcx.parent(trait_item.def_id)) ); @@ -2249,14 +2249,6 @@ fn param_env_with_gat_bounds<'tcx>( ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing) } -fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str { - match impl_item.kind { - ty::AssocKind::Const => "const", - ty::AssocKind::Fn => "method", - ty::AssocKind::Type => "type", - } -} - /// Manually check here that `async fn foo()` wasn't matched against `fn foo()`, /// and extract a better error if so. fn try_report_async_mismatch<'tcx>( diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index c878095ba0d7..a9f8630741a8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1276,7 +1276,7 @@ fn check_item_type( UnsizedHandling::Forbid => true, UnsizedHandling::Allow => false, UnsizedHandling::AllowIfForeignTail => { - let tail = tcx.struct_tail_erasing_lifetimes(item_ty, wfcx.param_env); + let tail = tcx.struct_tail_for_codegen(item_ty, wfcx.param_env); !matches!(tail.kind(), ty::Foreign(_)) } }; diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 47ff748547a9..8c1e5e78b75d 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -67,6 +67,7 @@ pub fn provide(providers: &mut Providers) { 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, + impl_super_outlives: item_bounds::impl_super_outlives, generics_of: generics_of::generics_of, predicates_of: predicates_of::predicates_of, predicates_defined_on, diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 6bff99ea65f0..ec48c781c0e4 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -212,6 +212,8 @@ pub(super) fn item_super_predicates( }) } +/// This exists as an optimization to compute only the item bounds of the item +/// that are not `Self` bounds. pub(super) fn item_non_self_assumptions( tcx: TyCtxt<'_>, def_id: DefId, @@ -226,6 +228,25 @@ pub(super) fn item_non_self_assumptions( } } +/// This exists as an optimization to compute only the supertraits of this impl's +/// trait that are outlives bounds. +pub(super) fn impl_super_outlives( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { + tcx.impl_trait_header(def_id).expect("expected an impl of trait").trait_ref.map_bound( + |trait_ref| { + let clause: ty::Clause<'_> = trait_ref.upcast(tcx); + tcx.mk_clauses_from_iter(util::elaborate(tcx, [clause]).filter(|clause| { + matches!( + clause.kind().skip_binder(), + ty::ClauseKind::TypeOutlives(_) | ty::ClauseKind::RegionOutlives(_) + ) + })) + }, + ) +} + struct AssocTyToOpaque<'tcx> { tcx: TyCtxt<'tcx>, fn_def_id: DefId, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index 034a4918b505..a59e9aa85fd7 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -253,32 +253,6 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( match (args_iter.peek(), params.peek()) { (Some(&arg), Some(¶m)) => { match (arg, ¶m.kind, arg_count.explicit_late_bound) { - ( - GenericArg::Const(hir::ConstArg { - is_desugared_from_effects: true, - .. - }), - GenericParamDefKind::Const { is_host_effect: false, .. } - | GenericParamDefKind::Type { .. } - | GenericParamDefKind::Lifetime, - _, - ) => { - // FIXME(effects): this should be removed - // SPECIAL CASE FOR DESUGARED EFFECT PARAMS - // This comes from the following example: - // - // ``` - // #[const_trait] - // pub trait PartialEq {} - // impl const PartialEq for () {} - // ``` - // - // Since this is a const impl, we need to insert a host arg at the end of - // `PartialEq`'s generics, but this errors since `Rhs` isn't specified. - // To work around this, we infer all arguments until we reach the host param. - args.push(ctx.inferred_kind(&args, param, infer_args)); - params.next(); - } (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) | ( GenericArg::Type(_) | GenericArg::Infer(_), diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e54f9486f6a8..fec6efdc0f71 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1736,6 +1736,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_error(tcx, guar) }; + // Check that the expected field type is WF. Otherwise, we emit no use-site error + // in the case of coercions for non-WF fields, which leads to incorrect error + // tainting. See issue #126272. + self.register_wf_obligation( + field_type.into(), + field.expr.span, + ObligationCauseCode::WellFormed(None), + ); + // Make sure to give a type to the field even if there's // an error, so we can continue type-checking. let ty = self.check_expr_with_hint(field.expr, field_type); @@ -2734,15 +2743,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } 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 - .variants() - .iter() - .next() - .unwrap() - .fields - .iter() - .any(|f| f.ident(self.tcx) == field) + && let [variant] = &adt_def.variants().raw + && variant.fields.iter().any(|f| f.ident(self.tcx) == field) { err.multipart_suggestion( "to access the field, dereference first", diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 8c1aa66332fc..841d25b54cc8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -404,8 +404,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { code: traits::ObligationCauseCode<'tcx>, ) { if !ty.references_error() { - let tail = - self.tcx.struct_tail_with_normalize(ty, |ty| self.normalize(span, ty), || {}); + let tail = self.tcx.struct_tail_with_normalize( + ty, + |ty| { + if self.next_trait_solver() { + self.try_structurally_resolve_type(span, ty) + } else { + self.normalize(span, ty) + } + }, + || {}, + ); // Sized types have static alignment, and so do slices. if tail.is_trivially_sized(self.tcx) || matches!(tail.kind(), ty::Slice(..)) { // Nothing else is required here. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index cef003e0a43d..89e7227eda2c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -22,7 +22,7 @@ use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::Session; use rustc_span::symbol::{kw, Ident}; -use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_span::{sym, Span, DUMMY_SP}; use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; @@ -1140,8 +1140,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .get(arg_idx + 1) .map(|&(_, sp)| sp) .unwrap_or_else(|| { - // Subtract one to move before `)` - call_expr.span.with_lo(call_expr.span.hi() - BytePos(1)) + // Try to move before `)`. Note that `)` here is not necessarily + // the latin right paren, it could be a Unicode-confusable that + // looks like a `)`, so we must not use `- BytePos(1)` + // manipulations here. + self.tcx().sess.source_map().end_point(call_expr.span) }); // Include next comma diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index f14159dc35a5..e0c0adac0767 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -654,17 +654,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { traits::upcast_choices(self.tcx, source_trait_ref, target_trait_def_id); // must be exactly one trait ref or we'd get an ambig error etc - if upcast_trait_refs.len() != 1 { + let [upcast_trait_ref] = upcast_trait_refs.as_slice() else { span_bug!( self.span, "cannot uniquely upcast `{:?}` to `{:?}`: `{:?}`", source_trait_ref, target_trait_def_id, upcast_trait_refs - ); - } + ) + }; - upcast_trait_refs.into_iter().next().unwrap() + *upcast_trait_ref } fn instantiate_binder_with_fresh_vars(&self, value: ty::Binder<'tcx, T>) -> T diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 94133357a3eb..61287d98676b 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -321,19 +321,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); }; let suggest_for_privacy = - |err: &mut Diag<'_>, mut msg: String, sugg: Vec| { - if sugg.len() == 1 { - let msg = format!("\ + |err: &mut Diag<'_>, mut msg: String, suggs: Vec| { + if let [sugg] = suggs.as_slice() { + err.help(format!("\ trait `{}` provides `{item_name}` is implemented but not reachable", - sugg[0].trim() - ); - err.help(msg); + sugg.trim(), + )); } else { - msg += &format!(" but {} not reachable", pluralize!("is", sugg.len())); + msg += &format!(" but {} not reachable", pluralize!("is", suggs.len())); err.span_suggestions( span, msg, - sugg, + suggs, Applicability::MaybeIncorrect, ); } @@ -2988,11 +2987,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if local_spans.primary_span().is_some() { - let msg = if local_preds.len() == 1 { + let msg = if let [local_pred] = local_preds.as_slice() { format!( "an implementation of `{}` might be missing for `{}`", - local_preds[0].trait_ref.print_trait_sugared(), - local_preds[0].self_ty() + local_pred.trait_ref.print_trait_sugared(), + local_pred.self_ty() ) } else { format!( @@ -3034,11 +3033,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } if foreign_spans.primary_span().is_some() { - let msg = if foreign_preds.len() == 1 { + let msg = if let [foreign_pred] = foreign_preds.as_slice() { format!( "the foreign item type `{}` doesn't implement `{}`", - foreign_preds[0].self_ty(), - foreign_preds[0].trait_ref.print_trait_sugared() + foreign_pred.self_ty(), + foreign_pred.trait_ref.print_trait_sugared() ) } else { format!( @@ -3388,26 +3387,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); self.suggest_use_candidates(candidates, |accessible_sugg, inaccessible_sugg, span| { - let suggest_for_access = |err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| { + let suggest_for_access = |err: &mut Diag<'_>, mut msg: String, suggs: Vec<_>| { msg += &format!( "; perhaps you want to import {one_of}", - one_of = if sugg.len() == 1 { "it" } else { "one of them" }, + one_of = if suggs.len() == 1 { "it" } else { "one of them" }, ); - err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect); + err.span_suggestions(span, msg, suggs, Applicability::MaybeIncorrect); }; - let suggest_for_privacy = |err: &mut Diag<'_>, sugg: Vec| { + let suggest_for_privacy = |err: &mut Diag<'_>, suggs: Vec| { let msg = format!( "{this_trait_is} implemented but not reachable", - this_trait_is = if sugg.len() == 1 { - format!("trait `{}` which provides `{item_name}` is", sugg[0].trim()) + this_trait_is = if let [sugg] = suggs.as_slice() { + format!("trait `{}` which provides `{item_name}` is", sugg.trim()) } else { format!("the following traits which provide `{item_name}` are") } ); - if sugg.len() == 1 { + if suggs.len() == 1 { err.help(msg); } else { - err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect); + err.span_suggestions(span, msg, suggs, Applicability::MaybeIncorrect); } }; if accessible_sugg.is_empty() { diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index e64f4ebf45df..55f002291f07 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -219,7 +219,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // `async-await/async-closures/force-move-due-to-inferred-kind.rs`. // // 2. If the coroutine-closure is forced to be `FnOnce` due to the way it - // uses its upvars, but not *all* upvars would force the closure to `FnOnce`. + // uses its upvars (e.g. it consumes a non-copy value), but not *all* upvars + // would force the closure to `FnOnce`. // See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`. // // This would lead to an impossible to satisfy situation, since `AsyncFnOnce` @@ -227,11 +228,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we force the inner coroutine to also be `move`. This only matters for // coroutine-closures that are `move` since otherwise they themselves will // be borrowing from the outer environment, so there's no self-borrows occuring. - // - // One *important* note is that we do a call to `process_collected_capture_information` - // to eagerly test whether the coroutine would end up `FnOnce`, but we do this - // *before* capturing all the closure args by-value below, since that would always - // cause the analysis to return `FnOnce`. if let UpvarArgs::Coroutine(..) = args && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) = self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind") @@ -246,19 +242,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_clause = hir::CaptureBy::Value { move_kw }; } // (2.) The way that the closure uses its upvars means it's `FnOnce`. - else if let (_, ty::ClosureKind::FnOnce, _) = self - .process_collected_capture_information( - capture_clause, - &delegate.capture_information, - ) - { + else if self.coroutine_body_consumes_upvars(closure_def_id, body) { capture_clause = hir::CaptureBy::Value { move_kw }; } } // As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode // to `ByRef` for the `async {}` block internal to async fns/closure. This means - // that we would *not* be moving all of the parameters into the async block by default. + // that we would *not* be moving all of the parameters into the async block in all cases. + // For example, when one of the arguments is `Copy`, we turn a consuming use into a copy of + // a reference, so for `async fn x(t: i32) {}`, we'd only take a reference to `t`. // // We force all of these arguments to be captured by move before we do expr use analysis. // @@ -535,6 +528,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Determines whether the body of the coroutine uses its upvars in a way that + /// consumes (i.e. moves) the value, which would force the coroutine to `FnOnce`. + /// In a more detailed comment above, we care whether this happens, since if + /// this happens, we want to force the coroutine to move all of the upvars it + /// would've borrowed from the parent coroutine-closure. + /// + /// This only really makes sense to be called on the child coroutine of a + /// coroutine-closure. + fn coroutine_body_consumes_upvars( + &self, + coroutine_def_id: LocalDefId, + body: &'tcx hir::Body<'tcx>, + ) -> bool { + // This block contains argument capturing details. Since arguments + // aren't upvars, we do not care about them for determining if the + // coroutine body actually consumes its upvars. + let hir::ExprKind::Block(&hir::Block { expr: Some(body), .. }, None) = body.value.kind + else { + bug!(); + }; + // Specifically, we only care about the *real* body of the coroutine. + // We skip out into the drop-temps within the block of the body in order + // to skip over the args of the desugaring. + let hir::ExprKind::DropTemps(body) = body.kind else { + bug!(); + }; + + let mut delegate = InferBorrowKind { + closure_def_id: coroutine_def_id, + capture_information: Default::default(), + fake_reads: Default::default(), + }; + + let _ = euv::ExprUseVisitor::new( + &FnCtxt::new(self, self.tcx.param_env(coroutine_def_id), coroutine_def_id), + &mut delegate, + ) + .consume_expr(body); + + let (_, kind, _) = self.process_collected_capture_information( + hir::CaptureBy::Ref, + &delegate.capture_information, + ); + + matches!(kind, ty::ClosureKind::FnOnce) + } + // Returns a list of `Ty`s for each upvar. fn final_upvar_tys(&self, closure_id: LocalDefId) -> Vec> { self.typeck_results diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 8c99b1f44476..96a6f52d60b6 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -818,6 +818,19 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { }); sess.time("layout_testing", || layout_test::test_layout(tcx)); sess.time("abi_testing", || abi_test::test_abi(tcx)); + + // If `-Zvalidate-mir` is set, we also want to compute the final MIR for each item + // (either its `mir_for_ctfe` or `optimized_mir`) since that helps uncover any bugs + // in MIR optimizations that may only be reachable through codegen, or other codepaths + // that requires the optimized/ctfe MIR, such as polymorphization, coroutine bodies, + // or evaluating consts. + if tcx.sess.opts.unstable_opts.validate_mir { + sess.time("ensuring_final_MIR_is_computable", || { + tcx.hir().par_body_owners(|def_id| { + tcx.instance_mir(ty::InstanceKind::Item(def_id.into())); + }); + }); + } } /// Runs the type-checking, region checking and other miscellaneous analysis diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index ce3b2f77f210..34f2dca7c42f 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -315,7 +315,8 @@ fn test_search_paths_tracking_hash_different_order() { let early_dcx = EarlyDiagCtxt::new(JSON); const JSON: ErrorOutputType = ErrorOutputType::Json { pretty: false, - json_rendered: HumanReadableErrorType::Default(ColorConfig::Never), + json_rendered: HumanReadableErrorType::Default, + color_config: ColorConfig::Never, }; let push = |opts: &mut Options, search_path| { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index d0be6cbee6e1..7a394a6d6c1a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -775,6 +775,10 @@ lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::Manual .label = argument has type `{$arg_ty}` .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value +lint_unexpected_builtin_cfg = unexpected `--cfg {$cfg}` flag + .controlled_by = config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}` + .incoherent = manually setting a built-in cfg can and does create incoherent behaviors + lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_println}` to the top of the `build.rs` lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg} diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d8674817cb5e..6b36944b2088 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1924,14 +1924,13 @@ declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMEN impl ExplicitOutlivesRequirements { fn lifetimes_outliving_lifetime<'tcx>( tcx: TyCtxt<'tcx>, - inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)], + inferred_outlives: impl Iterator, Span)>, item: DefId, lifetime: DefId, ) -> Vec> { let item_generics = tcx.generics_of(item); inferred_outlives - .iter() .filter_map(|(clause, _)| match clause.kind().skip_binder() { ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a { ty::ReEarlyParam(ebr) @@ -1947,11 +1946,10 @@ impl ExplicitOutlivesRequirements { } fn lifetimes_outliving_type<'tcx>( - inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)], + inferred_outlives: impl Iterator, Span)>, index: u32, ) -> Vec> { inferred_outlives - .iter() .filter_map(|(clause, _)| match clause.kind().skip_binder() { ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => { a.is_param(index).then_some(b) @@ -2094,7 +2092,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { ( Self::lifetimes_outliving_lifetime( cx.tcx, - inferred_outlives, + // don't warn if the inferred span actually came from the predicate we're looking at + // this happens if the type is recursively defined + inferred_outlives + .iter() + .filter(|(_, span)| !predicate.span.contains(*span)), item.owner_id.to_def_id(), region_def_id, ), @@ -2116,7 +2118,14 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { }; let index = ty_generics.param_def_id_to_index[&def_id]; ( - Self::lifetimes_outliving_type(inferred_outlives, index), + Self::lifetimes_outliving_type( + // don't warn if the inferred span actually came from the predicate we're looking at + // this happens if the type is recursively defined + inferred_outlives.iter().filter(|(_, span)| { + !predicate.span.contains(*span) + }), + index, + ), &predicate.bounds, predicate.span, predicate.origin == PredicateOrigin::WhereClause, diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 11ad1aa0e8d1..c9e2eee16b3b 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -533,7 +533,7 @@ pub struct EarlyContext<'a> { } impl EarlyContext<'_> { - /// Emit a lint at the appropriate level, with an optional associated span and an existing + /// Emit a lint at the appropriate level, with an associated span and an existing /// diagnostic. /// /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature @@ -544,7 +544,21 @@ impl EarlyContext<'_> { span: MultiSpan, diagnostic: BuiltinLintDiag, ) { - self.opt_span_lint(lint, Some(span), |diag| { + self.opt_span_lint_with_diagnostics(lint, Some(span), diagnostic); + } + + /// Emit a lint at the appropriate level, with an optional associated span and an existing + /// diagnostic. + /// + /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature + #[rustc_lint_diagnostics] + pub fn opt_span_lint_with_diagnostics( + &self, + lint: &'static Lint, + span: Option, + diagnostic: BuiltinLintDiag, + ) { + self.opt_span_lint(lint, span, |diag| { diagnostics::decorate_lint(self.sess(), diagnostic, diag); }); } diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index a96af0764772..f289d4c81b3c 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -438,5 +438,8 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & BuiltinLintDiag::OutOfScopeMacroCalls { path } => { lints::OutOfScopeMacroCalls { path }.decorate_lint(diag) } + BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => { + lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag) + } } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 7b04e8c39e60..6fb0a6246442 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -47,7 +47,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { fn inlined_check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint; - self.context.span_lint_with_diagnostics(lint_id.lint, span, diagnostic); + self.context.opt_span_lint_with_diagnostics(lint_id.lint, span, diagnostic); } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1a657d31865a..03962d796f4e 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2379,6 +2379,16 @@ pub mod unexpected_cfg_value { } } +#[derive(LintDiagnostic)] +#[diag(lint_unexpected_builtin_cfg)] +#[note(lint_controlled_by)] +#[note(lint_incoherent)] +pub struct UnexpectedBuiltinCfg { + pub(crate) cfg: String, + pub(crate) cfg_name: Symbol, + pub(crate) controlled_by: &'static str, +} + #[derive(LintDiagnostic)] #[diag(lint_macro_use_deprecated)] #[help] diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index ce7d203d8c05..d7fd41c0ad7a 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -527,11 +527,11 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { // Lint for constants that look like binding identifiers (#7526) if let PatKind::Path(hir::QPath::Resolved(None, path)) = p.kind { if let Res::Def(DefKind::Const, _) = path.res { - if path.segments.len() == 1 { + if let [segment] = path.segments { NonUpperCaseGlobals::check_upper_case( cx, "constant in pattern", - &path.segments[0].ident, + &segment.ident, ); } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 5b17c0d718a4..51896da893cd 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -211,15 +211,12 @@ fn lint_overflowing_range_endpoint<'tcx>( if !is_range_literal(struct_expr) { return false; }; - let ExprKind::Struct(_, eps, _) = &struct_expr.kind else { return false }; - if eps.len() != 2 { - return false; - } + let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false }; // We can suggest using an inclusive range // (`..=`) instead only if it is the `end` that is // overflowing and only by 1. - if !(eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max) { + if !(end.expr.hir_id == expr.hir_id && lit_val - 1 == max) { return false; }; @@ -232,7 +229,7 @@ fn lint_overflowing_range_endpoint<'tcx>( }; let sub_sugg = if expr.span.lo() == lit_span.lo() { - let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false }; + let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false }; UseInclusiveRange::WithoutParen { sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), start, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 795333224ba0..553d9db12c57 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -675,6 +675,13 @@ trait UnusedDelimLint { return true; } + // Do not lint against parentheses around `&raw [const|mut] expr`. + // These parentheses will have to be added e.g. when calling a method on the result of this + // expression, and we want to avoid churn wrt adding and removing parentheses. + if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) { + return true; + } + // Check if LHS needs parens to prevent false-positives in cases like // `fn x() -> u8 { ({ 0 } + 1) }`. // @@ -801,7 +808,7 @@ trait UnusedDelimLint { return; } let spans = match value.kind { - ast::ExprKind::Block(ref block, None) if block.stmts.len() == 1 => block.stmts[0] + ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => stmt .span .find_ancestor_inside(value.span) .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))), @@ -1537,14 +1544,12 @@ impl UnusedImportBraces { } // Trigger the lint only if there is one nested item - if items.len() != 1 { - return; - } + let [(tree, _)] = items.as_slice() else { return }; // Trigger the lint if the nested item is a non-self single item - let node_name = match items[0].0.kind { + let node_name = match tree.kind { ast::UseTreeKind::Simple(rename) => { - let orig_ident = items[0].0.prefix.segments.last().unwrap().ident; + let orig_ident = tree.prefix.segments.last().unwrap().ident; if orig_ident.name == kw::SelfLower { return; } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index ff0bdfcc9d26..c731b03a8759 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -42,6 +42,7 @@ declare_lint_pass! { DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, ELIDED_LIFETIMES_IN_PATHS, + EXPLICIT_BUILTIN_CFGS_IN_FLAGS, EXPORTED_PRIVATE_DEPENDENCIES, FFI_UNWIND_CALLS, FORBIDDEN_LINT_GROUPS, @@ -3288,6 +3289,39 @@ declare_lint! { "detects unexpected names and values in `#[cfg]` conditions", } +declare_lint! { + /// The `explicit_builtin_cfgs_in_flags` lint detects builtin cfgs set via the `--cfg` flag. + /// + /// ### Example + /// + /// ```text + /// rustc --cfg unix + /// ``` + /// + /// ```rust,ignore (needs command line option) + /// fn main() {} + /// ``` + /// + /// This will produce: + /// + /// ```text + /// error: unexpected `--cfg unix` flag + /// | + /// = note: config `unix` is only supposed to be controlled by `--target` + /// = note: manually setting a built-in cfg can and does create incoherent behaviors + /// = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// Setting builtin cfgs can and does produce incoherent behavior, it's better to the use + /// the appropriate `rustc` flag that controls the config. For example setting the `windows` + /// cfg but on Linux based target. + pub EXPLICIT_BUILTIN_CFGS_IN_FLAGS, + Deny, + "detects builtin cfgs set via the `--cfg`" +} + declare_lint! { /// The `repr_transparent_external_private_fields` lint /// detects types marked `#[repr(transparent)]` that (transitively) diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 19efa36b40fb..0f07de43e80e 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -746,6 +746,11 @@ pub enum BuiltinLintDiag { OutOfScopeMacroCalls { path: String, }, + UnexpectedBuiltinCfg { + cfg: String, + cfg_name: Symbol, + controlled_by: &'static str, + }, } /// Lints that are buffered up early on in the `Session` before the @@ -753,7 +758,7 @@ pub enum BuiltinLintDiag { #[derive(Debug)] pub struct BufferedEarlyLint { /// The span of code that we are linting on. - pub span: MultiSpan, + pub span: Option, /// The `NodeId` of the AST node that generated the lint. pub node_id: NodeId, @@ -791,7 +796,7 @@ impl LintBuffer { self.add_early_lint(BufferedEarlyLint { lint_id: LintId::of(lint), node_id, - span: span.into(), + span: Some(span.into()), diagnostic, }); } diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 4c1f78e6bee3..b2ff9efb41cb 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -259,6 +259,7 @@ fn main() { cmd.args(&components); for lib in output(&mut cmd).split_whitespace() { + let mut is_static = false; let name = if let Some(stripped) = lib.strip_prefix("-l") { stripped } else if let Some(stripped) = lib.strip_prefix('-') { @@ -266,8 +267,24 @@ fn main() { } else if Path::new(lib).exists() { // On MSVC llvm-config will print the full name to libraries, but // we're only interested in the name part - let name = Path::new(lib).file_name().unwrap().to_str().unwrap(); - name.trim_end_matches(".lib") + // On Unix when we get a static library llvm-config will print the + // full name and we *are* interested in the path, but we need to + // handle it separately. For example, when statically linking to + // libzstd llvm-config will output something like + // -lrt -ldl -lm -lz /usr/local/lib/libzstd.a -lxml2 + // and we transform the zstd part into + // cargo:rustc-link-search-native=/usr/local/lib + // cargo:rustc-link-lib=static=zstd + let path = Path::new(lib); + if lib.ends_with(".a") { + is_static = true; + println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); + let name = path.file_stem().unwrap().to_str().unwrap(); + name.trim_start_matches("lib") + } else { + let name = path.file_name().unwrap().to_str().unwrap(); + name.trim_end_matches(".lib") + } } else if lib.ends_with(".lib") { // Some MSVC libraries just come up with `.lib` tacked on, so chop // that off @@ -285,7 +302,13 @@ fn main() { continue; } - let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" }; + let kind = if name.starts_with("LLVM") { + llvm_kind + } else if is_static { + "static" + } else { + "dylib" + }; println!("cargo:rustc-link-lib={kind}={name}"); } diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 415399ed06c7..24fe1ebc07e4 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -41,6 +41,9 @@ metadata_crate_dep_multiple = metadata_crate_dep_not_static = `{$crate_name}` was unavailable as a static crate, preventing fully static linking +metadata_crate_dep_rustc_driver = + `feature(rustc_private)` is needed to link to the compiler's `rustc_driver` library + metadata_crate_location_unknown_type = extern location for {$crate_name} is of an unknown type: {$path} diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 17fd260fd794..39fa23766b5d 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -51,7 +51,7 @@ //! Additionally, the algorithm is geared towards finding *any* solution rather //! than finding a number of solutions (there are normally quite a few). -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::CrateNum; use rustc_middle::bug; use rustc_middle::middle::dependency_format::{Dependencies, DependencyList, Linkage}; @@ -59,12 +59,14 @@ use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; use rustc_session::cstore::CrateDepKind; use rustc_session::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; +use rustc_span::sym; use tracing::info; use crate::creader::CStore; use crate::errors::{ BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, LibRequired, - NonStaticCrateDep, RequiredPanicStrategy, RlibRequired, RustcLibRequired, TwoPanicRuntimes, + NonStaticCrateDep, RequiredPanicStrategy, RlibRequired, RustcDriverHelp, RustcLibRequired, + TwoPanicRuntimes, }; pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies { @@ -160,25 +162,49 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { Linkage::Dynamic | Linkage::IncludedFromDylib => {} } + let all_dylibs = || { + tcx.crates(()).iter().filter(|&&cnum| { + !tcx.dep_kind(cnum).macros_only() && tcx.used_crate_source(cnum).dylib.is_some() + }) + }; + + let mut upstream_in_dylibs = FxHashSet::default(); + + if tcx.features().rustc_private { + // We need this to prevent users of `rustc_driver` from linking dynamically to `std` + // which does not work as `std` is also statically linked into `rustc_driver`. + + // Find all libraries statically linked to upstream dylibs. + for &cnum in all_dylibs() { + let deps = tcx.dylib_dependency_formats(cnum); + for &(depnum, style) in deps.iter() { + if let RequireStatic = style { + upstream_in_dylibs.insert(depnum); + } + } + } + } + let mut formats = FxHashMap::default(); // Sweep all crates for found dylibs. Add all dylibs, as well as their // dependencies, ensuring there are no conflicts. The only valid case for a // dependency to be relied upon twice is for both cases to rely on a dylib. - for &cnum in tcx.crates(()).iter() { - if tcx.dep_kind(cnum).macros_only() { + for &cnum in all_dylibs() { + if upstream_in_dylibs.contains(&cnum) { + info!("skipping dylib: {}", tcx.crate_name(cnum)); + // If this dylib is also available statically linked to another dylib + // we try to use that instead. continue; } + let name = tcx.crate_name(cnum); - let src = tcx.used_crate_source(cnum); - if src.dylib.is_some() { - info!("adding dylib: {}", name); - add_library(tcx, cnum, RequireDynamic, &mut formats, &mut unavailable_as_static); - let deps = tcx.dylib_dependency_formats(cnum); - for &(depnum, style) in deps.iter() { - info!("adding {:?}: {}", style, tcx.crate_name(depnum)); - add_library(tcx, depnum, style, &mut formats, &mut unavailable_as_static); - } + info!("adding dylib: {}", name); + add_library(tcx, cnum, RequireDynamic, &mut formats, &mut unavailable_as_static); + let deps = tcx.dylib_dependency_formats(cnum); + for &(depnum, style) in deps.iter() { + info!("adding {:?}: {}", style, tcx.crate_name(depnum)); + add_library(tcx, depnum, style, &mut formats, &mut unavailable_as_static); } } @@ -268,12 +294,15 @@ fn add_library( // This error is probably a little obscure, but I imagine that it // can be refined over time. if link2 != link || link == RequireStatic { + let linking_to_rustc_driver = tcx.sess.psess.unstable_features.is_nightly_build() + && tcx.crates(()).iter().any(|&cnum| tcx.crate_name(cnum) == sym::rustc_driver); tcx.dcx().emit_err(CrateDepMultiple { crate_name: tcx.crate_name(cnum), non_static_deps: unavailable_as_static .drain(..) .map(|cnum| NonStaticCrateDep { crate_name: tcx.crate_name(cnum) }) .collect(), + rustc_driver_help: linking_to_rustc_driver.then_some(RustcDriverHelp), }); } } diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index 89970dddf9fd..42dec978b78c 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -38,6 +38,8 @@ pub struct CrateDepMultiple { pub crate_name: Symbol, #[subdiagnostic] pub non_static_deps: Vec, + #[subdiagnostic] + pub rustc_driver_help: Option, } #[derive(Subdiagnostic)] @@ -46,6 +48,10 @@ pub struct NonStaticCrateDep { pub crate_name: Symbol, } +#[derive(Subdiagnostic)] +#[help(metadata_crate_dep_rustc_driver)] +pub struct RustcDriverHelp; + #[derive(Diagnostic)] #[diag(metadata_two_panic_runtimes)] pub struct TwoPanicRuntimes { diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index e3d7dff3c66b..37c10b14054c 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -61,10 +61,6 @@ macro_rules! arena_types { [] dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>, [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>, [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>, - [] canonical_goal_evaluation: - rustc_type_ir::solve::inspect::CanonicalGoalEvaluationStep< - rustc_middle::ty::TyCtxt<'tcx> - >, [] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>, [] type_op_subtype: rustc_middle::infer::canonical::Canonical<'tcx, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 4c243e6330b9..edab6b5ebde8 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1165,17 +1165,23 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { } Node::ImplItem(ii) => { let kind = match ii.kind { - ImplItemKind::Const(..) => "assoc const", - ImplItemKind::Fn(..) => "method", - ImplItemKind::Type(_) => "assoc type", + ImplItemKind::Const(..) => "associated constant", + ImplItemKind::Fn(fn_sig, _) => match fn_sig.decl.implicit_self { + ImplicitSelfKind::None => "associated function", + _ => "method", + }, + ImplItemKind::Type(_) => "associated type", }; format!("{id} ({kind} `{}` in {})", ii.ident, path_str(ii.owner_id.def_id)) } Node::TraitItem(ti) => { let kind = match ti.kind { - TraitItemKind::Const(..) => "assoc constant", - TraitItemKind::Fn(..) => "trait method", - TraitItemKind::Type(..) => "assoc type", + TraitItemKind::Const(..) => "associated constant", + TraitItemKind::Fn(fn_sig, _) => match fn_sig.decl.implicit_self { + ImplicitSelfKind::None => "associated function", + _ => "trait method", + }, + TraitItemKind::Type(..) => "associated type", }; format!("{id} ({kind} `{}` in {})", ti.ident, path_str(ti.owner_id.def_id)) diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index b499604df87e..5bd7736a3f3c 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -28,6 +28,7 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::potential_query_instability)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(allocator_api)] @@ -48,7 +49,6 @@ #![feature(iter_from_coroutine)] #![feature(let_chains)] #![feature(macro_metavar_expr)] -#![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 6a9e67f74dac..8c27cac1ea85 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -228,9 +228,11 @@ pub fn explain_lint_level_source( err.note_once(format!( "`{flag} {hyphen_case_lint_name}` implied by `{flag} {hyphen_case_flag_val}`" )); - err.help_once(format!( - "to override `{flag} {hyphen_case_flag_val}` add `#[allow({name})]`" - )); + if matches!(orig_level, Level::Warn | Level::Deny) { + err.help_once(format!( + "to override `{flag} {hyphen_case_flag_val}` add `#[allow({name})]`" + )); + } } } LintLevelSource::Node { name: lint_attr_name, span, reason, .. } => { diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index ff6a3a9c12d3..b7d290e58d22 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -28,7 +28,7 @@ pub struct CodegenFnAttrs { pub link_ordinal: Option, /// The `#[target_feature(enable = "...")]` attribute and the enabled /// features (only enabled features are supported right now). - pub target_features: Vec, + pub target_features: Vec, /// The `#[linkage = "..."]` attribute on Rust-defined items and the value we found. pub linkage: Option, /// The `#[linkage = "..."]` attribute on foreign items and the value we found. @@ -51,6 +51,15 @@ pub struct CodegenFnAttrs { pub patchable_function_entry: Option, } +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] +pub struct TargetFeature { + /// The name of the target feature (e.g. "avx") + pub name: Symbol, + /// The feature is implied by another feature, rather than explicitly added by the + /// `#[target_feature]` attribute + pub implied: bool, +} + #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)] pub struct PatchableFunctionEntry { /// Nops to prepend to the function diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b6a294326503..075eae029041 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -407,6 +407,10 @@ rustc_queries! { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } + query impl_super_outlives(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + desc { |tcx| "elaborating supertrait outlives for trait of `{}`", 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) @@ -2183,7 +2187,7 @@ rustc_queries! { desc { "looking up supported target features" } } - query implied_target_features(feature: Symbol) -> &'tcx UnordSet { + query implied_target_features(feature: Symbol) -> &'tcx Vec { arena_cache eval_always desc { "looking up implied target features" } diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 0ce5c613c4ce..db56e0016a2d 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -98,6 +98,15 @@ impl AssocItem { } } + pub fn descr(&self) -> &'static str { + match self.kind { + ty::AssocKind::Const => "const", + ty::AssocKind::Fn if self.fn_has_self_parameter => "method", + ty::AssocKind::Fn => "associated function", + ty::AssocKind::Type => "type", + } + } + pub fn is_impl_trait_in_trait(&self) -> bool { self.opt_rpitit_info.is_some() } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8f8fd09c9e4d..8198b2fdc893 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -107,8 +107,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.mk_predefined_opaques_in_body(data) } type DefiningOpaqueTypes = &'tcx ty::List; - type CanonicalGoalEvaluationStepRef = - &'tcx solve::inspect::CanonicalGoalEvaluationStep>; type CanonicalVars = CanonicalVarInfos<'tcx>; fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo]) -> Self::CanonicalVars { self.mk_canonical_var_infos(infos) @@ -277,13 +275,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.debug_assert_args_compatible(def_id, args); } - fn intern_canonical_goal_evaluation_step( - self, - step: solve::inspect::CanonicalGoalEvaluationStep>, - ) -> &'tcx solve::inspect::CanonicalGoalEvaluationStep> { - self.arena.alloc(step) - } - fn mk_type_list_from_iter(self, args: I) -> T::Output where I: Iterator, diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 39c306a720f9..8cb8e9af11cf 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -376,7 +376,7 @@ pub struct GenericPredicates<'tcx> { impl<'tcx> GenericPredicates<'tcx> { pub fn instantiate( - &self, + self, tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, ) -> InstantiatedPredicates<'tcx> { @@ -386,20 +386,20 @@ impl<'tcx> GenericPredicates<'tcx> { } pub fn instantiate_own( - &self, + self, tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, ) -> impl Iterator, Span)> + DoubleEndedIterator + ExactSizeIterator { EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args) } - pub fn instantiate_own_identity(&self) -> impl Iterator, Span)> { + pub fn instantiate_own_identity(self) -> impl Iterator, Span)> { EarlyBinder::bind(self.predicates).iter_identity_copied() } #[instrument(level = "debug", skip(self, tcx))] fn instantiate_into( - &self, + self, tcx: TyCtxt<'tcx>, instantiated: &mut InstantiatedPredicates<'tcx>, args: GenericArgsRef<'tcx>, @@ -413,14 +413,14 @@ impl<'tcx> GenericPredicates<'tcx> { instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp)); } - pub fn instantiate_identity(&self, tcx: TyCtxt<'tcx>) -> InstantiatedPredicates<'tcx> { + pub fn instantiate_identity(self, tcx: TyCtxt<'tcx>) -> InstantiatedPredicates<'tcx> { let mut instantiated = InstantiatedPredicates::empty(); self.instantiate_identity_into(tcx, &mut instantiated); instantiated } fn instantiate_identity_into( - &self, + self, tcx: TyCtxt<'tcx>, instantiated: &mut InstantiatedPredicates<'tcx>, ) { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index d7d27975f60a..9204405d58f1 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -869,7 +869,7 @@ where metadata } } else { - match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() { + match tcx.struct_tail_for_codegen(pointee, cx.param_env()).kind() { ty::Slice(_) | ty::Str => tcx.types.usize, ty::Dynamic(data, _, ty::Dyn) => mk_dyn_vtable(data.principal()), _ => bug!("TyAndLayout::field({:?}): not applicable", this), @@ -1348,7 +1348,7 @@ impl<'tcx> TyCtxt<'tcx> { layout = layout.field(&cx, index); if !layout.is_sized() { // If it is not sized, then the tail must still have at least a known static alignment. - let tail = self.struct_tail_erasing_lifetimes(layout.ty, param_env); + let tail = self.struct_tail_for_codegen(layout.ty, param_env); if !matches!(tail.kind(), ty::Slice(..)) { bug!( "offset of not-statically-aligned field (type {:?}) cannot be computed statically", diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 3cf8531bb62d..365f434a264e 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -186,11 +186,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Should only be called if `ty` has no inference variables and does not /// need its lifetimes preserved (e.g. as part of codegen); otherwise /// normalization attempt may cause compiler bugs. - pub fn struct_tail_erasing_lifetimes( - self, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Ty<'tcx> { + pub fn struct_tail_for_codegen(self, ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> { let tcx = self; tcx.struct_tail_with_normalize(ty, |ty| tcx.normalize_erasing_regions(param_env, ty), || {}) } @@ -203,7 +199,7 @@ impl<'tcx> TyCtxt<'tcx> { /// handle `::Assoc` and `impl Trait`); pass the identity /// function to indicate no normalization should take place. /// - /// See also `struct_tail_erasing_lifetimes`, which is suitable for use + /// See also `struct_tail_for_codegen`, which is suitable for use /// during codegen. pub fn struct_tail_with_normalize( self, @@ -272,13 +268,13 @@ impl<'tcx> TyCtxt<'tcx> { /// Same as applying `struct_tail` on `source` and `target`, but only /// keeps going as long as the two types are instances of the same /// structure definitions. - /// For `(Foo>, Foo)`, the result will be `(Foo, Trait)`, + /// For `(Foo>, Foo)`, the result will be `(Foo, dyn Trait)`, /// whereas struct_tail produces `T`, and `Trait`, respectively. /// /// Should only be called if the types have no inference variables and do /// not need their lifetimes preserved (e.g., as part of codegen); otherwise, /// normalization attempt may cause compiler bugs. - pub fn struct_lockstep_tails_erasing_lifetimes( + pub fn struct_lockstep_tails_for_codegen( self, source: Ty<'tcx>, target: Ty<'tcx>, @@ -296,7 +292,7 @@ impl<'tcx> TyCtxt<'tcx> { /// For `(Foo>, Foo)`, the result will be `(Foo, Trait)`, /// whereas struct_tail produces `T`, and `Trait`, respectively. /// - /// See also `struct_lockstep_tails_erasing_lifetimes`, which is suitable for use + /// See also `struct_lockstep_tails_for_codegen`, which is suitable for use /// during codegen. pub fn struct_lockstep_tails_with_normalize( self, diff --git a/compiler/rustc_mir_build/src/build/matches/match_pair.rs b/compiler/rustc_mir_build/src/build/matches/match_pair.rs index 95fec154918a..ab2bfcbca3aa 100644 --- a/compiler/rustc_mir_build/src/build/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/build/matches/match_pair.rs @@ -208,14 +208,11 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> { subpairs = cx.field_match_pairs(downcast_place, subpatterns); let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| { - i == variant_index || { - (cx.tcx.features().exhaustive_patterns - || cx.tcx.features().min_exhaustive_patterns) - && !v - .inhabited_predicate(cx.tcx, adt_def) - .instantiate(cx.tcx, args) - .apply_ignore_module(cx.tcx, cx.param_env) - } + i == variant_index + || !v + .inhabited_predicate(cx.tcx, adt_def) + .instantiate(cx.tcx, args) + .apply_ignore_module(cx.tcx, cx.param_env) }) && (adt_def.did().is_local() || !adt_def.is_variant_list_non_exhaustive()); if irrefutable { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 48018fcaa36d..54a4204da71e 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -5,6 +5,7 @@ use std::ops::Bound; use rustc_errors::DiagArgValue; use rustc_hir::def::DefKind; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; +use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; use rustc_middle::thir::visit::Visitor; @@ -31,7 +32,7 @@ struct UnsafetyVisitor<'a, 'tcx> { safety_context: SafetyContext, /// The `#[target_feature]` attributes of the body. Used for checking /// calls to functions with `#[target_feature]` (RFC 2396). - body_target_features: &'tcx [Symbol], + body_target_features: &'tcx [TargetFeature], /// When inside the LHS of an assignment to a field, this is the type /// of the LHS and the span of the assignment expression. assignment_info: Option>, @@ -442,14 +443,21 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { // is_like_wasm check in hir_analysis/src/collect.rs let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; if !self.tcx.sess.target.options.is_like_wasm - && !callee_features - .iter() - .all(|feature| self.body_target_features.contains(feature)) + && !callee_features.iter().all(|feature| { + self.body_target_features.iter().any(|f| f.name == feature.name) + }) { let missing: Vec<_> = callee_features .iter() .copied() - .filter(|feature| !self.body_target_features.contains(feature)) + .filter(|feature| { + !feature.implied + && !self + .body_target_features + .iter() + .any(|body_feature| body_feature.name == feature.name) + }) + .map(|feature| feature.name) .collect(); let build_enabled = self .tcx 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 64c6ff952c68..07bf222fcca4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -483,9 +483,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { // Check if the match is exhaustive. let witnesses = report.non_exhaustiveness_witnesses; if !witnesses.is_empty() { - if source == hir::MatchSource::ForLoopDesugar && arms.len() == 2 { + if source == hir::MatchSource::ForLoopDesugar + && let [_, snd_arm] = *arms + { // the for loop pattern is not irrefutable - let pat = &self.thir[arms[1]].pattern; + let pat = &self.thir[snd_arm].pattern; // `pat` should be `Some()` from a desugared for loop. debug_assert_eq!(pat.span.desugaring_kind(), Some(DesugaringKind::ForLoop)); let PatKind::Variant { ref subpatterns, .. } = pat.kind else { bug!() }; @@ -695,9 +697,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { // Emit an extra note if the first uncovered witness would be uninhabited // if we disregard visibility. - let witness_1_is_privately_uninhabited = if (self.tcx.features().exhaustive_patterns - || self.tcx.features().min_exhaustive_patterns) - && let Some(witness_1) = witnesses.get(0) + let witness_1_is_privately_uninhabited = if let Some(witness_1) = witnesses.get(0) && let ty::Adt(adt, args) = witness_1.ty().kind() && adt.is_enum() && let Constructor::Variant(variant_index) = witness_1.ctor() @@ -1059,7 +1059,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary"); } else if cx.is_foreign_non_exhaustive_enum(ty) { err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively")); - } else if cx.is_uninhabited(ty.inner()) && cx.tcx.features().min_exhaustive_patterns { + } else if cx.is_uninhabited(ty.inner()) { // The type is uninhabited yet there is a witness: we must be in the `MaybeInvalid` // case. err.note(format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required")); diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index c3f4bbf1a652..31b207751942 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -350,8 +350,8 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera // An inline asm terminator can normally be chained, except when it diverges or uses asm // goto. InlineAsm { ref targets, .. } => { - if targets.len() == 1 { - CoverageSuccessors::Chainable(targets[0]) + if let [target] = targets[..] { + CoverageSuccessors::Chainable(target) } else { CoverageSuccessors::NotChainable(targets) } diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index e4fec786814d..49e41c35f1f2 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -309,11 +309,11 @@ fn verify_candidate_branch<'tcx>( ) -> bool { // In order for the optimization to be correct, the branch must... // ...have exactly one statement - if branch.statements.len() != 1 { + let [statement] = branch.statements.as_slice() else { return false; - } + }; // ...assign the discriminant of `place` in that statement - let StatementKind::Assign(boxed) = &branch.statements[0].kind else { return false }; + let StatementKind::Assign(boxed) = &statement.kind else { return false }; let (discr_place, Rvalue::Discriminant(from_place)) = &**boxed else { return false }; if *from_place != place { return false; diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index d955b96d06af..e5778f8a05d2 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -116,29 +116,30 @@ impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs { for debug_info in body.var_debug_info.iter_mut() { if let VarDebugInfoContents::Place(place) = &mut debug_info.value { let mut new_projections: Option> = None; - let mut last_deref = 0; - for (i, (base, elem)) in place.iter_projections().enumerate() { + for (base, elem) in place.iter_projections() { let base_ty = base.ty(&body.local_decls, tcx).ty; if elem == PlaceElem::Deref && base_ty.is_box() { - let new_projections = new_projections.get_or_insert_default(); + // Clone the projections before us, since now we need to mutate them. + let new_projections = + new_projections.get_or_insert_with(|| base.projection.to_vec()); let (unique_ty, nonnull_ty, ptr_ty) = build_ptr_tys(tcx, base_ty.boxed_ty(), unique_did, nonnull_did); - new_projections.extend_from_slice(&base.projection[last_deref..]); new_projections.extend_from_slice(&build_projection( unique_ty, nonnull_ty, ptr_ty, )); new_projections.push(PlaceElem::Deref); - - last_deref = i; + } else if let Some(new_projections) = new_projections.as_mut() { + // Keep building up our projections list once we've started it. + new_projections.push(elem); } } - if let Some(mut new_projections) = new_projections { - new_projections.extend_from_slice(&place.projection[last_deref..]); + // Store the mutated projections if we actually changed something. + if let Some(new_projections) = new_projections { place.projection = tcx.mk_place_elems(&new_projections); } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index f30732e6aaf3..324ddc5e799d 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -479,7 +479,9 @@ impl<'tcx> Inliner<'tcx> { return Err("incompatible instruction set"); } - if callee_attrs.target_features != self.codegen_fn_attrs.target_features { + let callee_feature_names = callee_attrs.target_features.iter().map(|f| f.name); + let this_feature_names = self.codegen_fn_attrs.target_features.iter().map(|f| f.name); + if callee_feature_names.ne(this_feature_names) { // In general it is not correct to inline a callee with target features that are a // subset of the caller. This is because the callee might contain calls, and the ABI of // those calls depends on the target features of the surrounding function. By moving a @@ -503,6 +505,10 @@ impl<'tcx> Inliner<'tcx> { ) -> Result<(), &'static str> { let tcx = self.tcx; + if let Some(_) = callee_body.tainted_by_errors { + return Err("Body is tainted"); + } + let mut threshold = if self.caller_is_inline_forwarder { self.tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30) } else if cross_crate_inlinable { diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 2fc5f7e536ba..1589653968c2 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -264,9 +264,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { }; // It's definitely not a clone if there are multiple arguments - if args.len() != 1 { - return; - } + let [arg] = &args[..] else { return }; let Some(destination_block) = *target else { return }; @@ -280,7 +278,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { // These types are easily available from locals, so check that before // doing DefId lookups to figure out what we're actually calling. - let arg_ty = args[0].node.ty(self.local_decls, self.tcx); + let arg_ty = arg.node.ty(self.local_decls, self.tcx); let ty::Ref(_region, inner_ty, Mutability::Not) = *arg_ty.kind() else { return }; diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 746d423b7a98..491ae1c0d083 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -898,8 +898,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.param_env, adt_def.non_enum_variant().fields[field].ty(self.tcx, args), ); - if fields.len() == 1 { - let src_ty = fields.raw[0].ty(self.body, self.tcx); + if let [field] = fields.raw.as_slice() { + let src_ty = field.ty(self.body, self.tcx); if !self.mir_assign_valid_types(src_ty, dest_ty) { self.fail(location, "union field has the wrong type"); } @@ -967,11 +967,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, "RawPtr should be in runtime MIR only"); } - if fields.len() != 2 { - self.fail(location, "raw pointer aggregate must have 2 fields"); - } else { - let data_ptr_ty = fields.raw[0].ty(self.body, self.tcx); - let metadata_ty = fields.raw[1].ty(self.body, self.tcx); + if let [data_ptr, metadata] = fields.raw.as_slice() { + let data_ptr_ty = data_ptr.ty(self.body, self.tcx); + let metadata_ty = metadata.ty(self.body, self.tcx); if let ty::RawPtr(in_pointee, in_mut) = data_ptr_ty.kind() { if *in_mut != mutability { self.fail(location, "input and output mutability must match"); @@ -998,6 +996,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, "metadata for pointer-to-thin must be unit"); } } + } else { + self.fail(location, "raw pointer aggregate must have 2 fields"); } } }, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index df2abf6dc9cc..0ae635f9b73e 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1019,7 +1019,7 @@ fn find_vtable_types_for_unsizing<'tcx>( if ty.is_sized(tcx.tcx, param_env) { return false; } - let tail = tcx.struct_tail_erasing_lifetimes(ty, param_env); + let tail = tcx.struct_tail_for_codegen(ty, param_env); match tail.kind() { ty::Foreign(..) => false, ty::Str | ty::Slice(..) | ty::Dynamic(..) => true, @@ -1029,7 +1029,7 @@ fn find_vtable_types_for_unsizing<'tcx>( if type_has_metadata(inner_source) { (inner_source, inner_target) } else { - tcx.struct_lockstep_tails_erasing_lifetimes(inner_source, inner_target, param_env) + tcx.struct_lockstep_tails_for_codegen(inner_source, inner_target, param_env) } }; diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index e69d8d84d7d8..00837f7cdd87 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -458,28 +458,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable { - let bound_sig = self_ty.fn_sig(cx); - let sig = bound_sig.skip_binder(); - let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future); - // `FnDef` and `FnPtr` only implement `AsyncFn*` when their - // return type implements `Future`. - let nested = vec![ - bound_sig - .rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])) - .upcast(cx), - ]; - let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput); - let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); - Ok(( - bound_sig.rebind(AsyncCallableRelevantTypes { - tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()), - output_coroutine_ty: sig.output(), - coroutine_return_ty: future_output_ty, - }), - nested, - )) + ty::FnDef(def_id, _) => { + let sig = self_ty.fn_sig(cx); + if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) { + fn_item_to_async_callable(cx, sig) + } else { + Err(NoSolution) + } } + ty::FnPtr(..) => { + let sig = self_ty.fn_sig(cx); + if sig.skip_binder().is_fn_trait_compatible() { + fn_item_to_async_callable(cx, sig) + } else { + Err(NoSolution) + } + } + ty::Closure(_, args) => { let args = args.as_closure(); let bound_sig = args.sig(); @@ -563,6 +558,29 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable( + cx: I, + bound_sig: ty::Binder>, +) -> Result<(ty::Binder>, Vec), NoSolution> { + let sig = bound_sig.skip_binder(); + let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future); + // `FnDef` and `FnPtr` only implement `AsyncFn*` when their + // return type implements `Future`. + let nested = vec![ + bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx), + ]; + let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput); + let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); + Ok(( + bound_sig.rebind(AsyncCallableRelevantTypes { + tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()), + output_coroutine_ty: sig.output(), + coroutine_return_ty: future_output_ty, + }), + nested, + )) +} + /// Given a coroutine-closure, project to its returned coroutine when we are *certain* /// that the closure's kind is compatible with the goal. fn coroutine_closure_to_certain_coroutine( diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index a3c21666bd67..86fb036cd3df 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -5,11 +5,10 @@ //! see the comment on [ProofTreeBuilder]. use std::marker::PhantomData; -use std::mem; use derive_where::derive_where; use rustc_type_ir::inherent::*; -use rustc_type_ir::{self as ty, search_graph, Interner}; +use rustc_type_ir::{self as ty, Interner}; use crate::delegate::SolverDelegate; use crate::solve::eval_ctxt::canonical; @@ -94,31 +93,10 @@ impl WipGoalEvaluation { } } -#[derive_where(PartialEq, Eq; I: Interner)] -pub(in crate::solve) enum WipCanonicalGoalEvaluationKind { - Overflow, - CycleInStack, - ProvisionalCacheHit, - Interned { final_revision: I::CanonicalGoalEvaluationStepRef }, -} - -impl std::fmt::Debug for WipCanonicalGoalEvaluationKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Overflow => write!(f, "Overflow"), - Self::CycleInStack => write!(f, "CycleInStack"), - Self::ProvisionalCacheHit => write!(f, "ProvisionalCacheHit"), - Self::Interned { final_revision: _ } => { - f.debug_struct("Interned").finish_non_exhaustive() - } - } - } -} - #[derive_where(PartialEq, Eq, Debug; I: Interner)] struct WipCanonicalGoalEvaluation { goal: CanonicalInput, - kind: Option>, + encountered_overflow: bool, /// Only used for uncached goals. After we finished evaluating /// the goal, this is interned and moved into `kind`. final_revision: Option>, @@ -127,25 +105,17 @@ struct WipCanonicalGoalEvaluation { impl WipCanonicalGoalEvaluation { fn finalize(self) -> inspect::CanonicalGoalEvaluation { - // We've already interned the final revision in - // `fn finalize_canonical_goal_evaluation`. - assert!(self.final_revision.is_none()); - let kind = match self.kind.unwrap() { - WipCanonicalGoalEvaluationKind::Overflow => { + inspect::CanonicalGoalEvaluation { + goal: self.goal, + kind: if self.encountered_overflow { + assert!(self.final_revision.is_none()); inspect::CanonicalGoalEvaluationKind::Overflow - } - WipCanonicalGoalEvaluationKind::CycleInStack => { - inspect::CanonicalGoalEvaluationKind::CycleInStack - } - WipCanonicalGoalEvaluationKind::ProvisionalCacheHit => { - inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit - } - WipCanonicalGoalEvaluationKind::Interned { final_revision } => { + } else { + let final_revision = self.final_revision.unwrap().finalize(); inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } - } - }; - - inspect::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() } + }, + result: self.result.unwrap(), + } } } @@ -308,7 +278,7 @@ impl, I: Interner> ProofTreeBuilder { ) -> ProofTreeBuilder { self.nested(|| WipCanonicalGoalEvaluation { goal, - kind: None, + encountered_overflow: false, final_revision: None, result: None, }) @@ -329,11 +299,11 @@ impl, I: Interner> ProofTreeBuilder { } } - pub fn canonical_goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind) { + pub fn canonical_goal_evaluation_overflow(&mut self) { if let Some(this) = self.as_mut() { match this { DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => { - assert_eq!(canonical_goal_evaluation.kind.replace(kind), None); + canonical_goal_evaluation.encountered_overflow = true; } _ => unreachable!(), }; @@ -547,51 +517,3 @@ impl, I: Interner> ProofTreeBuilder { } } } - -impl search_graph::ProofTreeBuilder for ProofTreeBuilder -where - D: SolverDelegate, - I: Interner, -{ - fn try_apply_proof_tree( - &mut self, - proof_tree: Option, - ) -> bool { - if !self.is_noop() { - if let Some(final_revision) = proof_tree { - let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision }; - self.canonical_goal_evaluation_kind(kind); - true - } else { - false - } - } else { - true - } - } - - fn on_provisional_cache_hit(&mut self) { - self.canonical_goal_evaluation_kind(WipCanonicalGoalEvaluationKind::ProvisionalCacheHit); - } - - fn on_cycle_in_stack(&mut self) { - self.canonical_goal_evaluation_kind(WipCanonicalGoalEvaluationKind::CycleInStack); - } - - fn finalize_canonical_goal_evaluation( - &mut self, - tcx: I, - ) -> Option { - self.as_mut().map(|this| match this { - DebugSolver::CanonicalGoalEvaluation(evaluation) => { - let final_revision = mem::take(&mut evaluation.final_revision).unwrap(); - let final_revision = - tcx.intern_canonical_goal_evaluation_step(final_revision.finalize()); - let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision }; - assert_eq!(evaluation.kind.replace(kind), None); - final_revision - } - _ => unreachable!(), - }) - } -} diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index fe053a506e71..81c89fad8e8a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -1,12 +1,13 @@ +use std::convert::Infallible; use std::marker::PhantomData; use rustc_type_ir::inherent::*; -use rustc_type_ir::search_graph::{self, CycleKind, UsageKind}; +use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{CanonicalInput, Certainty, QueryResult}; use rustc_type_ir::Interner; -use super::inspect::{self, ProofTreeBuilder}; -use super::FIXPOINT_STEP_LIMIT; +use super::inspect::ProofTreeBuilder; +use super::{has_no_inference_or_external_constraints, FIXPOINT_STEP_LIMIT}; use crate::delegate::SolverDelegate; /// This type is never constructed. We only use it to implement `search_graph::Delegate` @@ -22,43 +23,48 @@ where { type Cx = D::Interner; + const ENABLE_PROVISIONAL_CACHE: bool = true; + type ValidationScope = Infallible; + fn enter_validation_scope( + _cx: Self::Cx, + _input: CanonicalInput, + ) -> Option { + None + } + const FIXPOINT_STEP_LIMIT: usize = FIXPOINT_STEP_LIMIT; type ProofTreeBuilder = ProofTreeBuilder; + fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool { + inspect.is_noop() + } + const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize = 4; fn recursion_limit(cx: I) -> usize { cx.recursion_limit() } fn initial_provisional_result( cx: I, - kind: CycleKind, + kind: PathKind, input: CanonicalInput, ) -> QueryResult { match kind { - CycleKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes), - CycleKind::Inductive => response_no_constraints(cx, input, Certainty::overflow(false)), + PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes), + PathKind::Inductive => response_no_constraints(cx, input, Certainty::overflow(false)), } } - fn reached_fixpoint( - cx: I, - kind: UsageKind, + fn is_initial_provisional_result( + cx: Self::Cx, + kind: PathKind, input: CanonicalInput, - provisional_result: Option>, result: QueryResult, ) -> bool { - if let Some(r) = provisional_result { - r == result - } else { - match kind { - UsageKind::Single(CycleKind::Coinductive) => { - response_no_constraints(cx, input, Certainty::Yes) == result - } - UsageKind::Single(CycleKind::Inductive) => { - response_no_constraints(cx, input, Certainty::overflow(false)) == result - } - UsageKind::Mixed => false, + match kind { + PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes) == result, + PathKind::Inductive => { + response_no_constraints(cx, input, Certainty::overflow(false)) == result } } } @@ -68,7 +74,7 @@ where inspect: &mut ProofTreeBuilder, input: CanonicalInput, ) -> QueryResult { - inspect.canonical_goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow); + inspect.canonical_goal_evaluation_overflow(); response_no_constraints(cx, input, Certainty::overflow(true)) } @@ -76,6 +82,22 @@ where response_no_constraints(cx, input, Certainty::overflow(false)) } + fn is_ambiguous_result(result: QueryResult) -> bool { + result.is_ok_and(|response| { + has_no_inference_or_external_constraints(response) + && matches!(response.value.certainty, Certainty::Maybe(_)) + }) + } + + fn propagate_ambiguity( + cx: I, + for_input: CanonicalInput, + from_result: QueryResult, + ) -> QueryResult { + let certainty = from_result.unwrap().value.certainty; + response_no_constraints(cx, for_input, certainty) + } + fn step_is_coinductive(cx: I, input: CanonicalInput) -> bool { input.value.goal.predicate.is_coinductive(cx) } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index ccf8dcdf0b6f..cf5d65708ab0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -694,12 +694,12 @@ impl<'a> Parser<'a> { // `foo: ` ExprKind::Path(None, ast::Path { segments, .. }), token::Ident(kw::For | kw::Loop | kw::While, IdentIsRaw::No), - ) if segments.len() == 1 => { + ) if let [segment] = segments.as_slice() => { let snapshot = self.create_snapshot_for_diagnostic(); let label = Label { ident: Ident::from_str_and_span( - &format!("'{}", segments[0].ident), - segments[0].ident.span, + &format!("'{}", segment.ident), + segment.ident.span, ), }; match self.parse_expr_labeled(label, false) { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index baa5eb2df635..8775d792c3d2 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -471,9 +471,8 @@ impl<'a> Parser<'a> { Err(mut err) => { // Maybe the user misspelled `macro_rules` (issue #91227) if self.token.is_ident() - && path.segments.len() == 1 - && edit_distance("macro_rules", &path.segments[0].ident.to_string(), 2) - .is_some() + && let [segment] = path.segments.as_slice() + && edit_distance("macro_rules", &segment.ident.to_string(), 2).is_some() { err.span_suggestion( path.span, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 70d2c98d4f1b..6f82d6b9826b 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -826,7 +826,8 @@ impl<'a> Parser<'a> { // We can only resolve single-segment paths at the moment, because multi-segment paths // require type-checking: see `visit_generic_arg` in `src/librustc_resolve/late.rs`. ast::ExprKind::Path(None, path) - if path.segments.len() == 1 && path.segments[0].args.is_none() => + if let [segment] = path.segments.as_slice() + && segment.args.is_none() => { true } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index b3efb87a4a26..b206f134f0e2 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -408,10 +408,14 @@ impl<'a> Parser<'a> { fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option>> { let eq_consumed = match self.token.kind { token::BinOpEq(..) => { - // Recover `let x = 1` as `let x = 1` + // Recover `let x = 1` as `let x = 1` We must not use `+ BytePos(1)` here + // because `` can be a multi-byte lookalike that was recovered, e.g. `➖=` (the + // `➖` is a U+2796 Heavy Minus Sign Unicode Character) that was recovered as a + // `-=`. + let extra_op_span = self.psess.source_map().start_point(self.token.span); self.dcx().emit_err(errors::CompoundAssignmentExpressionInLet { span: self.token.span, - suggestion: self.token.span.with_hi(self.token.span.lo() + BytePos(1)), + suggestion: extra_op_span, }); self.bump(); true @@ -672,7 +676,7 @@ impl<'a> Parser<'a> { match &expr.kind { ExprKind::Path(None, ast::Path { segments, .. }) - if segments.len() == 1 => + if let [segment] = segments.as_slice() => { if self.token == token::Colon && self.look_ahead(1, |token| { @@ -689,8 +693,8 @@ impl<'a> Parser<'a> { let snapshot = self.create_snapshot_for_diagnostic(); let label = Label { ident: Ident::from_str_and_span( - &format!("'{}", segments[0].ident), - segments[0].ident.span, + &format!("'{}", segment.ident), + segment.ident.span, ), }; match self.parse_expr_labeled(label, false) { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index cf7d724ab63a..2a63ecfe6587 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -125,6 +125,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), [sym::coverage, ..] => self.check_coverage(attr, span, target), [sym::optimize, ..] => self.check_optimize(hir_id, attr, target), + [sym::no_sanitize, ..] => self.check_no_sanitize(hir_id, attr, span, target), [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target), [sym::marker, ..] => self.check_marker(hir_id, attr, span, target), [sym::target_feature, ..] => { @@ -256,7 +257,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::may_dangle // FIXME(dropck_eyepatch) | sym::pointee // FIXME(derive_smart_pointer) | sym::linkage // FIXME(linkage) - | sym::no_sanitize // FIXME(no_sanitize) | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section) | sym::used // handled elsewhere to restrict to static items | sym::repr // handled elsewhere to restrict to type decls items @@ -451,6 +451,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// Checks that `#[no_sanitize(..)]` is applied to a function or method. + fn check_no_sanitize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + fn check_generic_attr( &self, hir_id: HirId, @@ -2208,8 +2213,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr.name_or_empty(), sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect ) && let Some(meta) = attr.meta_item_list() - && meta.len() == 1 - && let Some(item) = meta[0].meta_item() + && let [meta] = meta.as_slice() + && let Some(item) = meta.meta_item() && let MetaItemKind::NameValue(_) = &item.kind && item.path == sym::reason { diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 0537d3a69f6d..5d871bacb1da 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -19,9 +19,7 @@ impl DebuggerVisualizerCollector<'_> { return; }; - let hint = if hints.len() == 1 { - &hints[0] - } else { + let [hint] = hints.as_slice() else { self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span }); return; }; diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index a5c0b13c90be..6c9c848bb10a 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -5,6 +5,7 @@ // tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(feature = "rustc", feature(let_chains))] // tidy-alphabetical-end pub mod constructor; @@ -54,7 +55,6 @@ pub trait PatCx: Sized + fmt::Debug { type PatData: Clone; fn is_exhaustive_patterns_feature_on(&self) -> bool; - fn is_min_exhaustive_patterns_feature_on(&self) -> bool; /// The number of fields for this constructor. fn ctor_arity(&self, ctor: &Constructor, ty: &Self::Ty) -> usize; diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 6290aeb25231..10b7968a1a7e 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -23,6 +23,7 @@ use crate::constructor::{ }; use crate::lints::lint_nonexhaustive_missing_variants; use crate::pat_column::PatternColumn; +use crate::rustc::print::EnumInfo; use crate::usefulness::{compute_match_usefulness, PlaceValidity}; use crate::{errors, Captures, PatCx, PrivateUninhabitedField}; @@ -237,9 +238,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| { let is_visible = adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); - let is_uninhabited = (cx.tcx.features().exhaustive_patterns - || cx.tcx.features().min_exhaustive_patterns) - && cx.is_uninhabited(*ty); + let is_uninhabited = cx.is_uninhabited(*ty); let skip = is_uninhabited && (!is_visible || is_non_exhaustive); (ty, PrivateUninhabitedField(skip)) }); @@ -826,77 +825,64 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> { use print::{FieldPat, Pat, PatKind}; let cx = self; - let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild); - let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p))); + let hoist = |p| Box::new(cx.hoist_witness_pat(p)); let kind = match pat.ctor() { Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) }, IntRange(range) => return self.hoist_pat_range(range, *pat.ty()), - Struct | Variant(_) | UnionField => match pat.ty().kind() { - ty::Tuple(..) => PatKind::Leaf { - subpatterns: subpatterns - .enumerate() - .map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern }) - .collect(), - }, - ty::Adt(adt_def, _) if adt_def.is_box() => { - // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside - // of `std`). So this branch is only reachable when the feature is enabled and - // the pattern is a box pattern. - PatKind::Deref { subpattern: subpatterns.next().unwrap() } - } - ty::Adt(adt_def, _args) => { - let variant_index = RustcPatCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); - let subpatterns = subpatterns - .enumerate() - .map(|(i, pattern)| FieldPat { field: FieldIdx::new(i), pattern }) - .collect(); - - if adt_def.is_enum() { - PatKind::Variant { adt_def: *adt_def, variant_index, subpatterns } - } else { - PatKind::Leaf { subpatterns } - } - } - _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()), - }, - // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should - // be careful to reconstruct the correct constant pattern here. However a string - // literal pattern will never be reported as a non-exhaustiveness witness, so we - // ignore this issue. - Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, - Slice(slice) => { - match slice.kind { - SliceKind::FixedLen(_) => PatKind::Slice { - prefix: subpatterns.collect(), - slice: None, - suffix: Box::new([]), + Struct if pat.ty().is_box() => { + // Outside of the `alloc` crate, the only way to create a struct pattern + // of type `Box` is to use a `box` pattern via #[feature(box_patterns)]. + PatKind::Box { subpattern: hoist(&pat.fields[0]) } + } + Struct | Variant(_) | UnionField => { + let enum_info = match *pat.ty().kind() { + ty::Adt(adt_def, _) if adt_def.is_enum() => EnumInfo::Enum { + adt_def, + variant_index: RustcPatCtxt::variant_index_for_adt(pat.ctor(), adt_def), }, - SliceKind::VarLen(prefix, _) => { - let mut subpatterns = subpatterns.peekable(); - let mut prefix: Vec<_> = subpatterns.by_ref().take(prefix).collect(); - if slice.array_len.is_some() { - // Improves diagnostics a bit: if the type is a known-size array, instead - // of reporting `[x, _, .., _, y]`, we prefer to report `[x, .., y]`. - // This is incorrect if the size is not known, since `[_, ..]` captures - // arrays of lengths `>= 1` whereas `[..]` captures any length. - while !prefix.is_empty() && is_wildcard(prefix.last().unwrap()) { - prefix.pop(); - } - while subpatterns.peek().is_some() - && is_wildcard(subpatterns.peek().unwrap()) - { - subpatterns.next(); - } - } - let suffix: Box<[_]> = subpatterns.collect(); - let wild = Pat { ty: pat.ty().inner(), kind: PatKind::Wild }; - PatKind::Slice { - prefix: prefix.into_boxed_slice(), - slice: Some(Box::new(wild)), - suffix, - } + ty::Adt(..) | ty::Tuple(..) => EnumInfo::NotEnum, + _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()), + }; + + let subpatterns = pat + .iter_fields() + .enumerate() + .map(|(i, pat)| FieldPat { field: FieldIdx::new(i), pattern: hoist(pat) }) + .collect::>(); + + PatKind::StructLike { enum_info, subpatterns } + } + Ref => PatKind::Deref { subpattern: hoist(&pat.fields[0]) }, + Slice(slice) => { + let (prefix_len, has_dot_dot) = match slice.kind { + SliceKind::FixedLen(len) => (len, false), + SliceKind::VarLen(prefix_len, _) => (prefix_len, true), + }; + + let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len); + + // If the pattern contains a `..`, but is applied to values of statically-known + // length (arrays), then we can slightly simplify diagnostics by merging any + // adjacent wildcard patterns into the `..`: `[x, _, .., _, y]` => `[x, .., y]`. + // (This simplification isn't allowed for slice values, because in that case + // `[x, .., y]` would match some slices that `[x, _, .., _, y]` would not.) + if has_dot_dot && slice.array_len.is_some() { + while let [rest @ .., last] = prefix + && would_print_as_wildcard(cx.tcx, last) + { + prefix = rest; + } + while let [first, rest @ ..] = suffix + && would_print_as_wildcard(cx.tcx, first) + { + suffix = rest; } } + + let prefix = prefix.iter().map(hoist).collect(); + let suffix = suffix.iter().map(hoist).collect(); + + PatKind::Slice { prefix, has_dot_dot, suffix } } &Str(value) => PatKind::Constant { value }, Never if self.tcx.features().never_patterns => PatKind::Never, @@ -914,6 +900,22 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } } +/// Returns `true` if the given pattern would be printed as a wildcard (`_`). +fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool { + match p.ctor() { + Constructor::IntRange(IntRange { + lo: MaybeInfiniteInt::NegInfinity, + hi: MaybeInfiniteInt::PosInfinity, + }) + | Constructor::Wildcard + | Constructor::NonExhaustive + | Constructor::Hidden + | Constructor::PrivateUninhabited => true, + Constructor::Never if !tcx.features().never_patterns => true, + _ => false, + } +} + impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { type Ty = RevealedTy<'tcx>; type Error = ErrorGuaranteed; @@ -925,9 +927,6 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { fn is_exhaustive_patterns_feature_on(&self) -> bool { self.tcx.features().exhaustive_patterns } - fn is_min_exhaustive_patterns_feature_on(&self) -> bool { - self.tcx.features().min_exhaustive_patterns - } fn ctor_arity(&self, ctor: &crate::constructor::Constructor, ty: &Self::Ty) -> usize { self.ctor_arity(ctor, *ty) diff --git a/compiler/rustc_pattern_analysis/src/rustc/print.rs b/compiler/rustc_pattern_analysis/src/rustc/print.rs index 4b76764e8b13..7d6387146054 100644 --- a/compiler/rustc_pattern_analysis/src/rustc/print.rs +++ b/compiler/rustc_pattern_analysis/src/rustc/print.rs @@ -12,7 +12,7 @@ use std::fmt; use rustc_middle::thir::PatRange; -use rustc_middle::ty::{self, AdtDef, Ty}; +use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::sym; use rustc_target::abi::{FieldIdx, VariantIdx}; @@ -33,14 +33,13 @@ pub(crate) struct Pat<'tcx> { pub(crate) enum PatKind<'tcx> { Wild, - Variant { - adt_def: AdtDef<'tcx>, - variant_index: VariantIdx, + StructLike { + enum_info: EnumInfo<'tcx>, subpatterns: Vec>, }, - Leaf { - subpatterns: Vec>, + Box { + subpattern: Box>, }, Deref { @@ -55,7 +54,9 @@ pub(crate) enum PatKind<'tcx> { Slice { prefix: Box<[Box>]>, - slice: Option>>, + /// True if this slice-like pattern should include a `..` between the + /// prefix and suffix. + has_dot_dot: bool, suffix: Box<[Box>]>, }, @@ -64,130 +65,155 @@ pub(crate) enum PatKind<'tcx> { impl<'tcx> fmt::Display for Pat<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Printing lists is a chore. - 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.kind { PatKind::Wild => write!(f, "_"), PatKind::Never => write!(f, "!"), - PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => { - let variant_and_name = match self.kind { - PatKind::Variant { adt_def, variant_index, .. } => ty::tls::with(|tcx| { - let variant = adt_def.variant(variant_index); - let adt_did = adt_def.did(); - let name = if tcx.get_diagnostic_item(sym::Option) == Some(adt_did) - || tcx.get_diagnostic_item(sym::Result) == Some(adt_did) - { - variant.name.to_string() - } else { - format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name) - }; - Some((variant, name)) - }), - _ => self.ty.ty_adt_def().and_then(|adt_def| { - if !adt_def.is_enum() { - ty::tls::with(|tcx| { - Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did()))) - }) - } else { - None - } - }), - }; - - if let Some((variant, name)) = &variant_and_name { - write!(f, "{name}")?; - - // Only for Adt we can have `S {...}`, - // which we handle separately here. - if variant.ctor.is_none() { - write!(f, " {{ ")?; - - let mut printed = 0; - for p in subpatterns { - if let PatKind::Wild = p.pattern.kind { - continue; - } - let name = variant.fields[p.field].name; - write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; - printed += 1; - } - - let is_union = self.ty.ty_adt_def().is_some_and(|adt| adt.is_union()); - if printed < variant.fields.len() && (!is_union || printed == 0) { - write!(f, "{}..", start_or_comma())?; - } - - return write!(f, " }}"); - } - } - - let num_fields = - variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len()); - if num_fields != 0 || variant_and_name.is_none() { - write!(f, "(")?; - for i in 0..num_fields { - write!(f, "{}", start_or_comma())?; - - // Common case: the field is where we expect it. - if let Some(p) = subpatterns.get(i) { - if p.field.index() == i { - write!(f, "{}", p.pattern)?; - continue; - } - } - - // Otherwise, we have to go looking for it. - if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { - write!(f, "{}", p.pattern)?; - } else { - write!(f, "_")?; - } - } - write!(f, ")")?; - } - - Ok(()) - } - PatKind::Deref { ref subpattern } => { - match self.ty.kind() { - ty::Adt(def, _) if def.is_box() => write!(f, "box ")?, - ty::Ref(_, _, mutbl) => { - write!(f, "&{}", mutbl.prefix_str())?; - } - _ => bug!("{} is a bad Deref pattern type", self.ty), - } - write!(f, "{subpattern}") + PatKind::Box { ref subpattern } => write!(f, "box {subpattern}"), + PatKind::StructLike { ref enum_info, ref subpatterns } => { + ty::tls::with(|tcx| write_struct_like(f, tcx, self.ty, enum_info, subpatterns)) } + PatKind::Deref { ref subpattern } => write_ref_like(f, self.ty, subpattern), PatKind::Constant { value } => write!(f, "{value}"), PatKind::Range(ref range) => write!(f, "{range}"), - PatKind::Slice { ref prefix, ref slice, ref suffix } => { - write!(f, "[")?; - for p in prefix.iter() { - write!(f, "{}{}", start_or_comma(), p)?; - } - if let Some(ref slice) = *slice { - write!(f, "{}", start_or_comma())?; - match slice.kind { - PatKind::Wild => {} - _ => write!(f, "{slice}")?, - } - write!(f, "..")?; - } - for p in suffix.iter() { - write!(f, "{}{}", start_or_comma(), p)?; - } - write!(f, "]") + PatKind::Slice { ref prefix, has_dot_dot, ref suffix } => { + write_slice_like(f, prefix, has_dot_dot, suffix) } } } } + +/// Returns a closure that will return `""` when called the first time, +/// and then return `", "` when called any subsequent times. +/// Useful for printing comma-separated lists. +fn start_or_comma() -> impl FnMut() -> &'static str { + let mut first = true; + move || { + if first { + first = false; + "" + } else { + ", " + } + } +} + +#[derive(Clone, Debug)] +pub(crate) enum EnumInfo<'tcx> { + Enum { adt_def: AdtDef<'tcx>, variant_index: VariantIdx }, + NotEnum, +} + +fn write_struct_like<'tcx>( + f: &mut impl fmt::Write, + tcx: TyCtxt<'_>, + ty: Ty<'tcx>, + enum_info: &EnumInfo<'tcx>, + subpatterns: &[FieldPat<'tcx>], +) -> fmt::Result { + let variant_and_name = match *enum_info { + EnumInfo::Enum { adt_def, variant_index } => { + let variant = adt_def.variant(variant_index); + let adt_did = adt_def.did(); + let name = if tcx.is_diagnostic_item(sym::Option, adt_did) + || tcx.is_diagnostic_item(sym::Result, adt_did) + { + variant.name.to_string() + } else { + format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name) + }; + Some((variant, name)) + } + EnumInfo::NotEnum => ty.ty_adt_def().and_then(|adt_def| { + Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did()))) + }), + }; + + let mut start_or_comma = start_or_comma(); + + if let Some((variant, name)) = &variant_and_name { + write!(f, "{name}")?; + + // Only for Adt we can have `S {...}`, + // which we handle separately here. + if variant.ctor.is_none() { + write!(f, " {{ ")?; + + let mut printed = 0; + for p in subpatterns { + if let PatKind::Wild = p.pattern.kind { + continue; + } + let name = variant.fields[p.field].name; + write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; + printed += 1; + } + + let is_union = ty.ty_adt_def().is_some_and(|adt| adt.is_union()); + if printed < variant.fields.len() && (!is_union || printed == 0) { + write!(f, "{}..", start_or_comma())?; + } + + return write!(f, " }}"); + } + } + + let num_fields = variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len()); + if num_fields != 0 || variant_and_name.is_none() { + write!(f, "(")?; + for i in 0..num_fields { + write!(f, "{}", start_or_comma())?; + + // Common case: the field is where we expect it. + if let Some(p) = subpatterns.get(i) { + if p.field.index() == i { + write!(f, "{}", p.pattern)?; + continue; + } + } + + // Otherwise, we have to go looking for it. + if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { + write!(f, "{}", p.pattern)?; + } else { + write!(f, "_")?; + } + } + write!(f, ")")?; + } + + Ok(()) +} + +fn write_ref_like<'tcx>( + f: &mut impl fmt::Write, + ty: Ty<'tcx>, + subpattern: &Pat<'tcx>, +) -> fmt::Result { + match ty.kind() { + ty::Ref(_, _, mutbl) => { + write!(f, "&{}", mutbl.prefix_str())?; + } + _ => bug!("{ty} is a bad ref pattern type"), + } + write!(f, "{subpattern}") +} + +fn write_slice_like<'tcx>( + f: &mut impl fmt::Write, + prefix: &[Box>], + has_dot_dot: bool, + suffix: &[Box>], +) -> fmt::Result { + let mut start_or_comma = start_or_comma(); + write!(f, "[")?; + for p in prefix.iter() { + write!(f, "{}{}", start_or_comma(), p)?; + } + if has_dot_dot { + write!(f, "{}..", start_or_comma())?; + } + for p in suffix.iter() { + write!(f, "{}{}", start_or_comma(), p)?; + } + write!(f, "]") +} diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 9710c9e1303d..6535afcc3986 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -543,13 +543,11 @@ //! recurse into subpatterns. That second part is done through [`PlaceValidity`], most notably //! [`PlaceValidity::specialize`]. //! -//! Having said all that, in practice we don't fully follow what's been presented in this section. -//! Let's call "toplevel exception" the case where the match scrutinee itself has type `!` or -//! `EmptyEnum`. First, on stable rust, we require `_` patterns for empty types in all cases apart -//! from the toplevel exception. The `exhaustive_patterns` and `min_exaustive_patterns` allow -//! omitting patterns in the cases described above. There's a final detail: in the toplevel -//! exception or with the `exhaustive_patterns` feature, we ignore place validity when checking -//! whether a pattern is required for exhaustiveness. I (Nadrieril) hope to deprecate this behavior. +//! Having said all that, we don't fully follow what's been presented in this section. For +//! backwards-compatibility, we ignore place validity when checking whether a pattern is required +//! for exhaustiveness in two cases: when the `exhaustive_patterns` feature gate is on, or when the +//! match scrutinee itself has type `!` or `EmptyEnum`. I (Nadrieril) hope to deprecate this +//! exception. //! //! //! @@ -953,13 +951,10 @@ impl PlaceInfo { self.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors); // Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if // it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`). - let empty_arms_are_unreachable = self.validity.is_known_valid() - && (is_toplevel_exception - || cx.is_exhaustive_patterns_feature_on() - || cx.is_min_exhaustive_patterns_feature_on()); + let empty_arms_are_unreachable = self.validity.is_known_valid(); // Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the // toplevel exception and `exhaustive_patterns` cases for backwards compatibility. - let can_omit_empty_arms = empty_arms_are_unreachable + let can_omit_empty_arms = self.validity.is_known_valid() || is_toplevel_exception || cx.is_exhaustive_patterns_feature_on(); diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs index 01a56eaa78fc..ec0bcd43ad24 100644 --- a/compiler/rustc_pattern_analysis/tests/common/mod.rs +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -152,10 +152,6 @@ impl PatCx for Cx { false } - fn is_min_exhaustive_patterns_feature_on(&self) -> bool { - true - } - fn ctor_arity(&self, ctor: &Constructor, ty: &Self::Ty) -> usize { ty.sub_tys(ctor).len() } diff --git a/compiler/rustc_query_system/src/dep_graph/debug.rs b/compiler/rustc_query_system/src/dep_graph/debug.rs index 4d009d63de59..12ed57427116 100644 --- a/compiler/rustc_query_system/src/dep_graph/debug.rs +++ b/compiler/rustc_query_system/src/dep_graph/debug.rs @@ -46,15 +46,14 @@ pub struct EdgeFilter { impl EdgeFilter { pub fn new(test: &str) -> Result> { - let parts: Vec<_> = test.split("->").collect(); - if parts.len() != 2 { - Err(format!("expected a filter like `a&b -> c&d`, not `{test}`").into()) - } else { + if let [source, target] = *test.split("->").collect::>() { Ok(EdgeFilter { - source: DepNodeFilter::new(parts[0]), - target: DepNodeFilter::new(parts[1]), + source: DepNodeFilter::new(source), + target: DepNodeFilter::new(target), index_to_node: Lock::new(FxHashMap::default()), }) + } else { + Err(format!("expected a filter like `a&b -> c&d`, not `{test}`").into()) } } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2fa3692bb289..d57dabdd78d9 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -283,6 +283,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { parent_scope, finalize.then(|| Finalize::new(id, path.span)), None, + None, ) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { let res = module.res().expect("visibility resolved to unnamed block"); @@ -372,8 +373,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { has_attributes: !item.attrs.is_empty(), root_span, root_id, - vis: Cell::new(Some(vis)), - used: Default::default(), + vis, }); self.r.indeterminate_imports.push(import); @@ -888,9 +888,11 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { root_span: item.span, span: item.span, module_path: Vec::new(), - vis: Cell::new(Some(vis)), - used: Cell::new(used.then_some(Used::Other)), + vis, }); + if used { + self.r.import_use_map.insert(import, Used::Other); + } self.r.potentially_unused_imports.push(import); let imported_binding = self.r.import(binding, import); if parent == self.r.graph_root { @@ -1089,8 +1091,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { root_span: span, span, module_path: Vec::new(), - vis: Cell::new(Some(ty::Visibility::Restricted(CRATE_DEF_ID))), - used: Default::default(), + vis: ty::Visibility::Restricted(CRATE_DEF_ID), }) }; @@ -1125,6 +1126,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { ident, MacroNS, &self.parent_scope, + None, ); if let Ok(binding) = result { let import = macro_use_import(self, ident.span, false); @@ -1253,9 +1255,9 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { root_span: span, span, module_path: Vec::new(), - vis: Cell::new(Some(vis)), - used: Cell::new(Some(Used::Other)), + vis, }); + self.r.import_use_map.insert(import, Used::Other); let import_binding = self.r.import(binding, import); self.r.define(self.r.graph_root, ident, MacroNS, import_binding); } else { diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index a81999715610..1cee876b80fe 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -381,9 +381,9 @@ impl Resolver<'_, '_> { for import in self.potentially_unused_imports.iter() { match import.kind { - _ if import.used.get().is_some() - || import.expect_vis().is_public() - || import.span.is_dummy() => + _ if import.vis.is_public() + || import.span.is_dummy() + || self.import_use_map.contains_key(import) => { if let ImportKind::MacroUse { .. } = import.kind { if !import.span.is_dummy() { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 8080bb60e415..942026ef0122 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1052,6 +1052,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, false, false, + None, ) { suggestions.extend( ext.helper_attrs @@ -1506,6 +1507,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, None, + None, ) { let desc = match binding.res() { Res::Def(DefKind::Macro(MacroKind::Bang), _) => { @@ -1983,6 +1985,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, ribs: Option<&PerNS>>>, ignore_binding: Option>, + ignore_import: Option>, module: Option>, failed_segment_idx: usize, ident: Ident, @@ -2066,11 +2069,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, None, ignore_binding, + ignore_import, ) .ok() } else if let Some(ribs) = ribs && let Some(TypeNS | ValueNS) = opt_ns { + assert!(ignore_import.is_none()); match self.resolve_ident_in_lexical_scope( ident, ns_to_try, @@ -2091,6 +2096,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, ignore_binding, + ignore_import, ) .ok() }; @@ -2132,6 +2138,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) { // Check whether the name refers to an item in the value namespace. let binding = if let Some(ribs) = ribs { + assert!(ignore_import.is_none()); self.resolve_ident_in_lexical_scope( ident, ValueNS, @@ -2206,6 +2213,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, ignore_binding, + ignore_import, ) { let descr = binding.res().descr(); (format!("{descr} `{ident}` is not a crate or module"), suggestion) @@ -2259,7 +2267,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ) -> Option<(Vec, Option)> { // Replace first ident with `self` and check if that is valid. path[0].ident.name = kw::SelfLower; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } @@ -2278,7 +2286,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ) -> Option<(Vec, Option)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Crate; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some(( @@ -2309,7 +2317,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ) -> Option<(Vec, Option)> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Super; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } @@ -2343,7 +2351,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { for name in extern_crate_names.into_iter() { // Replace first ident with a crate name and check if that is valid. path[0].ident.name = name; - let result = self.maybe_resolve_path(&path, None, parent_scope); + let result = self.maybe_resolve_path(&path, None, parent_scope, None); debug!( "make_external_crate_suggestion: name={:?} path={:?} result={:?}", name, path, result @@ -2509,12 +2517,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } /// Finds a cfg-ed out item inside `module` with the matching name. - pub(crate) fn find_cfg_stripped( - &mut self, - err: &mut Diag<'_>, - segment: &Symbol, - module: DefId, - ) { + pub(crate) fn find_cfg_stripped(&self, err: &mut Diag<'_>, segment: &Symbol, module: DefId) { let local_items; let symbols = if module.is_local() { local_items = self diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 947ba569ab0e..149c639efab8 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -14,6 +14,7 @@ use Determinacy::*; use Namespace::*; use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; +use crate::imports::Import; use crate::late::{ConstantHasGenerics, NoConstantGenericsReason, PathSource, Rib, RibKind}; use crate::macros::{sub_namespace_match, MacroRulesScope}; use crate::{ @@ -351,6 +352,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), ignore_binding, + None, ); if let Ok(binding) = item { // The ident resolves to an item. @@ -364,6 +366,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + None, ) .ok() .map(LexicalScopeBinding::Item) @@ -383,6 +386,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize: Option, force: bool, ignore_binding: Option>, + ignore_import: Option>, ) -> Result, Determinacy> { bitflags::bitflags! { #[derive(Clone, Copy)] @@ -455,6 +459,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, true, force, + ignore_import, ) { Ok((Some(ext), _)) => { if ext.helper_attrs.contains(&ident.name) { @@ -496,6 +501,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, finalize, ignore_binding, + ignore_import, ); match binding { Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)), @@ -518,6 +524,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { !matches!(scope_set, ScopeSet::Late(..)), finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), ignore_binding, + ignore_import, ); match binding { Ok(binding) => { @@ -585,6 +592,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, None, ignore_binding, + ignore_import, ) { if matches!(use_prelude, UsePrelude::Yes) || this.is_builtin_macro(binding.res()) @@ -738,8 +746,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ident: Ident, ns: Namespace, parent_scope: &ParentScope<'a>, + ignore_import: Option>, ) -> Result, Determinacy> { - self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None) + self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None, ignore_import) .map_err(|(determinacy, _)| determinacy) } @@ -752,9 +761,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option, ignore_binding: Option>, + ignore_import: Option>, ) -> Result, Determinacy> { - self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize, ignore_binding) - .map_err(|(determinacy, _)| determinacy) + self.resolve_ident_in_module_ext( + module, + ident, + ns, + parent_scope, + finalize, + ignore_binding, + ignore_import, + ) + .map_err(|(determinacy, _)| determinacy) } #[instrument(level = "debug", skip(self))] @@ -766,6 +784,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option, ignore_binding: Option>, + ignore_import: Option>, ) -> Result, (Determinacy, Weak)> { let tmp_parent_scope; let mut adjusted_parent_scope = parent_scope; @@ -792,6 +811,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { false, finalize, ignore_binding, + ignore_import, ) } @@ -804,6 +824,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option, ignore_binding: Option>, + ignore_import: Option>, ) -> Result, Determinacy> { self.resolve_ident_in_module_unadjusted_ext( module, @@ -813,6 +834,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { false, finalize, ignore_binding, + ignore_import, ) .map_err(|(determinacy, _)| determinacy) } @@ -831,6 +853,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // This binding should be ignored during in-module resolution, so that we don't get // "self-confirming" import resolutions during import validation and checking. ignore_binding: Option>, + ignore_import: Option>, ) -> Result, (Determinacy, Weak)> { let module = match module { ModuleOrUniformRoot::Module(module) => module, @@ -843,6 +866,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + ignore_import, ); return binding.map_err(|determinacy| (determinacy, Weak::No)); } @@ -879,6 +903,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + ignore_import, ); return binding.map_err(|determinacy| (determinacy, Weak::No)); } @@ -962,25 +987,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Check if one of single imports can still define the name, // if it can then our result is not determined and can be invalidated. for single_import in &resolution.single_imports { - let Some(import_vis) = single_import.vis.get() else { - // This branch handles a cycle in single imports, which occurs - // when we've previously **steal** the `vis` value during an import - // process. + if ignore_import == Some(*single_import) { + // This branch handles a cycle in single imports. // // For example: // ``` // use a::b; // use b as a; // ``` - // 1. Steal the `vis` in `use a::b` and attempt to locate `a` in the + // 1. Record `use a::b` as the `ignore_import` and attempt to locate `a` in the // current module. // 2. Encounter the import `use b as a`, which is a `single_import` for `a`, // and try to find `b` in the current module. // 3. Re-encounter the `use a::b` import since it's a `single_import` of `b`. // This leads to entering this branch. continue; - }; - if !self.is_accessible_from(import_vis, parent_scope.module) { + } + if !self.is_accessible_from(single_import.vis, parent_scope.module) { continue; } if let Some(ignored) = ignore_binding @@ -1022,6 +1045,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &single_import.parent_scope, None, ignore_binding, + ignore_import, ) { Err(Determined) => continue, Ok(binding) @@ -1070,10 +1094,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Check if one of glob imports can still define the name, // if it can then our "no resolution" result is not determined and can be invalidated. for glob_import in module.globs.borrow().iter() { - let Some(import_vis) = glob_import.vis.get() else { + if ignore_import == Some(*glob_import) { continue; - }; - if !self.is_accessible_from(import_vis, parent_scope.module) { + } + if !self.is_accessible_from(glob_import.vis, parent_scope.module) { continue; } let module = match glob_import.imported_module.get() { @@ -1100,6 +1124,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { adjusted_parent_scope, None, ignore_binding, + ignore_import, ); match result { @@ -1412,8 +1437,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { path: &[Segment], opt_ns: Option, // `None` indicates a module path in import parent_scope: &ParentScope<'a>, + ignore_import: Option>, ) -> PathResult<'a> { - self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None) + self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None, ignore_import) } #[instrument(level = "debug", skip(self))] @@ -1424,8 +1450,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, finalize: Option, ignore_binding: Option>, + ignore_import: Option>, ) -> PathResult<'a> { - self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding) + self.resolve_path_with_ribs( + path, + opt_ns, + parent_scope, + finalize, + None, + ignore_binding, + ignore_import, + ) } pub(crate) fn resolve_path_with_ribs( @@ -1436,6 +1471,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize: Option, ribs: Option<&PerNS>>>, ignore_binding: Option>, + ignore_import: Option>, ) -> PathResult<'a> { let mut module = None; let mut allow_super = true; @@ -1538,10 +1574,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, finalize, ignore_binding, + ignore_import, ) } else if let Some(ribs) = ribs && let Some(TypeNS | ValueNS) = opt_ns { + assert!(ignore_import.is_none()); match self.resolve_ident_in_lexical_scope( ident, ns, @@ -1570,6 +1608,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { finalize, finalize.is_some(), ignore_binding, + ignore_import, ) }; @@ -1644,6 +1683,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope, ribs, ignore_binding, + ignore_import, module, segment_idx, ident, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c7af21027b8d..42171edf7574 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -175,8 +175,7 @@ pub(crate) struct ImportData<'a> { pub module_path: Vec, /// The resolution of `module_path`. pub imported_module: Cell>>, - pub vis: Cell>, - pub used: Cell>, + pub vis: ty::Visibility, } /// All imports are unique and allocated on a same arena, @@ -195,10 +194,6 @@ impl<'a> ImportData<'a> { } } - pub(crate) fn expect_vis(&self) -> ty::Visibility { - self.vis.get().expect("encountered cleared import visibility") - } - pub(crate) fn id(&self) -> Option { match self.kind { ImportKind::Single { id, .. } @@ -267,7 +262,7 @@ fn pub_use_of_private_extern_crate_hack( match (&import.kind, &binding.kind) { (ImportKind::Single { .. }, NameBindingKind::Import { import: binding_import, .. }) if let ImportKind::ExternCrate { id, .. } = binding_import.kind - && import.expect_vis().is_public() => + && import.vis.is_public() => { Some(id) } @@ -279,7 +274,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// Given a binding and an import that resolves to it, /// return the corresponding binding defined by the import. pub(crate) fn import(&self, binding: NameBinding<'a>, import: Import<'a>) -> NameBinding<'a> { - let import_vis = import.expect_vis().to_def_id(); + let import_vis = import.vis.to_def_id(); let vis = if binding.vis.is_at_least(import_vis, self.tcx) || pub_use_of_private_extern_crate_hack(import, binding).is_some() { @@ -488,7 +483,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }); self.record_use(target, dummy_binding, Used::Other); } else if import.imported_module.get().is_none() { - import.used.set(Some(Used::Other)); + self.import_use_map.insert(import, Used::Other); if let Some(id) = import.id() { self.used_imports.insert(id); } @@ -773,11 +768,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let module = if let Some(module) = import.imported_module.get() { module } else { - // For better failure detection, pretend that the import will - // not define any names while resolving its module path. - let orig_vis = import.vis.take(); - let path_res = self.maybe_resolve_path(&import.module_path, None, &import.parent_scope); - import.vis.set(orig_vis); + let path_res = self.maybe_resolve_path( + &import.module_path, + None, + &import.parent_scope, + Some(import), + ); match path_res { PathResult::Module(module) => module, @@ -807,16 +803,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.per_ns(|this, ns| { if !type_ns_only || ns == TypeNS { if let Err(Undetermined) = source_bindings[ns].get() { - // For better failure detection, pretend that the import will - // not define any names while resolving its module path. - let orig_vis = import.vis.take(); let binding = this.maybe_resolve_ident_in_module( module, source, ns, &import.parent_scope, + Some(import), ); - import.vis.set(orig_vis); source_bindings[ns].set(binding); } else { return; @@ -855,7 +848,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// Optionally returns an unresolved import error. This error is buffered and used to /// consolidate multiple unresolved import errors into a single diagnostic. fn finalize_import(&mut self, import: Import<'a>) -> Option { - let orig_vis = import.vis.take(); let ignore_binding = match &import.kind { ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(), _ => None, @@ -874,11 +866,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import.parent_scope, Some(finalize), ignore_binding, + Some(import), ); let no_ambiguity = ambiguity_errors_len(&self.ambiguity_errors) == prev_ambiguity_errors_len; - import.vis.set(orig_vis); + let module = match path_res { PathResult::Module(module) => { // Consistency checks, analogous to `finalize_macro_resolutions`. @@ -1013,8 +1006,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if !is_prelude && let Some(max_vis) = max_vis.get() - && let import_vis = import.expect_vis() - && !max_vis.is_at_least(import_vis, self.tcx) + && !max_vis.is_at_least(import.vis, self.tcx) { let def_id = self.local_def_id(id); self.lint_buffer.buffer_lint( @@ -1023,7 +1015,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { import.span, BuiltinLintDiag::RedundantImportVisibility { max_vis: max_vis.to_string(def_id, self.tcx), - import_vis: import_vis.to_string(def_id, self.tcx), + import_vis: import.vis.to_string(def_id, self.tcx), span: import.span, }, ); @@ -1038,9 +1030,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // importing it if available. let mut path = import.module_path.clone(); path.push(Segment::from_ident(ident)); - if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(&path, None, &import.parent_scope, Some(finalize), ignore_binding) - { + if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = self.resolve_path( + &path, + None, + &import.parent_scope, + Some(finalize), + ignore_binding, + None, + ) { let res = module.res().map(|r| (r, ident)); for error in &mut self.privacy_errors[privacy_errors_len..] { error.outermost_res = res; @@ -1051,7 +1048,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut all_ns_err = true; self.per_ns(|this, ns| { if !type_ns_only || ns == TypeNS { - let orig_vis = import.vis.take(); let binding = this.resolve_ident_in_module( module, ident, @@ -1059,8 +1055,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import.parent_scope, Some(Finalize { report_private: false, ..finalize }), target_bindings[ns].get(), + Some(import), ); - import.vis.set(orig_vis); match binding { Ok(binding) => { @@ -1123,6 +1119,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &import.parent_scope, Some(finalize), None, + None, ); if binding.is_ok() { all_ns_failed = false; @@ -1233,7 +1230,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut crate_private_reexport = false; self.per_ns(|this, ns| { if let Ok(binding) = source_bindings[ns].get() { - if !binding.vis.is_at_least(import.expect_vis(), this.tcx) { + if !binding.vis.is_at_least(import.vis, this.tcx) { reexport_error = Some((ns, binding)); if let ty::Visibility::Restricted(binding_def_id) = binding.vis { if binding_def_id.is_top_level_module() { @@ -1349,7 +1346,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // module defined by a block). // Skip if the import is public or was used through non scope-based resolution, // e.g. through a module-relative path. - if import.used.get() == Some(Used::Other) + if self.import_use_map.get(&import) == Some(&Used::Other) || self.effective_visibilities.is_exported(self.local_def_id(id)) { return false; @@ -1370,6 +1367,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, target_bindings[ns].get(), + None, ) { Ok(other_binding) => { is_redundant = binding.res() == other_binding.res() diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f844930ad265..4a70fc0f3084 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -445,8 +445,8 @@ impl<'a> PathSource<'a> { Some(ExprKind::Call(call_expr, _)) => match &call_expr.kind { // the case of `::some_crate()` ExprKind::Path(_, path) - if path.segments.len() == 2 - && path.segments[0].ident.name == kw::PathRoot => + if let [segment, _] = path.segments.as_slice() + && segment.ident.name == kw::PathRoot => { "external crate" } @@ -1388,6 +1388,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { finalize, Some(&self.ribs), None, + None, ) } @@ -2395,15 +2396,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } fn future_proof_import(&mut self, use_tree: &UseTree) { - let segments = &use_tree.prefix.segments; - if !segments.is_empty() { - let ident = segments[0].ident; + if let [segment, rest @ ..] = use_tree.prefix.segments.as_slice() { + let ident = segment.ident; if ident.is_path_segment_keyword() || ident.span.is_rust_2015() { return; } let nss = match use_tree.kind { - UseTreeKind::Simple(..) if segments.len() == 1 => &[TypeNS, ValueNS][..], + UseTreeKind::Simple(..) if rest.is_empty() => &[TypeNS, ValueNS][..], _ => &[TypeNS], }; let report_error = |this: &Self, ns| { @@ -2667,119 +2667,128 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let mut function_type_rib = Rib::new(kind); let mut function_value_rib = Rib::new(kind); let mut function_lifetime_rib = LifetimeRib::new(lifetime_kind); - let mut seen_bindings = FxHashMap::default(); - // Store all seen lifetimes names from outer scopes. - let mut seen_lifetimes = FxHashSet::default(); - // We also can't shadow bindings from associated parent items. - for ns in [ValueNS, TypeNS] { - for parent_rib in self.ribs[ns].iter().rev() { - seen_bindings.extend(parent_rib.bindings.keys().map(|ident| (*ident, ident.span))); + // Only check for shadowed bindings if we're declaring new params. + if !params.is_empty() { + let mut seen_bindings = FxHashMap::default(); + // Store all seen lifetimes names from outer scopes. + let mut seen_lifetimes = FxHashSet::default(); - // Break at mod level, to account for nested items which are - // allowed to shadow generic param names. - if matches!(parent_rib.kind, RibKind::Module(..)) { + // We also can't shadow bindings from associated parent items. + for ns in [ValueNS, TypeNS] { + for parent_rib in self.ribs[ns].iter().rev() { + seen_bindings + .extend(parent_rib.bindings.keys().map(|ident| (*ident, ident.span))); + + // Break at mod level, to account for nested items which are + // allowed to shadow generic param names. + if matches!(parent_rib.kind, RibKind::Module(..)) { + break; + } + } + } + + // Forbid shadowing lifetime bindings + for rib in self.lifetime_ribs.iter().rev() { + seen_lifetimes.extend(rib.bindings.iter().map(|(ident, _)| *ident)); + if let LifetimeRibKind::Item = rib.kind { break; } } - } - // Forbid shadowing lifetime bindings - for rib in self.lifetime_ribs.iter().rev() { - seen_lifetimes.extend(rib.bindings.iter().map(|(ident, _)| *ident)); - if let LifetimeRibKind::Item = rib.kind { - break; - } - } + for param in params { + let ident = param.ident.normalize_to_macros_2_0(); + debug!("with_generic_param_rib: {}", param.id); - for param in params { - let ident = param.ident.normalize_to_macros_2_0(); - debug!("with_generic_param_rib: {}", param.id); - - if let GenericParamKind::Lifetime = param.kind - && let Some(&original) = seen_lifetimes.get(&ident) - { - diagnostics::signal_lifetime_shadowing(self.r.tcx.sess, original, param.ident); - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; - } - - match seen_bindings.entry(ident) { - Entry::Occupied(entry) => { - let span = *entry.get(); - let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span); - self.report_error(param.ident.span, err); - let rib = match param.kind { - GenericParamKind::Lifetime => { - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; - } - GenericParamKind::Type { .. } => &mut function_type_rib, - GenericParamKind::Const { .. } => &mut function_value_rib, - }; - - // Taint the resolution in case of errors to prevent follow up errors in typeck - self.r.record_partial_res(param.id, PartialRes::new(Res::Err)); - rib.bindings.insert(ident, Res::Err); + if let GenericParamKind::Lifetime = param.kind + && let Some(&original) = seen_lifetimes.get(&ident) + { + diagnostics::signal_lifetime_shadowing(self.r.tcx.sess, original, param.ident); + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); continue; } - Entry::Vacant(entry) => { - entry.insert(param.ident.span); - } - } - if param.ident.name == kw::UnderscoreLifetime { - self.r - .dcx() - .emit_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }); - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; - } + match seen_bindings.entry(ident) { + Entry::Occupied(entry) => { + let span = *entry.get(); + let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span); + self.report_error(param.ident.span, err); + let rib = match param.kind { + GenericParamKind::Lifetime => { + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); + continue; + } + GenericParamKind::Type { .. } => &mut function_type_rib, + GenericParamKind::Const { .. } => &mut function_value_rib, + }; - if param.ident.name == kw::StaticLifetime { - self.r.dcx().emit_err(errors::StaticLifetimeIsReserved { - span: param.ident.span, - lifetime: param.ident, - }); - // Record lifetime res, so lowering knows there is something fishy. - self.record_lifetime_param(param.id, LifetimeRes::Error); - continue; - } - - let def_id = self.r.local_def_id(param.id); - - // Plain insert (no renaming). - let (rib, def_kind) = match param.kind { - GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam), - GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam), - GenericParamKind::Lifetime => { - let res = LifetimeRes::Param { param: def_id, binder }; - self.record_lifetime_param(param.id, res); - function_lifetime_rib.bindings.insert(ident, (param.id, res)); - continue; - } - }; - - let res = match kind { - RibKind::Item(..) | RibKind::AssocItem => Res::Def(def_kind, def_id.to_def_id()), - RibKind::Normal => { - // FIXME(non_lifetime_binders): Stop special-casing - // const params to error out here. - if self.r.tcx.features().non_lifetime_binders - && matches!(param.kind, GenericParamKind::Type { .. }) - { - Res::Def(def_kind, def_id.to_def_id()) - } else { - Res::Err + // Taint the resolution in case of errors to prevent follow up errors in typeck + self.r.record_partial_res(param.id, PartialRes::new(Res::Err)); + rib.bindings.insert(ident, Res::Err); + continue; + } + Entry::Vacant(entry) => { + entry.insert(param.ident.span); } } - _ => span_bug!(param.ident.span, "Unexpected rib kind {:?}", kind), - }; - self.r.record_partial_res(param.id, PartialRes::new(res)); - rib.bindings.insert(ident, res); + + if param.ident.name == kw::UnderscoreLifetime { + self.r + .dcx() + .emit_err(errors::UnderscoreLifetimeIsReserved { span: param.ident.span }); + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); + continue; + } + + if param.ident.name == kw::StaticLifetime { + self.r.dcx().emit_err(errors::StaticLifetimeIsReserved { + span: param.ident.span, + lifetime: param.ident, + }); + // Record lifetime res, so lowering knows there is something fishy. + self.record_lifetime_param(param.id, LifetimeRes::Error); + continue; + } + + let def_id = self.r.local_def_id(param.id); + + // Plain insert (no renaming). + let (rib, def_kind) = match param.kind { + GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam), + GenericParamKind::Const { .. } => { + (&mut function_value_rib, DefKind::ConstParam) + } + GenericParamKind::Lifetime => { + let res = LifetimeRes::Param { param: def_id, binder }; + self.record_lifetime_param(param.id, res); + function_lifetime_rib.bindings.insert(ident, (param.id, res)); + continue; + } + }; + + let res = match kind { + RibKind::Item(..) | RibKind::AssocItem => { + Res::Def(def_kind, def_id.to_def_id()) + } + RibKind::Normal => { + // FIXME(non_lifetime_binders): Stop special-casing + // const params to error out here. + if self.r.tcx.features().non_lifetime_binders + && matches!(param.kind, GenericParamKind::Type { .. }) + { + Res::Def(def_kind, def_id.to_def_id()) + } else { + Res::Err + } + } + _ => span_bug!(param.ident.span, "Unexpected rib kind {:?}", kind), + }; + self.r.record_partial_res(param.id, PartialRes::new(res)); + rib.bindings.insert(ident, res); + } } self.lifetime_ribs.push(function_lifetime_rib); @@ -3999,16 +4008,15 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { if this.should_report_errs() { if candidates.is_empty() { - if path.len() == 2 && prefix_path.len() == 1 { + if path.len() == 2 + && let [segment] = prefix_path + { // Delay to check whether methond name is an associated function or not // ``` // let foo = Foo {}; // foo::bar(); // possibly suggest to foo.bar(); //``` - err.stash( - prefix_path[0].ident.span, - rustc_errors::StashKey::CallAssocMethod, - ); + err.stash(segment.ident.span, rustc_errors::StashKey::CallAssocMethod); } else { // When there is no suggested imports, we can just emit the error // and suggestions immediately. Note that we bypass the usually error @@ -4186,7 +4194,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident); let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None }; if let Ok((_, res)) = - self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false) + self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false, None) { return Ok(Some(PartialRes::new(res))); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f126563fe58f..f778b0ee3acc 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -650,14 +650,14 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let typo_sugg = self .lookup_typo_candidate(path, following_seg, source.namespace(), is_expected) .to_opt_suggestion(); - if path.len() == 1 + if let [segment] = path && !matches!(source, PathSource::Delegation) && self.self_type_is_available() { if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call()) { - let self_is_available = self.self_value_is_available(path[0].ident.span); + let self_is_available = self.self_value_is_available(segment.ident.span); // Account for `Foo { field }` when suggesting `self.field` so we result on // `Foo { field: self.field }`. let pre = match source { @@ -665,7 +665,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if expr .fields .iter() - .any(|f| f.ident == path[0].ident && f.is_shorthand) => + .any(|f| f.ident == segment.ident && f.is_shorthand) => { format!("{path_str}: ") } @@ -1258,8 +1258,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ) }) .collect(); - if targets.len() == 1 { - let target = targets[0]; + if let [target] = targets.as_slice() { return Some(TypoSuggestion::single_item_from_ident(target.0.ident, target.1)); } } @@ -2058,6 +2057,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ident, ns, &self.parent_scope, + None, ) { let res = binding.res(); if filter_fn(res) { @@ -2104,8 +2104,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { filter_fn: &impl Fn(Res) -> bool, ) -> TypoCandidate { let mut names = Vec::new(); - if path.len() == 1 { - let mut ctxt = path.last().unwrap().ident.span.ctxt(); + if let [segment] = path { + let mut ctxt = segment.ident.span.ctxt(); // Search in lexical scope. // Walk backwards up the ribs in scope and collect candidates. diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 79d3bb685b38..02fdc1ae6685 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1016,6 +1016,8 @@ pub struct Resolver<'a, 'tcx> { partial_res_map: NodeMap, /// Resolutions for import nodes, which have multiple resolutions in different namespaces. import_res_map: NodeMap>>, + /// An import will be inserted into this map if it has been used. + import_use_map: FxHashMap, Used>, /// Resolutions for labels (node IDs of their corresponding blocks or loops). label_res_map: NodeMap, /// Resolutions for lifetimes. @@ -1129,9 +1131,6 @@ pub struct Resolver<'a, 'tcx> { /// Also includes of list of each fields visibility struct_constructors: LocalDefIdMap<(Res, ty::Visibility, Vec>)>, - /// Features declared for this crate. - declared_features: FxHashSet, - lint_buffer: LintBuffer, next_node_id: NodeId, @@ -1402,7 +1401,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let registered_tools = tcx.registered_tools(()); - let features = tcx.features(); let pub_vis = ty::Visibility::::Public; let edition = tcx.sess.edition(); @@ -1426,6 +1424,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { pat_span_map: Default::default(), partial_res_map: Default::default(), import_res_map: Default::default(), + import_use_map: Default::default(), label_res_map: Default::default(), lifetimes_res_map: Default::default(), extra_lifetime_params_map: Default::default(), @@ -1506,7 +1505,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { multi_segment_macro_resolutions: Default::default(), builtin_attrs: Default::default(), containers_deriving_copy: Default::default(), - declared_features: features.declared_features.clone(), lint_buffer: LintBuffer::default(), next_node_id: CRATE_NODE_ID, node_id_to_def_id, @@ -1844,7 +1842,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } /// Test if AmbiguityError ambi is any identical to any one inside ambiguity_errors - fn matches_previous_ambiguity_error(&mut self, ambi: &AmbiguityError<'_>) -> bool { + fn matches_previous_ambiguity_error(&self, ambi: &AmbiguityError<'_>) -> bool { for ambiguity_error in &self.ambiguity_errors { // if the span location and ident as well as its span are the same if ambiguity_error.kind == ambi.kind @@ -1905,10 +1903,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } } - let old_used = import.used.get(); - let new_used = Some(used); - if new_used > old_used { - import.used.set(new_used); + let old_used = self.import_use_map.entry(import).or_insert(used); + if *old_used < used { + *old_used = used; } if let Some(id) = import.id() { self.used_imports.insert(id); @@ -2120,7 +2117,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - match self.maybe_resolve_path(&segments, Some(ns), &parent_scope) { + match self.maybe_resolve_path(&segments, Some(ns), &parent_scope, None) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()), PathResult::NonModule(path_res) => path_res.full_res(), PathResult::Module(ModuleOrUniformRoot::ExternPrelude) | PathResult::Failed { .. } => { @@ -2204,6 +2201,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ident, ValueNS, parent_scope, + None, ) else { return; }; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 64ae0d82952d..7203fbe4a0c1 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -39,6 +39,7 @@ use crate::errors::{ self, AddAsNonDerive, CannotDetermineMacroResolution, CannotFindIdentInThisScope, MacroExpectedFound, RemoveSurroundingDerive, }; +use crate::imports::Import; use crate::Namespace::*; use crate::{ BindingKey, BuiltinMacroState, DeriveData, Determinacy, Finalize, MacroData, ModuleKind, @@ -108,8 +109,8 @@ pub(crate) fn sub_namespace_match( // `format!("{}", path)`, because that tries to insert // line-breaks and is slow. fn fast_print_path(path: &ast::Path) -> Symbol { - if path.segments.len() == 1 { - path.segments[0].ident.name + if let [segment] = path.segments.as_slice() { + segment.ident.name } else { let mut path_str = String::with_capacity(64); for (i, segment) in path.segments.iter().enumerate() { @@ -399,6 +400,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { &parent_scope, true, force, + None, ) { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { @@ -551,6 +553,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { force, deleg_impl, invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)), + None, ) { Ok((Some(ext), res)) => (ext, res), Ok((None, res)) => (self.dummy_ext(kind), res), @@ -704,8 +707,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_scope: &ParentScope<'a>, trace: bool, force: bool, + ignore_import: Option>, ) -> Result<(Option>, Res), Determinacy> { - self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None, None) + self.resolve_macro_or_delegation_path( + path, + kind, + parent_scope, + trace, + force, + None, + None, + ignore_import, + ) } fn resolve_macro_or_delegation_path( @@ -717,6 +730,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { force: bool, deleg_impl: Option, invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, + ignore_import: Option>, ) -> Result<(Option>, Res), Determinacy> { let path_span = ast_path.span; let mut path = Segment::from_path(ast_path); @@ -724,16 +738,16 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Possibly apply the macro helper hack if deleg_impl.is_none() && kind == Some(MacroKind::Bang) - && path.len() == 1 - && path[0].ident.span.ctxt().outer_expn_data().local_inner_macros + && let [segment] = path.as_slice() + && segment.ident.span.ctxt().outer_expn_data().local_inner_macros { - let root = Ident::new(kw::DollarCrate, path[0].ident.span); + let root = Ident::new(kw::DollarCrate, segment.ident.span); path.insert(0, Segment::from_ident(root)); } let res = if deleg_impl.is_some() || path.len() > 1 { let ns = if deleg_impl.is_some() { TypeNS } else { MacroNS }; - let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope) { + let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope, ignore_import) { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res), PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined), PathResult::NonModule(..) @@ -768,6 +782,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, force, None, + None, ); if let Err(Determinacy::Undetermined) = binding { return Err(Determinacy::Undetermined); @@ -852,6 +867,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &parent_scope, Some(Finalize::new(ast::CRATE_NODE_ID, path_span)), None, + None, ) { PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => { check_consistency(self, &path, path_span, kind, initial_res, res) @@ -871,7 +887,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if let PathResult::Failed { span, label, module, .. } = path_res { // try to suggest if it's not a macro, maybe a function if let PathResult::NonModule(partial_res) = - self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope) + self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope, None) && partial_res.unresolved_segments() == 0 { let sm = self.tcx.sess.source_map(); @@ -921,6 +937,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)), true, None, + None, ) { Ok(binding) => { let initial_res = initial_binding.map(|initial_binding| { @@ -966,6 +983,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)), true, None, + None, ); } } @@ -983,7 +1001,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let feature = stability.feature; let is_allowed = |feature| { - self.declared_features.contains(&feature) || span.allows_unstable(feature) + self.tcx.features().declared_features.contains(&feature) + || span.allows_unstable(feature) }; let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); if !is_allowed(feature) && !allowed_by_implication { @@ -1070,6 +1089,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None, false, None, + None, ); if fallback_binding.ok().and_then(|b| b.res().opt_def_id()) != Some(def_id) { self.tcx.sess.psess.buffer_lint( @@ -1143,7 +1163,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut indeterminate = false; for ns in namespaces { - match self.maybe_resolve_path(path, Some(*ns), &parent_scope) { + match self.maybe_resolve_path(path, Some(*ns), &parent_scope, None) { PathResult::Module(ModuleOrUniformRoot::Module(_)) => return Ok(true), PathResult::NonModule(partial_res) if partial_res.unresolved_segments() == 0 => { return Ok(true); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index a60af3f2d713..95d171409d86 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -602,7 +602,7 @@ impl OutputType { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ErrorOutputType { /// Output meant for the consumption of humans. - HumanReadable(HumanReadableErrorType), + HumanReadable(HumanReadableErrorType, ColorConfig), /// Output that's consumed by other tools such as `rustfix` or the `RLS`. Json { /// Render the JSON in a human readable way (with indents and newlines). @@ -610,12 +610,13 @@ pub enum ErrorOutputType { /// The JSON output includes a `rendered` field that includes the rendered /// human output. json_rendered: HumanReadableErrorType, + color_config: ColorConfig, }, } impl Default for ErrorOutputType { fn default() -> Self { - Self::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto)) + Self::HumanReadable(HumanReadableErrorType::Default, ColorConfig::Auto) } } @@ -1304,7 +1305,10 @@ pub(crate) const fn default_lib_output() -> CrateType { } pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg { - // Combine the configuration requested by the session (command line) with + // First disallow some configuration given on the command line + cfg::disallow_cfgs(sess, &user_cfg); + + // Then combine the configuration requested by the session (command line) with // some default and generated configuration items. user_cfg.extend(cfg::default_configuration(sess)); user_cfg @@ -1628,6 +1632,7 @@ pub fn parse_color(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Col /// Possible json config files pub struct JsonConfig { pub json_rendered: HumanReadableErrorType, + pub json_color: ColorConfig, json_artifact_notifications: bool, pub json_unused_externs: JsonUnusedExterns, json_future_incompat: bool, @@ -1665,8 +1670,7 @@ impl JsonUnusedExterns { /// The first value returned is how to render JSON diagnostics, and the second /// is whether or not artifact notifications are enabled. pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> JsonConfig { - let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType = - HumanReadableErrorType::Default; + let mut json_rendered = HumanReadableErrorType::Default; let mut json_color = ColorConfig::Never; let mut json_artifact_notifications = false; let mut json_unused_externs = JsonUnusedExterns::No; @@ -1693,7 +1697,8 @@ pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Json } JsonConfig { - json_rendered: json_rendered(json_color), + json_rendered, + json_color, json_artifact_notifications, json_unused_externs, json_future_incompat, @@ -1705,6 +1710,7 @@ pub fn parse_error_format( early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches, color: ColorConfig, + json_color: ColorConfig, json_rendered: HumanReadableErrorType, ) -> ErrorOutputType { // We need the `opts_present` check because the driver will send us Matches @@ -1714,18 +1720,22 @@ pub fn parse_error_format( let error_format = if matches.opts_present(&["error-format".to_owned()]) { match matches.opt_str("error-format").as_deref() { None | Some("human") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default, color) } Some("human-annotate-rs") => { - ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(color)) + ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet, color) } - Some("json") => ErrorOutputType::Json { pretty: false, json_rendered }, - Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered }, - Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)), - + Some("json") => { + ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color } + } + Some("pretty-json") => { + ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color } + } + Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short, color), Some(arg) => { early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable( - HumanReadableErrorType::Default(color), + HumanReadableErrorType::Default, + color, )); early_dcx.early_fatal(format!( "argument for `--error-format` must be `human`, `json` or \ @@ -1734,7 +1744,7 @@ pub fn parse_error_format( } } } else { - ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)) + ErrorOutputType::HumanReadable(HumanReadableErrorType::Default, color) }; match error_format { @@ -1788,7 +1798,7 @@ fn check_error_format_stability( if let ErrorOutputType::Json { pretty: true, .. } = error_format { early_dcx.early_fatal("`--error-format=pretty-json` is unstable"); } - if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) = + if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet, _) = error_format { early_dcx.early_fatal("`--error-format=human-annotate-rs` is unstable"); @@ -2389,12 +2399,13 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let JsonConfig { json_rendered, + json_color, json_artifact_notifications, json_unused_externs, json_future_incompat, } = parse_json(early_dcx, matches); - let error_format = parse_error_format(early_dcx, matches, color, json_rendered); + let error_format = parse_error_format(early_dcx, matches, color, json_color, json_rendered); early_dcx.abort_if_error_and_set_error_format(error_format); diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 4109ebb6d340..a64b1e21e9ea 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -17,12 +17,16 @@ //! - Add the activation logic in [`default_configuration`] //! - Add the cfg to [`CheckCfg::fill_well_known`] (and related files), //! so that the compiler can know the cfg is expected +//! - Add the cfg in [`disallow_cfgs`] to disallow users from setting it via `--cfg` //! - Add the feature gating in `compiler/rustc_feature/src/builtin_attrs.rs` use std::hash::Hash; use std::iter; +use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_lint_defs::builtin::EXPLICIT_BUILTIN_CFGS_IN_FLAGS; +use rustc_lint_defs::BuiltinLintDiag; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::Align; use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, Target, TargetTriple, TARGETS}; @@ -82,6 +86,67 @@ impl<'a, T: Eq + Hash + Copy + 'a> Extend<&'a T> for ExpectedValues { } } +/// Disallow builtin cfgs from the CLI. +pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { + let disallow = |cfg: &(Symbol, Option), controlled_by| { + let cfg_name = cfg.0; + let cfg = if let Some(value) = cfg.1 { + format!(r#"{}="{}""#, cfg_name, value) + } else { + format!("{}", cfg_name) + }; + sess.psess.opt_span_buffer_lint( + EXPLICIT_BUILTIN_CFGS_IN_FLAGS, + None, + ast::CRATE_NODE_ID, + BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }, + ) + }; + + // We want to restrict setting builtin cfgs that will produce incoherent behavior + // between the cfg and the rustc cli flag that sets it. + // + // The tests are in tests/ui/cfg/disallowed-cli-cfgs.rs. + + // By-default all builtin cfgs are disallowed, only those are allowed: + // - test: as it makes sense to the have the `test` cfg active without the builtin + // test harness. See Cargo `harness = false` config. + // + // Cargo `--cfg test`: https://github.com/rust-lang/cargo/blob/bc89bffa5987d4af8f71011c7557119b39e44a65/src/cargo/core/compiler/mod.rs#L1124 + + for cfg in user_cfgs { + match cfg { + (sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"), + (sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"), + (sym::ub_checks, None) => disallow(cfg, "-Z ub-checks"), + (sym::sanitize, None | Some(_)) => disallow(cfg, "-Z sanitizer"), + ( + sym::sanitizer_cfi_generalize_pointers | sym::sanitizer_cfi_normalize_integers, + None | Some(_), + ) => disallow(cfg, "-Z sanitizer=cfi"), + (sym::proc_macro, None) => disallow(cfg, "--crate-type proc-macro"), + (sym::panic, Some(sym::abort | sym::unwind)) => disallow(cfg, "-C panic"), + (sym::target_feature, Some(_)) => disallow(cfg, "-C target-feature"), + (sym::unix, None) + | (sym::windows, None) + | (sym::relocation_model, Some(_)) + | (sym::target_abi, None | Some(_)) + | (sym::target_arch, Some(_)) + | (sym::target_endian, Some(_)) + | (sym::target_env, None | Some(_)) + | (sym::target_family, Some(_)) + | (sym::target_os, Some(_)) + | (sym::target_pointer_width, Some(_)) + | (sym::target_vendor, None | Some(_)) + | (sym::target_has_atomic, Some(_)) + | (sym::target_has_atomic_equal_alignment, Some(_)) + | (sym::target_has_atomic_load_store, Some(_)) + | (sym::target_thread_local, None) => disallow(cfg, "--target"), + _ => {} + } + } +} + /// Generate the default configs for a given session pub(crate) fn default_configuration(sess: &Session) -> Cfg { let mut ret = Cfg::default(); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index bf54aae1cfeb..df72e2430fd5 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -912,16 +912,9 @@ mod parse { match v { None => false, Some(s) => { - let parts = s.split('=').collect::>(); - if parts.len() != 2 { - return false; - } - let crate_name = parts[0].to_string(); - let fuel = parts[1].parse::(); - if fuel.is_err() { - return false; - } - *slot = Some((crate_name, fuel.unwrap())); + let [crate_name, fuel] = *s.split('=').collect::>() else { return false }; + let Ok(fuel) = fuel.parse::() else { return false }; + *slot = Some((crate_name.to_string(), fuel)); true } } @@ -1827,6 +1820,8 @@ options! { the same values as the target option of the same name"), meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics (default: no)"), + metrics_dir: Option = (None, parse_opt_pathbuf, [UNTRACKED], + "stores metrics about the errors being emitted by rustc to disk"), mir_emit_retag: bool = (false, parse_bool, [TRACKED], "emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ (default: no)"), diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index ebd5021dae1b..d6c58e9d1be1 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -306,10 +306,20 @@ impl ParseSess { span: impl Into, node_id: NodeId, diagnostic: BuiltinLintDiag, + ) { + self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic) + } + + pub fn opt_span_buffer_lint( + &self, + lint: &'static Lint, + span: Option, + node_id: NodeId, + diagnostic: BuiltinLintDiag, ) { self.buffered_lints.with_lock(|buffered_lints| { buffered_lints.push(BufferedEarlyLint { - span: span.into(), + span, node_id, lint_id: LintId::of(lint), diagnostic, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index be67baf57f6d..672dddf871e0 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -950,10 +950,10 @@ fn default_emitter( t => t, }; match sopts.error_format { - config::ErrorOutputType::HumanReadable(kind) => { - let (short, color_config) = kind.unzip(); + config::ErrorOutputType::HumanReadable(kind, color_config) => { + let short = kind.short(); - if let HumanReadableErrorType::AnnotateSnippet(_) = kind { + if let HumanReadableErrorType::AnnotateSnippet = kind { let emitter = AnnotateSnippetEmitter::new( Some(source_map), bundle, @@ -978,13 +978,14 @@ fn default_emitter( Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } } - config::ErrorOutputType::Json { pretty, json_rendered } => Box::new( + config::ErrorOutputType::Json { pretty, json_rendered, color_config } => Box::new( JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), source_map, fallback_bundle, pretty, json_rendered, + color_config, ) .registry(Some(registry)) .fluent_bundle(bundle) @@ -1425,20 +1426,23 @@ fn mk_emitter(output: ErrorOutputType) -> Box { let fallback_bundle = fallback_fluent_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false); let emitter: Box = match output { - config::ErrorOutputType::HumanReadable(kind) => { - let (short, color_config) = kind.unzip(); + config::ErrorOutputType::HumanReadable(kind, color_config) => { + let short = kind.short(); Box::new( HumanEmitter::new(stderr_destination(color_config), fallback_bundle) .short_message(short), ) } - config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::new( - Box::new(io::BufWriter::new(io::stderr())), - Lrc::new(SourceMap::new(FilePathMapping::empty())), - fallback_bundle, - pretty, - json_rendered, - )), + config::ErrorOutputType::Json { pretty, json_rendered, color_config } => { + Box::new(JsonEmitter::new( + Box::new(io::BufWriter::new(io::stderr())), + Lrc::new(SourceMap::new(FilePathMapping::empty())), + fallback_bundle, + pretty, + json_rendered, + color_config, + )) + } }; emitter } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 32fca6733bb5..9cb729ec4858 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1619,6 +1619,7 @@ symbols! { rustc_dirty, rustc_do_not_const_check, rustc_doc_primitive, + rustc_driver, rustc_dummy, rustc_dump_def_parents, rustc_dump_item_bounds, diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index ecc91ab9a310..b2116c512169 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -9,12 +9,12 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] #![feature(iter_intersperse)] #![feature(let_chains)] -#![feature(min_exhaustive_patterns)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 946947124c6e..8ce51ba2463a 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -3149,11 +3149,10 @@ impl Target { if let Some(a) = o.as_array() { for o in a { if let Some(s) = o.as_str() { - let p = s.split('=').collect::>(); - if p.len() == 2 { - let k = p[0].to_string(); - let v = p[1].to_string(); - base.$key_name.to_mut().push((k.into(), v.into())); + if let [k, v] = *s.split('=').collect::>() { + base.$key_name + .to_mut() + .push((k.to_string().into(), v.to_string().into())) } } } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs index f37781c3f638..65f2a1e30698 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs @@ -17,7 +17,7 @@ pub fn target() -> Target { llvm_target: macos_llvm_target(arch).into(), metadata: crate::spec::TargetMetadata { description: Some("ARM64 macOS (11.0+, Big Sur+)".into()), - tier: Some(2), + tier: Some(1), host_tools: Some(true), std: Some(true), }, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 64f966d7a305..da66ba270b33 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::symbol::{sym, Symbol}; /// Features that control behaviour of rustc, rather than the codegen. @@ -53,136 +54,154 @@ impl Stability { // // Stabilizing a target feature requires t-lang approval. -const ARM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +type ImpliedFeatures = &'static [&'static str]; + +const ARM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("aclass", Unstable(sym::arm_target_feature)), - ("aes", Unstable(sym::arm_target_feature)), - ("crc", Unstable(sym::arm_target_feature)), - ("d32", Unstable(sym::arm_target_feature)), - ("dotprod", Unstable(sym::arm_target_feature)), - ("dsp", Unstable(sym::arm_target_feature)), - ("fp-armv8", Unstable(sym::arm_target_feature)), - ("i8mm", Unstable(sym::arm_target_feature)), - ("mclass", Unstable(sym::arm_target_feature)), - ("neon", Unstable(sym::arm_target_feature)), - ("rclass", Unstable(sym::arm_target_feature)), - ("sha2", Unstable(sym::arm_target_feature)), + ("aclass", Unstable(sym::arm_target_feature), &[]), + ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ("crc", Unstable(sym::arm_target_feature), &[]), + ("d32", Unstable(sym::arm_target_feature), &[]), + ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), + ("dsp", Unstable(sym::arm_target_feature), &[]), + ("fp-armv8", Unstable(sym::arm_target_feature), &["vfp4"]), + ("i8mm", Unstable(sym::arm_target_feature), &["neon"]), + ("mclass", Unstable(sym::arm_target_feature), &[]), + ("neon", Unstable(sym::arm_target_feature), &["vfp3"]), + ("rclass", Unstable(sym::arm_target_feature), &[]), + ("sha2", Unstable(sym::arm_target_feature), &["neon"]), // This is needed for inline assembly, but shouldn't be stabilized as-is // since it should be enabled per-function using #[instruction_set], not // #[target_feature]. - ("thumb-mode", Unstable(sym::arm_target_feature)), - ("thumb2", Unstable(sym::arm_target_feature)), - ("trustzone", Unstable(sym::arm_target_feature)), - ("v5te", Unstable(sym::arm_target_feature)), - ("v6", Unstable(sym::arm_target_feature)), - ("v6k", Unstable(sym::arm_target_feature)), - ("v6t2", Unstable(sym::arm_target_feature)), - ("v7", Unstable(sym::arm_target_feature)), - ("v8", Unstable(sym::arm_target_feature)), - ("vfp2", Unstable(sym::arm_target_feature)), - ("vfp3", Unstable(sym::arm_target_feature)), - ("vfp4", Unstable(sym::arm_target_feature)), - ("virtualization", Unstable(sym::arm_target_feature)), + ("thumb-mode", Unstable(sym::arm_target_feature), &[]), + ("thumb2", Unstable(sym::arm_target_feature), &[]), + ("trustzone", Unstable(sym::arm_target_feature), &[]), + ("v5te", Unstable(sym::arm_target_feature), &[]), + ("v6", Unstable(sym::arm_target_feature), &["v5te"]), + ("v6k", Unstable(sym::arm_target_feature), &["v6"]), + ("v6t2", Unstable(sym::arm_target_feature), &["v6k", "thumb2"]), + ("v7", Unstable(sym::arm_target_feature), &["v6t2"]), + ("v8", Unstable(sym::arm_target_feature), &["v7"]), + ("vfp2", Unstable(sym::arm_target_feature), &[]), + ("vfp3", Unstable(sym::arm_target_feature), &["vfp2", "d32"]), + ("vfp4", Unstable(sym::arm_target_feature), &["vfp3"]), + ("virtualization", Unstable(sym::arm_target_feature), &[]), // tidy-alphabetical-end ]; -const AARCH64_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const AARCH64_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start // FEAT_AES & FEAT_PMULL - ("aes", Stable), + ("aes", Stable, &["neon"]), // FEAT_BF16 - ("bf16", Stable), + ("bf16", Stable, &[]), // FEAT_BTI - ("bti", Stable), + ("bti", Stable, &[]), // FEAT_CRC - ("crc", Stable), + ("crc", Stable, &[]), // FEAT_DIT - ("dit", Stable), + ("dit", Stable, &[]), // FEAT_DotProd - ("dotprod", Stable), + ("dotprod", Stable, &["neon"]), // FEAT_DPB - ("dpb", Stable), + ("dpb", Stable, &[]), // FEAT_DPB2 - ("dpb2", Stable), + ("dpb2", Stable, &["dpb"]), // FEAT_F32MM - ("f32mm", Stable), + ("f32mm", Stable, &["sve"]), // FEAT_F64MM - ("f64mm", Stable), + ("f64mm", Stable, &["sve"]), // FEAT_FCMA - ("fcma", Stable), + ("fcma", Stable, &["neon"]), // FEAT_FHM - ("fhm", Stable), + ("fhm", Stable, &["fp16"]), // FEAT_FLAGM - ("flagm", Stable), + ("flagm", Stable, &[]), // FEAT_FP16 - ("fp16", Stable), + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("fp16", Stable, &["neon"]), // FEAT_FRINTTS - ("frintts", Stable), + ("frintts", Stable, &[]), // FEAT_I8MM - ("i8mm", Stable), + ("i8mm", Stable, &[]), // FEAT_JSCVT - ("jsconv", Stable), + // Rust ties FP and Neon: https://github.com/rust-lang/rust/pull/91608 + ("jsconv", Stable, &["neon"]), // FEAT_LOR - ("lor", Stable), + ("lor", Stable, &[]), // FEAT_LSE - ("lse", Stable), + ("lse", Stable, &[]), // FEAT_MTE & FEAT_MTE2 - ("mte", Stable), + ("mte", Stable, &[]), // FEAT_AdvSimd & FEAT_FP - ("neon", Stable), + ("neon", Stable, &[]), // FEAT_PAUTH (address authentication) - ("paca", Stable), + ("paca", Stable, &[]), // FEAT_PAUTH (generic authentication) - ("pacg", Stable), + ("pacg", Stable, &[]), // FEAT_PAN - ("pan", Stable), + ("pan", Stable, &[]), // FEAT_PMUv3 - ("pmuv3", Stable), + ("pmuv3", Stable, &[]), // FEAT_RAND - ("rand", Stable), + ("rand", Stable, &[]), // FEAT_RAS & FEAT_RASv1p1 - ("ras", Stable), + ("ras", Stable, &[]), // FEAT_RCPC - ("rcpc", Stable), + ("rcpc", Stable, &[]), // FEAT_RCPC2 - ("rcpc2", Stable), + ("rcpc2", Stable, &["rcpc"]), // FEAT_RDM - ("rdm", Stable), + ("rdm", Stable, &["neon"]), // FEAT_SB - ("sb", Stable), + ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 - ("sha2", Stable), + ("sha2", Stable, &["neon"]), // FEAT_SHA512 & FEAT_SHA3 - ("sha3", Stable), + ("sha3", Stable, &["sha2"]), // FEAT_SM3 & FEAT_SM4 - ("sm4", Stable), + ("sm4", Stable, &["neon"]), // FEAT_SPE - ("spe", Stable), + ("spe", Stable, &[]), // FEAT_SSBS & FEAT_SSBS2 - ("ssbs", Stable), + ("ssbs", Stable, &[]), // FEAT_SVE - ("sve", Stable), + // It was decided that SVE requires Neon: https://github.com/rust-lang/rust/pull/91608 + // + // LLVM doesn't enable Neon for SVE. ARM indicates that they're separate, but probably always + // exist together: https://developer.arm.com/documentation/102340/0100/New-features-in-SVE2 + // + // "For backwards compatibility, Neon and VFP are required in the latest architectures." + ("sve", Stable, &["neon"]), // FEAT_SVE2 - ("sve2", Stable), + ("sve2", Stable, &["sve"]), // FEAT_SVE2_AES - ("sve2-aes", Stable), + ("sve2-aes", Stable, &["sve2", "aes"]), // FEAT_SVE2_BitPerm - ("sve2-bitperm", Stable), + ("sve2-bitperm", Stable, &["sve2"]), // FEAT_SVE2_SHA3 - ("sve2-sha3", Stable), + ("sve2-sha3", Stable, &["sve2", "sha3"]), // FEAT_SVE2_SM4 - ("sve2-sm4", Stable), + ("sve2-sm4", Stable, &["sve2", "sm4"]), // FEAT_TME - ("tme", Stable), - ("v8.1a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.2a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.3a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.4a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.5a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.6a", Unstable(sym::aarch64_ver_target_feature)), - ("v8.7a", Unstable(sym::aarch64_ver_target_feature)), + ("tme", Stable, &[]), + ( + "v8.1a", + Unstable(sym::aarch64_ver_target_feature), + &["crc", "lse", "rdm", "pan", "lor", "vh"], + ), + ("v8.2a", Unstable(sym::aarch64_ver_target_feature), &["v8.1a", "ras", "dpb"]), + ( + "v8.3a", + Unstable(sym::aarch64_ver_target_feature), + &["v8.2a", "rcpc", "paca", "pacg", "jsconv"], + ), + ("v8.4a", Unstable(sym::aarch64_ver_target_feature), &["v8.3a", "dotprod", "dit", "flagm"]), + ("v8.5a", Unstable(sym::aarch64_ver_target_feature), &["v8.4a", "ssbs", "sb", "dpb2", "bti"]), + ("v8.6a", Unstable(sym::aarch64_ver_target_feature), &["v8.5a", "bf16", "i8mm"]), + ("v8.7a", Unstable(sym::aarch64_ver_target_feature), &[]), // FEAT_VHE - ("vh", Stable), + ("vh", Stable, &[]), // tidy-alphabetical-end ]; @@ -190,295 +209,223 @@ const AARCH64_TIED_FEATURES: &[&[&str]] = &[ &["paca", "pacg"], // Together these represent `pauth` in LLVM ]; -const X86_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const X86_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("adx", Stable), - ("aes", Stable), - ("amx-bf16", Unstable(sym::x86_amx_intrinsics)), - ("amx-complex", Unstable(sym::x86_amx_intrinsics)), - ("amx-fp16", Unstable(sym::x86_amx_intrinsics)), - ("amx-int8", Unstable(sym::x86_amx_intrinsics)), - ("amx-tile", Unstable(sym::x86_amx_intrinsics)), - ("avx", Stable), - ("avx2", Stable), - ("avx512bf16", Unstable(sym::avx512_target_feature)), - ("avx512bitalg", Unstable(sym::avx512_target_feature)), - ("avx512bw", Unstable(sym::avx512_target_feature)), - ("avx512cd", Unstable(sym::avx512_target_feature)), - ("avx512dq", Unstable(sym::avx512_target_feature)), - ("avx512f", Unstable(sym::avx512_target_feature)), - ("avx512fp16", Unstable(sym::avx512_target_feature)), - ("avx512ifma", Unstable(sym::avx512_target_feature)), - ("avx512vbmi", Unstable(sym::avx512_target_feature)), - ("avx512vbmi2", Unstable(sym::avx512_target_feature)), - ("avx512vl", Unstable(sym::avx512_target_feature)), - ("avx512vnni", Unstable(sym::avx512_target_feature)), - ("avx512vp2intersect", Unstable(sym::avx512_target_feature)), - ("avx512vpopcntdq", Unstable(sym::avx512_target_feature)), - ("avxifma", Unstable(sym::avx512_target_feature)), - ("avxneconvert", Unstable(sym::avx512_target_feature)), - ("avxvnni", Unstable(sym::avx512_target_feature)), - ("avxvnniint16", Unstable(sym::avx512_target_feature)), - ("avxvnniint8", Unstable(sym::avx512_target_feature)), - ("bmi1", Stable), - ("bmi2", Stable), - ("cmpxchg16b", Stable), - ("ermsb", Unstable(sym::ermsb_target_feature)), - ("f16c", Stable), - ("fma", Stable), - ("fxsr", Stable), - ("gfni", Unstable(sym::avx512_target_feature)), - ("lahfsahf", Unstable(sym::lahfsahf_target_feature)), - ("lzcnt", Stable), - ("movbe", Stable), - ("pclmulqdq", Stable), - ("popcnt", Stable), - ("prfchw", Unstable(sym::prfchw_target_feature)), - ("rdrand", Stable), - ("rdseed", Stable), - ("rtm", Unstable(sym::rtm_target_feature)), - ("sha", Stable), - ("sha512", Unstable(sym::sha512_sm_x86)), - ("sm3", Unstable(sym::sha512_sm_x86)), - ("sm4", Unstable(sym::sha512_sm_x86)), - ("sse", Stable), - ("sse2", Stable), - ("sse3", Stable), - ("sse4.1", Stable), - ("sse4.2", Stable), - ("sse4a", Unstable(sym::sse4a_target_feature)), - ("ssse3", Stable), - ("tbm", Unstable(sym::tbm_target_feature)), - ("vaes", Unstable(sym::avx512_target_feature)), - ("vpclmulqdq", Unstable(sym::avx512_target_feature)), - ("xop", Unstable(sym::xop_target_feature)), - ("xsave", Stable), - ("xsavec", Stable), - ("xsaveopt", Stable), - ("xsaves", Stable), + ("adx", Stable, &[]), + ("aes", Stable, &["sse2"]), + ("amx-bf16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-complex", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-fp16", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-int8", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]), + ("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]), + ("avx", Stable, &["sse4.2"]), + ("avx2", Stable, &["avx"]), + ("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]), + ("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw", "avx512vl", "avx512dq"]), + ("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]), + ("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]), + ("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]), + ("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]), + ("bmi1", Stable, &[]), + ("bmi2", Stable, &[]), + ("cmpxchg16b", Stable, &[]), + ("ermsb", Unstable(sym::ermsb_target_feature), &[]), + ("f16c", Stable, &["avx"]), + ("fma", Stable, &["avx"]), + ("fxsr", Stable, &[]), + ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), + ("lzcnt", Stable, &[]), + ("movbe", Stable, &[]), + ("pclmulqdq", Stable, &[]), + ("popcnt", Stable, &[]), + ("prfchw", Unstable(sym::prfchw_target_feature), &[]), + ("rdrand", Stable, &[]), + ("rdseed", Stable, &[]), + ("rtm", Unstable(sym::rtm_target_feature), &[]), + ("sha", Stable, &["sse2"]), + ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sm3", Unstable(sym::sha512_sm_x86), &["avx"]), + ("sm4", Unstable(sym::sha512_sm_x86), &["avx2"]), + ("sse", Stable, &[]), + ("sse2", Stable, &["sse"]), + ("sse3", Stable, &["sse2"]), + ("sse4.1", Stable, &["ssse3"]), + ("sse4.2", Stable, &["sse4.1"]), + ("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]), + ("ssse3", Stable, &["sse3"]), + ("tbm", Unstable(sym::tbm_target_feature), &[]), + ("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]), + ("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]), + ("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]), + ("xsave", Stable, &[]), + ("xsavec", Stable, &["xsave"]), + ("xsaveopt", Stable, &["xsave"]), + ("xsaves", Stable, &["xsave"]), // tidy-alphabetical-end ]; -const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const HEXAGON_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("hvx", Unstable(sym::hexagon_target_feature)), - ("hvx-length128b", Unstable(sym::hexagon_target_feature)), + ("hvx", Unstable(sym::hexagon_target_feature), &[]), + ("hvx-length128b", Unstable(sym::hexagon_target_feature), &["hvx"]), // tidy-alphabetical-end ]; -const POWERPC_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const POWERPC_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("altivec", Unstable(sym::powerpc_target_feature)), - ("power10-vector", Unstable(sym::powerpc_target_feature)), - ("power8-altivec", Unstable(sym::powerpc_target_feature)), - ("power8-vector", Unstable(sym::powerpc_target_feature)), - ("power9-altivec", Unstable(sym::powerpc_target_feature)), - ("power9-vector", Unstable(sym::powerpc_target_feature)), - ("vsx", Unstable(sym::powerpc_target_feature)), + ("altivec", Unstable(sym::powerpc_target_feature), &[]), + ("power10-vector", Unstable(sym::powerpc_target_feature), &["power9-vector"]), + ("power8-altivec", Unstable(sym::powerpc_target_feature), &["altivec"]), + ("power8-vector", Unstable(sym::powerpc_target_feature), &["vsx", "power8-altivec"]), + ("power9-altivec", Unstable(sym::powerpc_target_feature), &["power8-altivec"]), + ("power9-vector", Unstable(sym::powerpc_target_feature), &["power8-vector", "power9-altivec"]), + ("vsx", Unstable(sym::powerpc_target_feature), &["altivec"]), // tidy-alphabetical-end ]; -const MIPS_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const MIPS_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("fp64", Unstable(sym::mips_target_feature)), - ("msa", Unstable(sym::mips_target_feature)), - ("virt", Unstable(sym::mips_target_feature)), + ("fp64", Unstable(sym::mips_target_feature), &[]), + ("msa", Unstable(sym::mips_target_feature), &[]), + ("virt", Unstable(sym::mips_target_feature), &[]), // tidy-alphabetical-end ]; -const RISCV_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const RISCV_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("a", Stable), - ("c", Stable), - ("d", Unstable(sym::riscv_target_feature)), - ("e", Unstable(sym::riscv_target_feature)), - ("f", Unstable(sym::riscv_target_feature)), - ("m", Stable), - ("relax", Unstable(sym::riscv_target_feature)), - ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature)), - ("v", Unstable(sym::riscv_target_feature)), - ("zba", Stable), - ("zbb", Stable), - ("zbc", Stable), - ("zbkb", Stable), - ("zbkc", Stable), - ("zbkx", Stable), - ("zbs", Stable), - ("zdinx", Unstable(sym::riscv_target_feature)), - ("zfh", Unstable(sym::riscv_target_feature)), - ("zfhmin", Unstable(sym::riscv_target_feature)), - ("zfinx", Unstable(sym::riscv_target_feature)), - ("zhinx", Unstable(sym::riscv_target_feature)), - ("zhinxmin", Unstable(sym::riscv_target_feature)), - ("zk", Stable), - ("zkn", Stable), - ("zknd", Stable), - ("zkne", Stable), - ("zknh", Stable), - ("zkr", Stable), - ("zks", Stable), - ("zksed", Stable), - ("zksh", Stable), - ("zkt", Stable), + ("a", Stable, &[]), + ("c", Stable, &[]), + ("d", Unstable(sym::riscv_target_feature), &["f"]), + ("e", Unstable(sym::riscv_target_feature), &[]), + ("f", Unstable(sym::riscv_target_feature), &[]), + ("m", Stable, &[]), + ("relax", Unstable(sym::riscv_target_feature), &[]), + ("unaligned-scalar-mem", Unstable(sym::riscv_target_feature), &[]), + ("v", Unstable(sym::riscv_target_feature), &[]), + ("zba", Stable, &[]), + ("zbb", Stable, &[]), + ("zbc", Stable, &[]), + ("zbkb", Stable, &[]), + ("zbkc", Stable, &[]), + ("zbkx", Stable, &[]), + ("zbs", Stable, &[]), + ("zdinx", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zfh", Unstable(sym::riscv_target_feature), &["zfhmin"]), + ("zfhmin", Unstable(sym::riscv_target_feature), &["f"]), + ("zfinx", Unstable(sym::riscv_target_feature), &[]), + ("zhinx", Unstable(sym::riscv_target_feature), &["zhinxmin"]), + ("zhinxmin", Unstable(sym::riscv_target_feature), &["zfinx"]), + ("zk", Stable, &["zkn", "zkr", "zkt"]), + ("zkn", Stable, &["zbkb", "zbkc", "zbkx", "zkne", "zknd", "zknh"]), + ("zknd", Stable, &[]), + ("zkne", Stable, &[]), + ("zknh", Stable, &[]), + ("zkr", Stable, &[]), + ("zks", Stable, &["zbkb", "zbkc", "zbkx", "zksed", "zksh"]), + ("zksed", Stable, &[]), + ("zksh", Stable, &[]), + ("zkt", Stable, &[]), // tidy-alphabetical-end ]; -const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const WASM_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("atomics", Unstable(sym::wasm_target_feature)), - ("bulk-memory", Stable), - ("exception-handling", Unstable(sym::wasm_target_feature)), - ("extended-const", Stable), - ("multivalue", Unstable(sym::wasm_target_feature)), - ("mutable-globals", Stable), - ("nontrapping-fptoint", Stable), - ("reference-types", Unstable(sym::wasm_target_feature)), - ("relaxed-simd", Stable), - ("sign-ext", Stable), - ("simd128", Stable), + ("atomics", Unstable(sym::wasm_target_feature), &[]), + ("bulk-memory", Stable, &[]), + ("exception-handling", Unstable(sym::wasm_target_feature), &[]), + ("extended-const", Stable, &[]), + ("multivalue", Unstable(sym::wasm_target_feature), &[]), + ("mutable-globals", Stable, &[]), + ("nontrapping-fptoint", Stable, &[]), + ("reference-types", Unstable(sym::wasm_target_feature), &[]), + ("relaxed-simd", Stable, &["simd128"]), + ("sign-ext", Stable, &[]), + ("simd128", Stable, &[]), // tidy-alphabetical-end ]; -const BPF_ALLOWED_FEATURES: &[(&str, Stability)] = &[("alu32", Unstable(sym::bpf_target_feature))]; +const BPF_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = + &[("alu32", Unstable(sym::bpf_target_feature), &[])]; -const CSKY_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const CSKY_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("10e60", Unstable(sym::csky_target_feature)), - ("2e3", Unstable(sym::csky_target_feature)), - ("3e3r1", Unstable(sym::csky_target_feature)), - ("3e3r2", Unstable(sym::csky_target_feature)), - ("3e3r3", Unstable(sym::csky_target_feature)), - ("3e7", Unstable(sym::csky_target_feature)), - ("7e10", Unstable(sym::csky_target_feature)), - ("cache", Unstable(sym::csky_target_feature)), - ("doloop", Unstable(sym::csky_target_feature)), - ("dsp1e2", Unstable(sym::csky_target_feature)), - ("dspe60", Unstable(sym::csky_target_feature)), - ("e1", Unstable(sym::csky_target_feature)), - ("e2", Unstable(sym::csky_target_feature)), - ("edsp", Unstable(sym::csky_target_feature)), - ("elrw", Unstable(sym::csky_target_feature)), - ("float1e2", Unstable(sym::csky_target_feature)), - ("float1e3", Unstable(sym::csky_target_feature)), - ("float3e4", Unstable(sym::csky_target_feature)), - ("float7e60", Unstable(sym::csky_target_feature)), - ("floate1", Unstable(sym::csky_target_feature)), - ("hard-tp", Unstable(sym::csky_target_feature)), - ("high-registers", Unstable(sym::csky_target_feature)), - ("hwdiv", Unstable(sym::csky_target_feature)), - ("mp", Unstable(sym::csky_target_feature)), - ("mp1e2", Unstable(sym::csky_target_feature)), - ("nvic", Unstable(sym::csky_target_feature)), - ("trust", Unstable(sym::csky_target_feature)), - ("vdsp2e60f", Unstable(sym::csky_target_feature)), - ("vdspv1", Unstable(sym::csky_target_feature)), - ("vdspv2", Unstable(sym::csky_target_feature)), + ("10e60", Unstable(sym::csky_target_feature), &["7e10"]), + ("2e3", Unstable(sym::csky_target_feature), &["e2"]), + ("3e3r1", Unstable(sym::csky_target_feature), &[]), + ("3e3r2", Unstable(sym::csky_target_feature), &["3e3r1", "doloop"]), + ("3e3r3", Unstable(sym::csky_target_feature), &["doloop"]), + ("3e7", Unstable(sym::csky_target_feature), &["2e3"]), + ("7e10", Unstable(sym::csky_target_feature), &["3e7"]), + ("cache", Unstable(sym::csky_target_feature), &[]), + ("doloop", Unstable(sym::csky_target_feature), &[]), + ("dsp1e2", Unstable(sym::csky_target_feature), &[]), + ("dspe60", Unstable(sym::csky_target_feature), &[]), + ("e1", Unstable(sym::csky_target_feature), &["elrw"]), + ("e2", Unstable(sym::csky_target_feature), &["e2"]), + ("edsp", Unstable(sym::csky_target_feature), &[]), + ("elrw", Unstable(sym::csky_target_feature), &[]), + ("float1e2", Unstable(sym::csky_target_feature), &[]), + ("float1e3", Unstable(sym::csky_target_feature), &[]), + ("float3e4", Unstable(sym::csky_target_feature), &[]), + ("float7e60", Unstable(sym::csky_target_feature), &[]), + ("floate1", Unstable(sym::csky_target_feature), &[]), + ("hard-tp", Unstable(sym::csky_target_feature), &[]), + ("high-registers", Unstable(sym::csky_target_feature), &[]), + ("hwdiv", Unstable(sym::csky_target_feature), &[]), + ("mp", Unstable(sym::csky_target_feature), &["2e3"]), + ("mp1e2", Unstable(sym::csky_target_feature), &["3e7"]), + ("nvic", Unstable(sym::csky_target_feature), &[]), + ("trust", Unstable(sym::csky_target_feature), &[]), + ("vdsp2e60f", Unstable(sym::csky_target_feature), &[]), + ("vdspv1", Unstable(sym::csky_target_feature), &[]), + ("vdspv2", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end //fpu // tidy-alphabetical-start - ("fdivdu", Unstable(sym::csky_target_feature)), - ("fpuv2_df", Unstable(sym::csky_target_feature)), - ("fpuv2_sf", Unstable(sym::csky_target_feature)), - ("fpuv3_df", Unstable(sym::csky_target_feature)), - ("fpuv3_hf", Unstable(sym::csky_target_feature)), - ("fpuv3_hi", Unstable(sym::csky_target_feature)), - ("fpuv3_sf", Unstable(sym::csky_target_feature)), - ("hard-float", Unstable(sym::csky_target_feature)), - ("hard-float-abi", Unstable(sym::csky_target_feature)), + ("fdivdu", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv2_sf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_df", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hf", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_hi", Unstable(sym::csky_target_feature), &[]), + ("fpuv3_sf", Unstable(sym::csky_target_feature), &[]), + ("hard-float", Unstable(sym::csky_target_feature), &[]), + ("hard-float-abi", Unstable(sym::csky_target_feature), &[]), // tidy-alphabetical-end ]; -const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("d", Unstable(sym::loongarch_target_feature)), - ("f", Unstable(sym::loongarch_target_feature)), - ("frecipe", Unstable(sym::loongarch_target_feature)), - ("lasx", Unstable(sym::loongarch_target_feature)), - ("lbt", Unstable(sym::loongarch_target_feature)), - ("lsx", Unstable(sym::loongarch_target_feature)), - ("lvz", Unstable(sym::loongarch_target_feature)), - ("relax", Unstable(sym::loongarch_target_feature)), - ("ual", Unstable(sym::loongarch_target_feature)), + ("d", Unstable(sym::loongarch_target_feature), &["f"]), + ("f", Unstable(sym::loongarch_target_feature), &[]), + ("frecipe", Unstable(sym::loongarch_target_feature), &[]), + ("lasx", Unstable(sym::loongarch_target_feature), &["lsx"]), + ("lbt", Unstable(sym::loongarch_target_feature), &[]), + ("lsx", Unstable(sym::loongarch_target_feature), &["d"]), + ("lvz", Unstable(sym::loongarch_target_feature), &[]), + ("relax", Unstable(sym::loongarch_target_feature), &[]), + ("ual", Unstable(sym::loongarch_target_feature), &[]), // tidy-alphabetical-end ]; -const IBMZ_ALLOWED_FEATURES: &[(&str, Stability)] = &[ +const IBMZ_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start - ("backchain", Unstable(sym::s390x_target_feature)), - ("vector", Unstable(sym::s390x_target_feature)), - // tidy-alphabetical-end -]; - -const X86_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ - // tidy-alphabetical-start - ("aes", &["sse2"]), - ("avx", &["sse4.2"]), - ("avx2", &["avx"]), - ("avx512bf16", &["avx512bw"]), - ("avx512bitalg", &["avx512bw"]), - ("avx512bw", &["avx512f"]), - ("avx512cd", &["avx512f"]), - ("avx512dq", &["avx512f"]), - ("avx512f", &["avx2"]), - ("avx512fp16", &["avx512bw", "avx512vl", "avx512dq"]), - ("avx512vbmi", &["avx512bw"]), - ("avx512vbmi2", &["avx512bw"]), - ("avx512vl", &["avx512f"]), - ("avx512vnni", &["avx512f"]), - ("avx512vp2intersect", &["avx512f"]), - ("avx512vpopcntdq", &["avx512f"]), - ("f16c", &["avx"]), - ("fma", &["avx"]), - ("gfni", &["sse2"]), - ("pclmulqdq", &["sse2"]), - ("sha", &["sse2"]), - ("sse2", &["sse"]), - ("sse3", &["sse2"]), - ("sse4.1", &["ssse3"]), - ("sse4.2", &["sse4.1"]), - ("ssse3", &["sse3"]), - ("vaes", &["avx", "aes"]), - ("vpclmulqdq", &["avx", "pclmulqdq"]), - ("xsavec", &["xsave"]), - ("xsaveopt", &["xsave"]), - ("xsaves", &["xsave"]), - // tidy-alphabetical-end -]; - -const AARCH64_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ - // tidy-alphabetical-start - ("aes", &["neon"]), - ("f32mm", &["sve"]), - ("f64mm", &["sve"]), - ("fcma", &["neon"]), - ("fhm", &["fp16"]), - ("fp16", &["neon"]), - ("jsconv", &["neon"]), - ("rcpc2", &["rcpc"]), - ("sha2", &["neon"]), - ("sha3", &["sha2"]), - ("sm4", &["neon"]), - ("sve", &["fp16"]), - ("sve2", &["sve"]), - ("sve2-aes", &["sve2", "aes"]), - ("sve2-bitperm", &["sve2"]), - ("sve2-sha3", &["sve2", "sha3"]), - ("sve2-sm4", &["sve2", "sm4"]), - // tidy-alphabetical-end -]; - -const RISCV_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ - // tidy-alphabetical-start - ("zb", &["zba", "zbc", "zbs"]), - ("zk", &["zkn", "zkr", "zks", "zkt", "zbkb", "zbkc", "zkbx"]), - ("zkn", &["zknd", "zkne", "zknh", "zbkb", "zbkc", "zkbx"]), - ("zks", &["zksed", "zksh", "zbkb", "zbkc", "zkbx"]), - // tidy-alphabetical-end -]; - -const WASM_IMPLIED_FEATURES: &[(&str, &[&str])] = &[ - // tidy-alphabetical-start - ("relaxed-simd", &["simd128"]), + ("backchain", Unstable(sym::s390x_target_feature), &[]), + ("vector", Unstable(sym::s390x_target_feature), &[]), // tidy-alphabetical-end ]; @@ -501,10 +448,13 @@ pub fn all_known_features() -> impl Iterator { .chain(LOONGARCH_ALLOWED_FEATURES) .chain(IBMZ_ALLOWED_FEATURES) .cloned() + .map(|(f, s, _)| (f, s)) } impl super::spec::Target { - pub fn supported_target_features(&self) -> &'static [(&'static str, Stability)] { + pub fn supported_target_features( + &self, + ) -> &'static [(&'static str, Stability, ImpliedFeatures)] { match &*self.arch { "arm" => ARM_ALLOWED_FEATURES, "aarch64" | "arm64ec" => AARCH64_ALLOWED_FEATURES, @@ -529,13 +479,27 @@ impl super::spec::Target { } } - pub fn implied_target_features(&self) -> &'static [(&'static str, &'static [&'static str])] { - match &*self.arch { - "aarch4" => AARCH64_IMPLIED_FEATURES, - "riscv32" | "riscv64" => RISCV_IMPLIED_FEATURES, - "x86" | "x86_64" => X86_IMPLIED_FEATURES, - "wasm32" | "wasm64" => WASM_IMPLIED_FEATURES, - _ => &[], + pub fn implied_target_features( + &self, + base_features: impl Iterator, + ) -> FxHashSet { + let implied_features = self + .supported_target_features() + .iter() + .map(|(f, _, i)| (Symbol::intern(f), i)) + .collect::>(); + + // implied target features have their own implied target features, so we traverse the + // map until there are no more features to add + let mut features = FxHashSet::default(); + let mut new_features = base_features.collect::>(); + while let Some(new_feature) = new_features.pop() { + if features.insert(new_feature) { + if let Some(implied_features) = implied_features.get(&new_feature) { + new_features.extend(implied_features.iter().copied().map(Symbol::intern)) + } + } } + features } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 72a4d4c12056..9ab470578592 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -388,39 +388,67 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_impls.non_blanket_impls().values().flatten().count(); // If there is only one implementation of the trait, suggest using it. // Otherwise, use a placeholder comment for the implementation. - let (message, self_type) = if non_blanket_impl_count == 1 { + let (message, self_types) = if non_blanket_impl_count == 1 { ( "use the fully-qualified path to the only available \ implementation", - format!( + vec![format!( "{}", self.tcx.type_of(impl_def_id).instantiate_identity() - ), + )], + ) + } else if non_blanket_impl_count < 20 { + ( + "use a fully-qualified path to one of the available \ + implementations", + trait_impls + .non_blanket_impls() + .values() + .flatten() + .map(|id| { + format!( + "{}", + self.tcx.type_of(id).instantiate_identity() + ) + }) + .collect::>(), ) } else { ( "use a fully-qualified path to a specific available \ implementation", - "/* self type */".to_string(), + vec!["/* self type */".to_string()], ) }; - let mut suggestions = - vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))]; - if let Some(generic_arg) = trait_path_segment.args { - let between_span = - trait_path_segment.ident.span.between(generic_arg.span_ext); - // get rid of :: between Trait and - // must be '::' between them, otherwise the parser won't accept the code - suggestions.push((between_span, "".to_string())); - suggestions - .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); - } else { - suggestions.push(( - trait_path_segment.ident.span.shrink_to_hi(), - ">".to_string(), - )); - } - err.multipart_suggestion( + let suggestions: Vec<_> = self_types + .into_iter() + .map(|self_type| { + let mut suggestions = vec![( + path.span.shrink_to_lo(), + format!("<{self_type} as "), + )]; + if let Some(generic_arg) = trait_path_segment.args { + let between_span = trait_path_segment + .ident + .span + .between(generic_arg.span_ext); + // get rid of :: between Trait and + // must be '::' between them, otherwise the parser won't accept the code + suggestions.push((between_span, "".to_string())); + suggestions.push(( + generic_arg.span_ext.shrink_to_hi(), + ">".to_string(), + )); + } else { + suggestions.push(( + trait_path_segment.ident.span.shrink_to_hi(), + ">".to_string(), + )); + } + suggestions + }) + .collect(); + err.multipart_suggestions( message, suggestions, Applicability::MaybeIncorrect, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 1cee82f04ea0..95d4509c100a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -944,8 +944,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // The current method call returns `Result<_, ()>` && self.can_eq(obligation.param_env, ty, found_ty) // There's a single argument in the method call and it is a closure - && args.len() == 1 - && let Some(arg) = args.get(0) + && let [arg] = args && let hir::ExprKind::Closure(closure) = arg.kind // The closure has a block for its body with no tail expression && let body = self.tcx.hir().body(closure.body) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 3a082893c5c6..f656f9b0e383 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -73,10 +73,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } }); - let impl_def_id_and_args = if self_match_impls.len() == 1 { - self_match_impls[0] - } else if fuzzy_match_impls.len() == 1 { - fuzzy_match_impls[0] + let impl_def_id_and_args = if let [impl_] = self_match_impls[..] { + impl_ + } else if let [impl_] = fuzzy_match_impls[..] { + impl_ } else { return None; }; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 0d15ef55e24e..9269177eb503 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -5300,7 +5300,8 @@ impl<'v> Visitor<'v> for FindTypeParam { match ty.kind { hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {} hir::TyKind::Path(hir::QPath::Resolved(None, path)) - if path.segments.len() == 1 && path.segments[0].ident.name == self.param => + if let [segment] = path.segments + && segment.ident.name == self.param => { if !self.nested { debug!(?ty, "FindTypeParam::visit_ty"); diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index e8de8457440f..4e4022830d46 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -332,13 +332,9 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { pub fn candidates(&'a self) -> Vec> { let mut candidates = vec![]; - let last_eval_step = match self.evaluation_kind { - inspect::CanonicalGoalEvaluationKind::Overflow - | inspect::CanonicalGoalEvaluationKind::CycleInStack - | inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit => { - warn!("unexpected root evaluation: {:?}", self.evaluation_kind); - return vec![]; - } + let last_eval_step = match &self.evaluation_kind { + // An annoying edge case in case the recursion limit is 0. + inspect::CanonicalGoalEvaluationKind::Overflow => return vec![], inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } => final_revision, }; 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 2085d3da4434..9de62031311b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -467,8 +467,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } candidates.vec.push(AsyncClosureCandidate); } - ty::FnDef(..) | ty::FnPtr(..) => { - candidates.vec.push(AsyncClosureCandidate); + // Provide an impl, but only for suitable `fn` pointers. + ty::FnPtr(sig) => { + if sig.is_fn_trait_compatible() { + candidates.vec.push(AsyncClosureCandidate); + } + } + // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). + ty::FnDef(def_id, _) => { + let tcx = self.tcx(); + if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() + && tcx.codegen_fn_attrs(def_id).target_features.is_empty() + { + candidates.vec.push(AsyncClosureCandidate); + } } _ => {} } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1d9a90f0300a..1b2767a6a627 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -17,7 +17,6 @@ use rustc_hir::LangItem; use rustc_infer::infer::relate::TypeRelation; use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType}; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_infer::traits::util::elaborate; use rustc_infer::traits::TraitObligation; use rustc_middle::bug; use rustc_middle::dep_graph::{dep_kinds, DepNodeIndex}; @@ -2801,31 +2800,22 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } // Register any outlives obligations from the trait here, cc #124336. - if matches!(self.tcx().def_kind(def_id), DefKind::Impl { of_trait: true }) - && let Some(header) = self.tcx().impl_trait_header(def_id) - { - let trait_clause: ty::Clause<'tcx> = - header.trait_ref.instantiate(self.tcx(), args).upcast(self.tcx()); - for clause in elaborate(self.tcx(), [trait_clause]) { - if matches!( - clause.kind().skip_binder(), - ty::ClauseKind::TypeOutlives(..) | ty::ClauseKind::RegionOutlives(..) - ) { - let clause = normalize_with_depth_to( - self, - param_env, - cause.clone(), - recursion_depth, - clause, - &mut obligations, - ); - obligations.push(Obligation { - cause: cause.clone(), - recursion_depth, - param_env, - predicate: clause.as_predicate(), - }); - } + if matches!(tcx.def_kind(def_id), DefKind::Impl { of_trait: true }) { + for clause in tcx.impl_super_outlives(def_id).iter_instantiated(tcx, args) { + let clause = normalize_with_depth_to( + self, + param_env, + cause.clone(), + recursion_depth, + clause, + &mut obligations, + ); + obligations.push(Obligation { + cause: cause.clone(), + recursion_depth, + param_env, + predicate: clause.as_predicate(), + }); } } diff --git a/compiler/rustc_transmute/src/layout/nfa.rs b/compiler/rustc_transmute/src/layout/nfa.rs index 0dd26badc885..5db5a8f222db 100644 --- a/compiler/rustc_transmute/src/layout/nfa.rs +++ b/compiler/rustc_transmute/src/layout/nfa.rs @@ -87,6 +87,7 @@ where pub(crate) fn from_tree(tree: Tree) -> Result { Ok(match tree { Tree::Byte(b) => Self::from_byte(b), + #[cfg(bootstrap)] Tree::Def(..) => unreachable!(), Tree::Ref(r) => Self::from_ref(r), Tree::Alt(alts) => { diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index f1675f80717f..d90c3bedc701 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -549,7 +549,7 @@ fn fn_abi_sanity_check<'tcx>( // With metadata. Must be unsized and not on the stack. assert!(arg.layout.is_unsized() && !on_stack); // Also, must not be `extern` type. - let tail = cx.tcx.struct_tail_with_normalize(arg.layout.ty, |ty| ty, || {}); + let tail = cx.tcx.struct_tail_for_codegen(arg.layout.ty, cx.param_env()); if matches!(tail.kind(), ty::Foreign(..)) { // These types do not have metadata, so having `meta_attrs` is bogus. // Conceptually, unsized arguments must be copied around, which requires dynamically diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 3ef10f4e43c4..1eb03fc3bd6a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -244,7 +244,7 @@ fn layout_of_uncached<'tcx>( metadata } else { - let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); + let unsized_part = tcx.struct_tail_for_codegen(pointee, param_env); match unsized_part.kind() { ty::Foreign(..) => { diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index c1f6fb36324e..8797288070e7 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -8,7 +8,7 @@ use derive_where::derive_where; use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; #[cfg(feature = "nightly")] use rustc_serialize::Decodable; -use tracing::debug; +use tracing::instrument; use crate::data_structures::SsoHashSet; use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -831,28 +831,20 @@ impl<'a, I: Interner> ArgFolder<'a, I> { /// As indicated in the diagram, here the same type `&'a i32` is instantiated once, but in the /// first case we do not increase the De Bruijn index and in the second case we do. The reason /// is that only in the second case have we passed through a fn binder. + #[instrument(level = "trace", skip(self), fields(binders_passed = self.binders_passed), ret)] fn shift_vars_through_binders>(&self, val: T) -> T { - debug!( - "shift_vars(val={:?}, binders_passed={:?}, has_escaping_bound_vars={:?})", - val, - self.binders_passed, - val.has_escaping_bound_vars() - ); - if self.binders_passed == 0 || !val.has_escaping_bound_vars() { - return val; + val + } else { + ty::fold::shift_vars(self.cx, val, self.binders_passed) } - - let result = ty::fold::shift_vars(TypeFolder::cx(self), val, self.binders_passed); - debug!("shift_vars: shifted result = {:?}", result); - - result } fn shift_region_through_binders(&self, region: I::Region) -> I::Region { if self.binders_passed == 0 || !region.has_escaping_bound_vars() { - return region; + region + } else { + ty::fold::shift_region(self.cx, region, self.binders_passed) } - ty::fold::shift_region(self.cx, region, self.binders_passed) } } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 16dcf76e73eb..8e3534b0e9eb 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -48,7 +48,7 @@ use std::mem; use rustc_index::{Idx, IndexVec}; -use tracing::debug; +use tracing::instrument; use crate::data_structures::Lrc; use crate::inherent::*; @@ -91,6 +91,7 @@ pub trait TypeFoldable: TypeVisitable { fn fold_with>(self, folder: &mut F) -> Self { match self.try_fold_with(folder) { Ok(t) => t, + #[cfg(bootstrap)] Err(e) => match e {}, } } @@ -115,6 +116,7 @@ pub trait TypeSuperFoldable: TypeFoldable { fn super_fold_with>(self, folder: &mut F) -> Self { match self.try_super_fold_with(folder) { Ok(t) => t, + #[cfg(bootstrap)] Err(e) => match e {}, } } @@ -415,15 +417,14 @@ pub fn shift_region(cx: I, region: I::Region, amount: u32) -> I::Re } } +#[instrument(level = "trace", skip(cx), ret)] pub fn shift_vars(cx: I, value: T, amount: u32) -> T where T: TypeFoldable, { - debug!("shift_vars(value={:?}, amount={})", value, amount); - if amount == 0 || !value.has_escaping_bound_vars() { - return value; + value + } else { + value.fold_with(&mut Shifter::new(cx, amount)) } - - value.fold_with(&mut Shifter::new(cx, amount)) } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index c251540c0fc2..f2492ede4f5e 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -11,7 +11,6 @@ use crate::inherent::*; use crate::ir_print::IrPrint; use crate::lang_items::TraitSolverLangItem; use crate::relate::Relate; -use crate::solve::inspect::CanonicalGoalEvaluationStep; use crate::solve::{ CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult, SolverMode, }; @@ -65,11 +64,6 @@ pub trait Interner: + Eq + TypeVisitable + SliceLike; - type CanonicalGoalEvaluationStepRef: Copy - + Debug - + Hash - + Eq - + Deref>; type CanonicalVars: Copy + Debug @@ -177,11 +171,6 @@ pub trait Interner: fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs); - fn intern_canonical_goal_evaluation_step( - self, - step: CanonicalGoalEvaluationStep, - ) -> Self::CanonicalGoalEvaluationStepRef; - fn mk_type_list_from_iter(self, args: I) -> T::Output where I: Iterator, @@ -390,7 +379,6 @@ impl CollectAndApply for Result { } impl search_graph::Cx for I { - type ProofTree = Option; type Input = CanonicalInput; type Result = QueryResult; diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index be4f1069cd16..47f7cefac6ad 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -1,18 +1,17 @@ use derive_where::derive_where; -use rustc_index::IndexVec; -use super::{AvailableDepth, Cx, StackDepth, StackEntry}; -use crate::data_structures::{HashMap, HashSet}; - -#[derive_where(Debug, Clone, Copy; X: Cx)] -struct QueryData { - result: X::Result, - proof_tree: X::ProofTree, -} +use super::{AvailableDepth, Cx, NestedGoals}; +use crate::data_structures::HashMap; struct Success { - data: X::Tracked>, additional_depth: usize, + nested_goals: NestedGoals, + result: X::Tracked, +} + +struct WithOverflow { + nested_goals: NestedGoals, + result: X::Tracked, } /// The cache entry for a given input. @@ -23,24 +22,15 @@ struct Success { #[derive_where(Default; X: Cx)] struct CacheEntry { success: Option>, - /// We have to be careful when caching roots of cycles. - /// - /// See the doc comment of `StackEntry::cycle_participants` for more - /// details. - nested_goals: HashSet, - with_overflow: HashMap>>, + with_overflow: HashMap>, } #[derive_where(Debug; X: Cx)] pub(super) struct CacheData<'a, X: Cx> { pub(super) result: X::Result, - pub(super) proof_tree: X::ProofTree, pub(super) additional_depth: usize, pub(super) encountered_overflow: bool, - // FIXME: This is currently unused, but impacts the design - // by requiring a closure for `Cx::with_global_cache`. - #[allow(dead_code)] - pub(super) nested_goals: &'a HashSet, + pub(super) nested_goals: &'a NestedGoals, } #[derive_where(Default; X: Cx)] pub struct GlobalCache { @@ -55,20 +45,21 @@ impl GlobalCache { input: X::Input, result: X::Result, - proof_tree: X::ProofTree, dep_node: X::DepNodeIndex, additional_depth: usize, encountered_overflow: bool, - nested_goals: &HashSet, + nested_goals: NestedGoals, ) { - let data = cx.mk_tracked(QueryData { result, proof_tree }, dep_node); + let result = cx.mk_tracked(result, dep_node); let entry = self.map.entry(input).or_default(); - entry.nested_goals.extend(nested_goals); if encountered_overflow { - entry.with_overflow.insert(additional_depth, data); + let with_overflow = WithOverflow { nested_goals, result }; + let prev = entry.with_overflow.insert(additional_depth, with_overflow); + assert!(prev.is_none()); } else { - entry.success = Some(Success { data, additional_depth }); + let prev = entry.success.replace(Success { additional_depth, nested_goals, result }); + assert!(prev.is_none()); } } @@ -80,36 +71,37 @@ impl GlobalCache { &'a self, cx: X, input: X::Input, - stack: &IndexVec>, available_depth: AvailableDepth, + mut candidate_is_applicable: impl FnMut(&NestedGoals) -> bool, ) -> Option> { let entry = self.map.get(&input)?; - if stack.iter().any(|e| entry.nested_goals.contains(&e.input)) { - return None; - } - - if let Some(ref success) = entry.success { - if available_depth.cache_entry_is_applicable(success.additional_depth) { - let QueryData { result, proof_tree } = cx.get_tracked(&success.data); + if let Some(Success { additional_depth, ref nested_goals, ref result }) = entry.success { + if available_depth.cache_entry_is_applicable(additional_depth) + && candidate_is_applicable(nested_goals) + { return Some(CacheData { - result, - proof_tree, - additional_depth: success.additional_depth, + result: cx.get_tracked(&result), + additional_depth, encountered_overflow: false, - nested_goals: &entry.nested_goals, + nested_goals, }); } } - entry.with_overflow.get(&available_depth.0).map(|e| { - let QueryData { result, proof_tree } = cx.get_tracked(e); - CacheData { - result, - proof_tree, - additional_depth: available_depth.0, - encountered_overflow: true, - nested_goals: &entry.nested_goals, + let additional_depth = available_depth.0; + if let Some(WithOverflow { nested_goals, result }) = + entry.with_overflow.get(&additional_depth) + { + if candidate_is_applicable(nested_goals) { + return Some(CacheData { + result: cx.get_tracked(result), + additional_depth, + encountered_overflow: true, + nested_goals, + }); } - }) + } + + None } } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 4abf99b1ded8..d47c9e725f35 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -1,19 +1,32 @@ +/// The search graph is responsible for caching and cycle detection in the trait +/// solver. Making sure that caching doesn't result in soundness bugs or unstable +/// query results is very challenging and makes this one of the most-involved +/// self-contained components of the compiler. +/// +/// We added fuzzing support to test its correctness. The fuzzers used to verify +/// the current implementation can be found in https://github.com/lcnr/search_graph_fuzz. +/// +/// This is just a quick overview of the general design, please check out the relevant +/// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for +/// more details. Caching is split between a global cache and the per-cycle `provisional_cache`. +/// The global cache has to be completely unobservable, while the per-cycle cache may impact +/// behavior as long as the resulting behavior is still correct. +use std::cmp::Ordering; +use std::collections::BTreeSet; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; -use std::mem; use derive_where::derive_where; use rustc_index::{Idx, IndexVec}; use tracing::debug; -use crate::data_structures::{HashMap, HashSet}; +use crate::data_structures::HashMap; use crate::solve::SolverMode; mod global_cache; use global_cache::CacheData; pub use global_cache::GlobalCache; -mod validate; /// The search graph does not simply use `Interner` directly /// to enable its fuzzing without having to stub the rest of @@ -22,7 +35,6 @@ mod validate; /// about `Input` and `Result` as they are implementation details /// of the search graph. pub trait Cx: Copy { - type ProofTree: Debug + Copy; type Input: Debug + Eq + Hash + Copy; type Result: Debug + Eq + Hash + Copy; @@ -43,30 +55,41 @@ pub trait Cx: Copy { ) -> R; } -pub trait ProofTreeBuilder { - fn try_apply_proof_tree(&mut self, proof_tree: X::ProofTree) -> bool; - fn on_provisional_cache_hit(&mut self); - fn on_cycle_in_stack(&mut self); - fn finalize_canonical_goal_evaluation(&mut self, cx: X) -> X::ProofTree; -} - pub trait Delegate { type Cx: Cx; - const FIXPOINT_STEP_LIMIT: usize; - type ProofTreeBuilder: ProofTreeBuilder; + /// Whether to use the provisional cache. Set to `false` by a fuzzer when + /// validating the search graph. + const ENABLE_PROVISIONAL_CACHE: bool; + type ValidationScope; + /// Returning `Some` disables the global cache for the current goal. + /// + /// The `ValidationScope` is used when fuzzing the search graph to track + /// for which goals the global cache has been disabled. This is necessary + /// as we may otherwise ignore the global cache entry for some goal `G` + /// only to later use it, failing to detect a cycle goal and potentially + /// changing the result. + fn enter_validation_scope( + cx: Self::Cx, + input: ::Input, + ) -> Option; + const FIXPOINT_STEP_LIMIT: usize; + + type ProofTreeBuilder; + fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool; + + const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize; fn recursion_limit(cx: Self::Cx) -> usize; fn initial_provisional_result( cx: Self::Cx, - kind: CycleKind, + kind: PathKind, input: ::Input, ) -> ::Result; - fn reached_fixpoint( + fn is_initial_provisional_result( cx: Self::Cx, - kind: UsageKind, + kind: PathKind, input: ::Input, - provisional_result: Option<::Result>, result: ::Result, ) -> bool; fn on_stack_overflow( @@ -79,6 +102,13 @@ pub trait Delegate { input: ::Input, ) -> ::Result; + fn is_ambiguous_result(result: ::Result) -> bool; + fn propagate_ambiguity( + cx: Self::Cx, + for_input: ::Input, + from_result: ::Result, + ) -> ::Result; + fn step_is_coinductive(cx: Self::Cx, input: ::Input) -> bool; } @@ -86,19 +116,20 @@ pub trait Delegate { /// result. In the case we return an initial provisional result depending /// on the kind of cycle. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CycleKind { +pub enum PathKind { Coinductive, Inductive, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UsageKind { - Single(CycleKind), + Single(PathKind), Mixed, } impl UsageKind { fn merge(self, other: Self) -> Self { match (self, other) { + (UsageKind::Mixed, _) | (_, UsageKind::Mixed) => UsageKind::Mixed, (UsageKind::Single(lhs), UsageKind::Single(rhs)) => { if lhs == rhs { UsageKind::Single(lhs) @@ -106,11 +137,11 @@ impl UsageKind { UsageKind::Mixed } } - (UsageKind::Mixed, UsageKind::Mixed) - | (UsageKind::Mixed, UsageKind::Single(_)) - | (UsageKind::Single(_), UsageKind::Mixed) => UsageKind::Mixed, } } + fn and_merge(&mut self, other: Self) { + *self = self.merge(other); + } } #[derive(Debug, Clone, Copy)] @@ -132,7 +163,7 @@ impl AvailableDepth { } Some(if last.encountered_overflow { - AvailableDepth(last.available_depth.0 / 2) + AvailableDepth(last.available_depth.0 / D::DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW) } else { AvailableDepth(last.available_depth.0 - 1) }) @@ -148,97 +179,181 @@ impl AvailableDepth { } } +/// All cycle heads a given goal depends on, ordered by their stack depth. +/// +/// We therefore pop the cycle heads from highest to lowest. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +struct CycleHeads { + heads: BTreeSet, +} + +impl CycleHeads { + fn is_empty(&self) -> bool { + self.heads.is_empty() + } + + fn highest_cycle_head(&self) -> StackDepth { + *self.heads.last().unwrap() + } + + fn opt_highest_cycle_head(&self) -> Option { + self.heads.last().copied() + } + + fn opt_lowest_cycle_head(&self) -> Option { + self.heads.first().copied() + } + + fn remove_highest_cycle_head(&mut self) { + let last = self.heads.pop_last(); + debug_assert_ne!(last, None); + } + + fn insert(&mut self, head: StackDepth) { + self.heads.insert(head); + } + + fn merge(&mut self, heads: &CycleHeads) { + for &head in heads.heads.iter() { + self.insert(head); + } + } + + /// Update the cycle heads of a goal at depth `this` given the cycle heads + /// of a nested goal. This merges the heads after filtering the parent goal + /// itself. + fn extend_from_child(&mut self, this: StackDepth, child: &CycleHeads) { + for &head in child.heads.iter() { + match head.cmp(&this) { + Ordering::Less => {} + Ordering::Equal => continue, + Ordering::Greater => unreachable!(), + } + + self.insert(head); + } + } +} + +/// The nested goals of each stack entry and the path from the +/// stack entry to that nested goal. +/// +/// We only start tracking nested goals once we've either encountered +/// overflow or a solver cycle. This is a performance optimization to +/// avoid tracking nested goals on the happy path. +/// +/// We use nested goals for two reasons: +/// - when rebasing provisional cache entries +/// - when checking whether we have to ignore a global cache entry as reevaluating +/// it would encounter a cycle or use a provisional cache entry. +/// +/// We need to disable the global cache if using it would hide a cycle, as +/// cycles can impact behavior. The cycle ABA may have different final +/// results from a the cycle BAB depending on the cycle root. +#[derive_where(Debug, Default; X: Cx)] +struct NestedGoals { + nested_goals: HashMap, +} +impl NestedGoals { + fn is_empty(&self) -> bool { + self.nested_goals.is_empty() + } + + fn insert(&mut self, input: X::Input, path_from_entry: UsageKind) { + self.nested_goals.entry(input).or_insert(path_from_entry).and_merge(path_from_entry); + } + + fn merge(&mut self, nested_goals: &NestedGoals) { + #[allow(rustc::potential_query_instability)] + for (input, path_from_entry) in nested_goals.iter() { + self.insert(input, path_from_entry); + } + } + + /// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal + /// to the parent goal. + /// + /// If the path from this goal to the nested goal is inductive, the paths from this goal + /// to all nested goals of that nested goal are also inductive. Otherwise the paths are + /// the same as for the child. + fn extend_from_child(&mut self, step_kind: PathKind, nested_goals: &NestedGoals) { + #[allow(rustc::potential_query_instability)] + for (input, path_from_entry) in nested_goals.iter() { + let path_from_entry = match step_kind { + PathKind::Coinductive => path_from_entry, + PathKind::Inductive => UsageKind::Single(PathKind::Inductive), + }; + self.insert(input, path_from_entry); + } + } + + #[rustc_lint_query_instability] + #[allow(rustc::potential_query_instability)] + fn iter(&self) -> impl Iterator + '_ { + self.nested_goals.iter().map(|(i, p)| (*i, *p)) + } + + fn get(&self, input: X::Input) -> Option { + self.nested_goals.get(&input).copied() + } + + fn contains(&self, input: X::Input) -> bool { + self.nested_goals.contains_key(&input) + } +} + rustc_index::newtype_index! { #[orderable] #[gate_rustc_only] pub struct StackDepth {} } +/// Stack entries of the evaluation stack. Its fields tend to be lazily +/// when popping a child goal or completely immutable. #[derive_where(Debug; X: Cx)] struct StackEntry { input: X::Input, + /// The available depth of a given goal, immutable. available_depth: AvailableDepth, /// The maximum depth reached by this stack entry, only up-to date /// for the top of the stack and lazily updated for the rest. reached_depth: StackDepth, - /// Whether this entry is a non-root cycle participant. - /// - /// We must not move the result of non-root cycle participants to the - /// global cache. We store the highest stack depth of a head of a cycle - /// this goal is involved in. This necessary to soundly cache its - /// provisional result. - non_root_cycle_participant: Option, + /// All cycle heads this goal depends on. Lazily updated and only + /// up-to date for the top of the stack. + heads: CycleHeads, + /// Whether evaluating this goal encountered overflow. Lazily updated. encountered_overflow: bool, + /// Whether this goal has been used as the root of a cycle. This gets + /// eagerly updated when encountering a cycle. has_been_used: Option, - /// We put only the root goal of a coinductive cycle into the global cache. - /// - /// If we were to use that result when later trying to prove another cycle - /// participant, we can end up with unstable query results. - /// - /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for - /// an example of where this is needed. - /// - /// There can be multiple roots on the same stack, so we need to track - /// cycle participants per root: - /// ```plain - /// A :- B - /// B :- A, C - /// C :- D - /// D :- C - /// ``` - nested_goals: HashSet, + /// The nested goals of this goal, see the doc comment of the type. + nested_goals: NestedGoals, + /// Starts out as `None` and gets set when rerunning this /// goal in case we encounter a cycle. provisional_result: Option, } -/// The provisional result for a goal which is not on the stack. -#[derive(Debug)] -struct DetachedEntry { - /// The head of the smallest non-trivial cycle involving this entry. - /// - /// Given the following rules, when proving `A` the head for - /// the provisional entry of `C` would be `B`. - /// ```plain - /// A :- B - /// B :- C - /// C :- A + B + C - /// ``` - head: StackDepth, - result: X::Result, -} - -/// Stores the stack depth of a currently evaluated goal *and* already -/// computed results for goals which depend on other goals still on the stack. -/// -/// The provisional result may depend on whether the stack above it is inductive -/// or coinductive. Because of this, we store separate provisional results for -/// each case. If an provisional entry is not applicable, it may be the case -/// that we already have provisional result while computing a goal. In this case -/// we prefer the provisional result to potentially avoid fixpoint iterations. -/// See tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs for an example. -/// -/// The provisional cache can theoretically result in changes to the observable behavior, -/// see tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs. -#[derive_where(Default; X: Cx)] +/// A provisional result of an already computed goals which depends on other +/// goals still on the stack. +#[derive_where(Debug; X: Cx)] struct ProvisionalCacheEntry { - stack_depth: Option, - with_inductive_stack: Option>, - with_coinductive_stack: Option>, -} - -impl ProvisionalCacheEntry { - fn is_empty(&self) -> bool { - self.stack_depth.is_none() - && self.with_inductive_stack.is_none() - && self.with_coinductive_stack.is_none() - } + /// Whether evaluating the goal encountered overflow. This is used to + /// disable the cache entry except if the last goal on the stack is + /// already involved in this cycle. + encountered_overflow: bool, + /// All cycle heads this cache entry depends on. + heads: CycleHeads, + /// The path from the highest cycle head to this goal. + path_from_head: PathKind, + nested_goals: NestedGoals, + result: X::Result, } pub struct SearchGraph, X: Cx = ::Cx> { @@ -247,7 +362,11 @@ pub struct SearchGraph, X: Cx = ::Cx> { /// /// An element is *deeper* in the stack if its index is *lower*. stack: IndexVec>, - provisional_cache: HashMap>, + /// The provisional cache contains entries for already computed goals which + /// still depend on goals higher-up in the stack. We don't move them to the + /// global cache and track them locally instead. A provisional cache entry + /// is only valid until the result of one of its cycle heads changes. + provisional_cache: HashMap>>, _marker: PhantomData, } @@ -266,77 +385,66 @@ impl, X: Cx> SearchGraph { self.mode } - fn update_parent_goal(&mut self, reached_depth: StackDepth, encountered_overflow: bool) { - if let Some(parent) = self.stack.raw.last_mut() { + /// Lazily update the stack entry for the parent goal. + /// This behavior is shared between actually evaluating goals + /// and using existing global cache entries to make sure they + /// have the same impact on the remaining evaluation. + fn update_parent_goal( + cx: X, + stack: &mut IndexVec>, + reached_depth: StackDepth, + heads: &CycleHeads, + encountered_overflow: bool, + nested_goals: &NestedGoals, + ) { + if let Some(parent_index) = stack.last_index() { + let parent = &mut stack[parent_index]; parent.reached_depth = parent.reached_depth.max(reached_depth); parent.encountered_overflow |= encountered_overflow; + + parent.heads.extend_from_child(parent_index, heads); + let step_kind = Self::step_kind(cx, parent.input); + parent.nested_goals.extend_from_child(step_kind, nested_goals); + // Once we've got goals which encountered overflow or a cycle, + // we track all goals whose behavior may depend depend on these + // goals as this change may cause them to now depend on additional + // goals, resulting in new cycles. See the dev-guide for examples. + if !nested_goals.is_empty() { + parent.nested_goals.insert(parent.input, UsageKind::Single(PathKind::Coinductive)) + } } } pub fn is_empty(&self) -> bool { - self.stack.is_empty() + if self.stack.is_empty() { + debug_assert!(self.provisional_cache.is_empty()); + true + } else { + false + } } - fn stack_coinductive_from( + /// The number of goals currently in the search graph. This should only be + /// used for debugging purposes. + pub fn debug_current_depth(&self) -> usize { + self.stack.len() + } + + fn step_kind(cx: X, input: X::Input) -> PathKind { + if D::step_is_coinductive(cx, input) { PathKind::Coinductive } else { PathKind::Inductive } + } + + /// Whether the path from `head` to the current stack entry is inductive or coinductive. + fn stack_path_kind( cx: X, stack: &IndexVec>, head: StackDepth, - ) -> bool { - stack.raw[head.index()..].iter().all(|entry| D::step_is_coinductive(cx, entry.input)) - } - - // When encountering a solver cycle, the result of the current goal - // depends on goals lower on the stack. - // - // We have to therefore be careful when caching goals. Only the final result - // of the cycle root, i.e. the lowest goal on the stack involved in this cycle, - // is moved to the global cache while all others are stored in a provisional cache. - // - // We update both the head of this cycle to rerun its evaluation until - // we reach a fixpoint and all other cycle participants to make sure that - // their result does not get moved to the global cache. - fn tag_cycle_participants( - stack: &mut IndexVec>, - usage_kind: Option, - head: StackDepth, - ) { - if let Some(usage_kind) = usage_kind { - stack[head].has_been_used = - Some(stack[head].has_been_used.map_or(usage_kind, |prev| prev.merge(usage_kind))); + ) -> PathKind { + if stack.raw[head.index()..].iter().all(|entry| D::step_is_coinductive(cx, entry.input)) { + PathKind::Coinductive + } else { + PathKind::Inductive } - debug_assert!(stack[head].has_been_used.is_some()); - - // The current root of these cycles. Note that this may not be the final - // root in case a later goal depends on a goal higher up the stack. - let mut current_root = head; - while let Some(parent) = stack[current_root].non_root_cycle_participant { - current_root = parent; - debug_assert!(stack[current_root].has_been_used.is_some()); - } - - let (stack, cycle_participants) = stack.raw.split_at_mut(head.index() + 1); - let current_cycle_root = &mut stack[current_root.as_usize()]; - for entry in cycle_participants { - entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head)); - current_cycle_root.nested_goals.insert(entry.input); - current_cycle_root.nested_goals.extend(mem::take(&mut entry.nested_goals)); - } - } - - fn clear_dependent_provisional_results( - provisional_cache: &mut HashMap>, - head: StackDepth, - ) { - #[allow(rustc::potential_query_instability)] - provisional_cache.retain(|_, entry| { - if entry.with_coinductive_stack.as_ref().is_some_and(|p| p.head == head) { - entry.with_coinductive_stack.take(); - } - if entry.with_inductive_stack.as_ref().is_some_and(|p| p.head == head) { - entry.with_inductive_stack.take(); - } - !entry.is_empty() - }); } /// Probably the most involved method of the whole solver. @@ -348,90 +456,66 @@ impl, X: Cx> SearchGraph { cx: X, input: X::Input, inspect: &mut D::ProofTreeBuilder, - mut prove_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, + mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, ) -> X::Result { - self.check_invariants(); - // Check for overflow. let Some(available_depth) = AvailableDepth::allowed_depth_for_nested::(cx, &self.stack) else { - if let Some(last) = self.stack.raw.last_mut() { - last.encountered_overflow = true; - } - - debug!("encountered stack overflow"); - return D::on_stack_overflow(cx, inspect, input); + return self.handle_overflow(cx, input, inspect); }; - if let Some(result) = self.lookup_global_cache(cx, input, available_depth, inspect) { + // We check the provisional cache before checking the global cache. This simplifies + // the implementation as we can avoid worrying about cases where both the global and + // provisional cache may apply, e.g. consider the following example + // + // - xxBA overflow + // - A + // - BA cycle + // - CB :x: + if let Some(result) = self.lookup_provisional_cache(cx, input) { return result; } - // Check whether the goal is in the provisional cache. - // The provisional result may rely on the path to its cycle roots, - // so we have to check the path of the current goal matches that of - // the cache entry. - let cache_entry = self.provisional_cache.entry(input).or_default(); - if let Some(entry) = cache_entry - .with_coinductive_stack - .as_ref() - .filter(|p| Self::stack_coinductive_from(cx, &self.stack, p.head)) - .or_else(|| { - cache_entry - .with_inductive_stack - .as_ref() - .filter(|p| !Self::stack_coinductive_from(cx, &self.stack, p.head)) - }) - { - debug!("provisional cache hit"); - // We have a nested goal which is already in the provisional cache, use - // its result. We do not provide any usage kind as that should have been - // already set correctly while computing the cache entry. - inspect.on_provisional_cache_hit(); - Self::tag_cycle_participants(&mut self.stack, None, entry.head); - return entry.result; - } else if let Some(stack_depth) = cache_entry.stack_depth { - debug!("encountered cycle with depth {stack_depth:?}"); - // We have a nested goal which directly relies on a goal deeper in the stack. - // - // We start by tagging all cycle participants, as that's necessary for caching. - // - // Finally we can return either the provisional response or the initial response - // in case we're in the first fixpoint iteration for this goal. - inspect.on_cycle_in_stack(); - - let is_coinductive_cycle = Self::stack_coinductive_from(cx, &self.stack, stack_depth); - let cycle_kind = - if is_coinductive_cycle { CycleKind::Coinductive } else { CycleKind::Inductive }; - Self::tag_cycle_participants( - &mut self.stack, - Some(UsageKind::Single(cycle_kind)), - stack_depth, - ); - - // Return the provisional result or, if we're in the first iteration, - // start with no constraints. - return if let Some(result) = self.stack[stack_depth].provisional_result { - result - } else { - D::initial_provisional_result(cx, cycle_kind, input) - }; + // Lookup the global cache unless we're building proof trees or are currently + // fuzzing. + let validate_cache = if !D::inspect_is_noop(inspect) { + None + } else if let Some(scope) = D::enter_validation_scope(cx, input) { + // When validating the global cache we need to track the goals for which the + // global cache has been disabled as it may otherwise change the result for + // cyclic goals. We don't care about goals which are not on the current stack + // so it's fine to drop their scope eagerly. + self.lookup_global_cache_untracked(cx, input, available_depth) + .inspect(|expected| debug!(?expected, "validate cache entry")) + .map(|r| (scope, r)) + } else if let Some(result) = self.lookup_global_cache(cx, input, available_depth) { + return result; } else { - // No entry, we push this goal on the stack and try to prove it. - let depth = self.stack.next_index(); - let entry = StackEntry { - input, - available_depth, - reached_depth: depth, - non_root_cycle_participant: None, - encountered_overflow: false, - has_been_used: None, - nested_goals: Default::default(), - provisional_result: None, - }; - assert_eq!(self.stack.push(entry), depth); - cache_entry.stack_depth = Some(depth); + None }; + // Detect cycles on the stack. We do this after the global cache lookup to + // avoid iterating over the stack in case a goal has already been computed. + // This may not have an actual performance impact and we could reorder them + // as it may reduce the number of `nested_goals` we need to track. + if let Some(result) = self.check_cycle_on_stack(cx, input) { + debug_assert!(validate_cache.is_none(), "global cache and cycle on stack"); + return result; + } + + // Unfortunate, it looks like we actually have to compute this goalrar. + let depth = self.stack.next_index(); + let entry = StackEntry { + input, + available_depth, + reached_depth: depth, + heads: Default::default(), + encountered_overflow: false, + has_been_used: None, + nested_goals: Default::default(), + provisional_result: None, + }; + assert_eq!(self.stack.push(entry), depth); + // This is for global caching, so we properly track query dependencies. // Everything that affects the `result` should be performed within this // `with_anon_task` closure. If computing this goal depends on something @@ -439,65 +523,320 @@ impl, X: Cx> SearchGraph { // must not be added to the global cache. Notably, this is the case for // trait solver cycles participants. let ((final_entry, result), dep_node) = cx.with_cached_task(|| { - for _ in 0..D::FIXPOINT_STEP_LIMIT { - match self.fixpoint_step_in_task(cx, input, inspect, &mut prove_goal) { - StepResult::Done(final_entry, result) => return (final_entry, result), - StepResult::HasChanged => debug!("fixpoint changed provisional results"), + self.evaluate_goal_in_task(cx, input, inspect, &mut evaluate_goal) + }); + + // We've finished computing the goal and have popped it from the stack, + // lazily update its parent goal. + Self::update_parent_goal( + cx, + &mut self.stack, + final_entry.reached_depth, + &final_entry.heads, + final_entry.encountered_overflow, + &final_entry.nested_goals, + ); + + // We're now done with this goal. We only add the root of cycles to the global cache. + // In case this goal is involved in a larger cycle add it to the provisional cache. + if final_entry.heads.is_empty() { + if let Some((_scope, expected)) = validate_cache { + // Do not try to move a goal into the cache again if we're testing + // the global cache. + assert_eq!(result, expected, "input={input:?}"); + } else if D::inspect_is_noop(inspect) { + self.insert_global_cache(cx, input, final_entry, result, dep_node) + } + } else if D::ENABLE_PROVISIONAL_CACHE { + debug_assert!(validate_cache.is_none()); + let entry = self.provisional_cache.entry(input).or_default(); + let StackEntry { heads, nested_goals, encountered_overflow, .. } = final_entry; + let path_from_head = Self::stack_path_kind(cx, &self.stack, heads.highest_cycle_head()); + entry.push(ProvisionalCacheEntry { + encountered_overflow, + heads, + path_from_head, + nested_goals, + result, + }); + } else { + debug_assert!(validate_cache.is_none()); + } + + result + } + + fn handle_overflow( + &mut self, + cx: X, + input: X::Input, + inspect: &mut D::ProofTreeBuilder, + ) -> X::Result { + if let Some(last) = self.stack.raw.last_mut() { + last.encountered_overflow = true; + // If computing a goal `B` depends on another goal `A` and + // `A` has a nested goal which overflows, then computing `B` + // at the same depth, but with `A` already on the stack, + // would encounter a solver cycle instead, potentially + // changing the result. + // + // We must therefore not use the global cache entry for `B` in that case. + // See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs + last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Coinductive)); + } + + debug!("encountered stack overflow"); + D::on_stack_overflow(cx, inspect, input) + } + + /// When reevaluating a goal with a changed provisional result, all provisional cache entry + /// which depend on this goal get invalidated. + fn clear_dependent_provisional_results(&mut self) { + let head = self.stack.next_index(); + #[allow(rustc::potential_query_instability)] + self.provisional_cache.retain(|_, entries| { + entries.retain(|entry| entry.heads.highest_cycle_head() != head); + !entries.is_empty() + }); + } + + /// A necessary optimization to handle complex solver cycles. A provisional cache entry + /// relies on a set of cycle heads and the path towards these heads. When popping a cycle + /// head from the stack after we've finished computing it, we can't be sure that the + /// provisional cache entry is still applicable. We need to keep the cache entries to + /// prevent hangs. + /// + /// What we therefore do is check whether the cycle kind of all cycles the goal of a + /// provisional cache entry is involved in would stay the same when computing the + /// goal without its cycle head on the stack. For more details, see the relevant + /// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html). + /// + /// This can be thought of rotating the sub-tree of this provisional result and changing + /// its entry point while making sure that all paths through this sub-tree stay the same. + /// + /// + /// In case the popped cycle head failed to reach a fixpoint anything which depends on + /// its provisional result is invalid. Actually discarding provisional cache entries in + /// this case would cause hangs, so we instead change the result of dependant provisional + /// cache entries to also be ambiguous. This causes some undesirable ambiguity for nested + /// goals whose result doesn't actually depend on this cycle head, but that's acceptable + /// to me. + fn rebase_provisional_cache_entries( + &mut self, + cx: X, + stack_entry: &StackEntry, + mut mutate_result: impl FnMut(X::Input, X::Result) -> X::Result, + ) { + let head = self.stack.next_index(); + #[allow(rustc::potential_query_instability)] + self.provisional_cache.retain(|&input, entries| { + entries.retain_mut(|entry| { + let ProvisionalCacheEntry { + encountered_overflow: _, + heads, + path_from_head, + nested_goals, + result, + } = entry; + if heads.highest_cycle_head() != head { + return true; + } + + // We don't try rebasing if the path from the current head + // to the cache entry is not coinductive or if the path from + // the cache entry to the current head is not coinductive. + // + // Both of these constraints could be weakened, but by only + // accepting coinductive paths we don't have to worry about + // changing the cycle kind of the remaining cycles. We can + // extend this in the future once there's a known issue + // caused by it. + if *path_from_head != PathKind::Coinductive + || nested_goals.get(stack_entry.input).unwrap() + != UsageKind::Single(PathKind::Coinductive) + { + return false; + } + + // Merge the cycle heads of the provisional cache entry and the + // popped head. If the popped cycle head was a root, discard all + // provisional cache entries which depend on it. + heads.remove_highest_cycle_head(); + heads.merge(&stack_entry.heads); + let Some(head) = heads.opt_highest_cycle_head() else { + return false; + }; + + // As we've made sure that the path from the new highest cycle + // head to the uses of the popped cycle head are fully coinductive, + // we can be sure that the paths to all nested goals of the popped + // cycle head remain the same. We can simply merge them. + nested_goals.merge(&stack_entry.nested_goals); + // We now care about the path from the next highest cycle head to the + // provisional cache entry. + *path_from_head = Self::stack_path_kind(cx, &self.stack, head); + // Mutate the result of the provisional cache entry in case we did + // not reach a fixpoint. + *result = mutate_result(input, *result); + true + }); + !entries.is_empty() + }); + } + + fn lookup_provisional_cache(&mut self, cx: X, input: X::Input) -> Option { + if !D::ENABLE_PROVISIONAL_CACHE { + return None; + } + + let entries = self.provisional_cache.get(&input)?; + for &ProvisionalCacheEntry { + encountered_overflow, + ref heads, + path_from_head, + ref nested_goals, + result, + } in entries + { + let head = heads.highest_cycle_head(); + if encountered_overflow { + // This check is overly strict and very subtle. We need to make sure that if + // a global cache entry depends on some goal without adding it to its + // `nested_goals`, that goal must never have an applicable provisional + // cache entry to avoid incorrectly applying the cache entry. + // + // As we'd have to otherwise track literally all nested goals, we only + // apply provisional cache entries which encountered overflow once the + // current goal is already part of the same cycle. This check could be + // improved but seems to be good enough for now. + let last = self.stack.raw.last().unwrap(); + if !last.heads.opt_lowest_cycle_head().is_some_and(|lowest| lowest <= head) { + continue; } } - debug!("canonical cycle overflow"); - let current_entry = self.stack.pop().unwrap(); - debug_assert!(current_entry.has_been_used.is_none()); - let result = D::on_fixpoint_overflow(cx, input); - (current_entry, result) - }); + // A provisional cache entry is only valid if the current path from its + // highest cycle head to the goal is the same. + if path_from_head == Self::stack_path_kind(cx, &self.stack, head) { + // While we don't have to track the full depth of the provisional cache entry, + // we do have to increment the required depth by one as we'd have already failed + // with overflow otherwise + let next_index = self.stack.next_index(); + let last = &mut self.stack.raw.last_mut().unwrap(); + let path_from_entry = Self::step_kind(cx, last.input); + last.nested_goals.insert(input, UsageKind::Single(path_from_entry)); - let proof_tree = inspect.finalize_canonical_goal_evaluation(cx); - - self.update_parent_goal(final_entry.reached_depth, final_entry.encountered_overflow); - - // We're now done with this goal. In case this goal is involved in a larger cycle - // do not remove it from the provisional cache and update its provisional result. - // We only add the root of cycles to the global cache. - if let Some(head) = final_entry.non_root_cycle_participant { - let coinductive_stack = Self::stack_coinductive_from(cx, &self.stack, head); - - let entry = self.provisional_cache.get_mut(&input).unwrap(); - entry.stack_depth = None; - if coinductive_stack { - entry.with_coinductive_stack = Some(DetachedEntry { head, result }); - } else { - entry.with_inductive_stack = Some(DetachedEntry { head, result }); - } - } else { - // When encountering a cycle, both inductive and coinductive, we only - // move the root into the global cache. We also store all other cycle - // participants involved. - // - // We must not use the global cache entry of a root goal if a cycle - // participant is on the stack. This is necessary to prevent unstable - // results. See the comment of `StackEntry::nested_goals` for - // more details. - self.provisional_cache.remove(&input); - let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len(); - cx.with_global_cache(self.mode, |cache| { - cache.insert( + Self::update_parent_goal( cx, - input, - result, - proof_tree, - dep_node, - additional_depth, - final_entry.encountered_overflow, - &final_entry.nested_goals, - ) - }) + &mut self.stack, + next_index, + heads, + false, + nested_goals, + ); + debug_assert!(self.stack[head].has_been_used.is_some()); + debug!(?head, ?path_from_head, "provisional cache hit"); + return Some(result); + } } - self.check_invariants(); + None + } - result + /// Even if there is a global cache entry for a given goal, we need to make sure + /// evaluating this entry would not have ended up depending on either a goal + /// already on the stack or a provisional cache entry. + fn candidate_is_applicable( + cx: X, + stack: &IndexVec>, + provisional_cache: &HashMap>>, + nested_goals: &NestedGoals, + ) -> bool { + // If the global cache entry didn't depend on any nested goals, it always + // applies. + if nested_goals.is_empty() { + return true; + } + + // If a nested goal of the global cache entry is on the stack, we would + // definitely encounter a cycle. + if stack.iter().any(|e| nested_goals.contains(e.input)) { + debug!("cache entry not applicable due to stack"); + return false; + } + + // The global cache entry is also invalid if there's a provisional cache entry + // would apply for any of its nested goals. + #[allow(rustc::potential_query_instability)] + for (input, path_from_global_entry) in nested_goals.iter() { + let Some(entries) = provisional_cache.get(&input) else { + continue; + }; + + debug!(?input, ?path_from_global_entry, ?entries, "candidate_is_applicable"); + // A provisional cache entry is applicable if the path to + // its highest cycle head is equal to the expected path. + for &ProvisionalCacheEntry { + encountered_overflow, + ref heads, + path_from_head, + nested_goals: _, + result: _, + } in entries.iter() + { + // We don't have to worry about provisional cache entries which encountered + // overflow, see the relevant comment in `lookup_provisional_cache`. + if encountered_overflow { + continue; + } + + // A provisional cache entry only applies if the path from its highest head + // matches the path when encountering the goal. + let head = heads.highest_cycle_head(); + let full_path = match Self::stack_path_kind(cx, stack, head) { + PathKind::Coinductive => path_from_global_entry, + PathKind::Inductive => UsageKind::Single(PathKind::Inductive), + }; + + match (full_path, path_from_head) { + (UsageKind::Mixed, _) + | (UsageKind::Single(PathKind::Coinductive), PathKind::Coinductive) + | (UsageKind::Single(PathKind::Inductive), PathKind::Inductive) => { + debug!( + ?full_path, + ?path_from_head, + "cache entry not applicable due to matching paths" + ); + return false; + } + _ => debug!(?full_path, ?path_from_head, "paths don't match"), + } + } + } + + true + } + + /// Used when fuzzing the global cache. Accesses the global cache without + /// updating the state of the search graph. + fn lookup_global_cache_untracked( + &self, + cx: X, + input: X::Input, + available_depth: AvailableDepth, + ) -> Option { + cx.with_global_cache(self.mode, |cache| { + cache + .get(cx, input, available_depth, |nested_goals| { + Self::candidate_is_applicable( + cx, + &self.stack, + &self.provisional_cache, + nested_goals, + ) + }) + .map(|c| c.result) + }) } /// Try to fetch a previously computed result from the global cache, @@ -508,97 +847,206 @@ impl, X: Cx> SearchGraph { cx: X, input: X::Input, available_depth: AvailableDepth, - inspect: &mut D::ProofTreeBuilder, ) -> Option { cx.with_global_cache(self.mode, |cache| { - let CacheData { - result, - proof_tree, - additional_depth, - encountered_overflow, - nested_goals: _, // FIXME: consider nested goals here. - } = cache.get(cx, input, &self.stack, available_depth)?; - - // If we're building a proof tree and the current cache entry does not - // contain a proof tree, we do not use the entry but instead recompute - // the goal. We simply overwrite the existing entry once we're done, - // caching the proof tree. - if !inspect.try_apply_proof_tree(proof_tree) { - return None; - } + let CacheData { result, additional_depth, encountered_overflow, nested_goals } = cache + .get(cx, input, available_depth, |nested_goals| { + Self::candidate_is_applicable( + cx, + &self.stack, + &self.provisional_cache, + nested_goals, + ) + })?; // Update the reached depth of the current goal to make sure // its state is the same regardless of whether we've used the // global cache or not. let reached_depth = self.stack.next_index().plus(additional_depth); - self.update_parent_goal(reached_depth, encountered_overflow); + // We don't move cycle participants to the global cache, so the + // cycle heads are always empty. + let heads = Default::default(); + Self::update_parent_goal( + cx, + &mut self.stack, + reached_depth, + &heads, + encountered_overflow, + nested_goals, + ); - debug!("global cache hit"); + debug!(?additional_depth, "global cache hit"); Some(result) }) } -} -enum StepResult { - Done(StackEntry, X::Result), - HasChanged, -} + fn check_cycle_on_stack(&mut self, cx: X, input: X::Input) -> Option { + let (head, _stack_entry) = self.stack.iter_enumerated().find(|(_, e)| e.input == input)?; + debug!("encountered cycle with depth {head:?}"); + // We have a nested goal which directly relies on a goal deeper in the stack. + // + // We start by tagging all cycle participants, as that's necessary for caching. + // + // Finally we can return either the provisional response or the initial response + // in case we're in the first fixpoint iteration for this goal. + let path_kind = Self::stack_path_kind(cx, &self.stack, head); + let usage_kind = UsageKind::Single(path_kind); + self.stack[head].has_been_used = + Some(self.stack[head].has_been_used.map_or(usage_kind, |prev| prev.merge(usage_kind))); + + // Subtle: when encountering a cyclic goal, we still first checked for overflow, + // so we have to update the reached depth. + let next_index = self.stack.next_index(); + let last_index = self.stack.last_index().unwrap(); + let last = &mut self.stack[last_index]; + last.reached_depth = last.reached_depth.max(next_index); + + let path_from_entry = Self::step_kind(cx, last.input); + last.nested_goals.insert(input, UsageKind::Single(path_from_entry)); + last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Coinductive)); + if last_index != head { + last.heads.insert(head); + } + + // Return the provisional result or, if we're in the first iteration, + // start with no constraints. + if let Some(result) = self.stack[head].provisional_result { + Some(result) + } else { + Some(D::initial_provisional_result(cx, path_kind, input)) + } + } + + /// Whether we've reached a fixpoint when evaluating a cycle head. + fn reached_fixpoint( + &mut self, + cx: X, + stack_entry: &StackEntry, + usage_kind: UsageKind, + result: X::Result, + ) -> bool { + if let Some(prev) = stack_entry.provisional_result { + prev == result + } else if let UsageKind::Single(kind) = usage_kind { + D::is_initial_provisional_result(cx, kind, stack_entry.input, result) + } else { + false + } + } -impl, X: Cx> SearchGraph { /// When we encounter a coinductive cycle, we have to fetch the /// result of that cycle while we are still computing it. Because /// of this we continuously recompute the cycle until the result /// of the previous iteration is equal to the final result, at which /// point we are done. - fn fixpoint_step_in_task( + fn evaluate_goal_in_task( &mut self, cx: X, input: X::Input, inspect: &mut D::ProofTreeBuilder, - prove_goal: &mut F, - ) -> StepResult - where - F: FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, - { - let result = prove_goal(self, inspect); - let stack_entry = self.stack.pop().unwrap(); - debug_assert_eq!(stack_entry.input, input); + mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result, + ) -> (StackEntry, X::Result) { + let mut i = 0; + loop { + let result = evaluate_goal(self, inspect); + let stack_entry = self.stack.pop().unwrap(); + debug_assert_eq!(stack_entry.input, input); - // If the current goal is not the root of a cycle, we are done. - let Some(usage_kind) = stack_entry.has_been_used else { - return StepResult::Done(stack_entry, result); - }; + // If the current goal is not the root of a cycle, we are done. + // + // There are no provisional cache entries which depend on this goal. + let Some(usage_kind) = stack_entry.has_been_used else { + return (stack_entry, result); + }; - // If it is a cycle head, we have to keep trying to prove it until - // we reach a fixpoint. We need to do so for all cycle heads, - // not only for the root. - // - // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs - // for an example. + // If it is a cycle head, we have to keep trying to prove it until + // we reach a fixpoint. We need to do so for all cycle heads, + // not only for the root. + // + // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs + // for an example. + // + // Check whether we reached a fixpoint, either because the final result + // is equal to the provisional result of the previous iteration, or because + // this was only the root of either coinductive or inductive cycles, and the + // final result is equal to the initial response for that case. + if self.reached_fixpoint(cx, &stack_entry, usage_kind, result) { + self.rebase_provisional_cache_entries(cx, &stack_entry, |_, result| result); + return (stack_entry, result); + } - // Start by clearing all provisional cache entries which depend on this - // the current goal. - Self::clear_dependent_provisional_results( - &mut self.provisional_cache, - self.stack.next_index(), - ); + // If computing this goal results in ambiguity with no constraints, + // we do not rerun it. It's incredibly difficult to get a different + // response in the next iteration in this case. These changes would + // likely either be caused by incompleteness or can change the maybe + // cause from ambiguity to overflow. Returning ambiguity always + // preserves soundness and completeness even if the goal is be known + // to succeed or fail. + // + // This prevents exponential blowup affecting multiple major crates. + // As we only get to this branch if we haven't yet reached a fixpoint, + // we also taint all provisional cache entries which depend on the + // current goal. + if D::is_ambiguous_result(result) { + self.rebase_provisional_cache_entries(cx, &stack_entry, |input, _| { + D::propagate_ambiguity(cx, input, result) + }); + return (stack_entry, result); + }; - // Check whether we reached a fixpoint, either because the final result - // is equal to the provisional result of the previous iteration, or because - // this was only the root of either coinductive or inductive cycles, and the - // final result is equal to the initial response for that case. - // - // If we did not reach a fixpoint, update the provisional result and reevaluate. - if D::reached_fixpoint(cx, usage_kind, input, stack_entry.provisional_result, result) { - StepResult::Done(stack_entry, result) - } else { - let depth = self.stack.push(StackEntry { + // If we've reached the fixpoint step limit, we bail with overflow and taint all + // provisional cache entries which depend on the current goal. + i += 1; + if i >= D::FIXPOINT_STEP_LIMIT { + debug!("canonical cycle overflow"); + let result = D::on_fixpoint_overflow(cx, input); + self.rebase_provisional_cache_entries(cx, &stack_entry, |input, _| { + D::on_fixpoint_overflow(cx, input) + }); + return (stack_entry, result); + } + + // Clear all provisional cache entries which depend on a previous provisional + // result of this goal and rerun. + self.clear_dependent_provisional_results(); + + debug!(?result, "fixpoint changed provisional results"); + self.stack.push(StackEntry { has_been_used: None, provisional_result: Some(result), ..stack_entry }); - debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth)); - StepResult::HasChanged } } + + /// When encountering a cycle, both inductive and coinductive, we only + /// move the root into the global cache. We also store all other cycle + /// participants involved. + /// + /// We must not use the global cache entry of a root goal if a cycle + /// participant is on the stack. This is necessary to prevent unstable + /// results. See the comment of `StackEntry::nested_goals` for + /// more details. + fn insert_global_cache( + &mut self, + cx: X, + input: X::Input, + final_entry: StackEntry, + result: X::Result, + dep_node: X::DepNodeIndex, + ) { + let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len(); + debug!(?final_entry, ?result, "insert global cache"); + cx.with_global_cache(self.mode, |cache| { + cache.insert( + cx, + input, + result, + dep_node, + additional_depth, + final_entry.encountered_overflow, + final_entry.nested_goals, + ) + }) + } } diff --git a/compiler/rustc_type_ir/src/search_graph/validate.rs b/compiler/rustc_type_ir/src/search_graph/validate.rs deleted file mode 100644 index 1ae806834ba7..000000000000 --- a/compiler/rustc_type_ir/src/search_graph/validate.rs +++ /dev/null @@ -1,75 +0,0 @@ -use super::*; - -impl, X: Cx> SearchGraph { - #[allow(rustc::potential_query_instability)] - pub(super) fn check_invariants(&self) { - if !cfg!(debug_assertions) { - return; - } - - let SearchGraph { mode: _, stack, provisional_cache, _marker } = self; - if stack.is_empty() { - assert!(provisional_cache.is_empty()); - } - - for (depth, entry) in stack.iter_enumerated() { - let StackEntry { - input, - available_depth: _, - reached_depth: _, - non_root_cycle_participant, - encountered_overflow: _, - has_been_used, - ref nested_goals, - provisional_result, - } = *entry; - let cache_entry = provisional_cache.get(&entry.input).unwrap(); - assert_eq!(cache_entry.stack_depth, Some(depth)); - if let Some(head) = non_root_cycle_participant { - assert!(head < depth); - assert!(nested_goals.is_empty()); - assert_ne!(stack[head].has_been_used, None); - - let mut current_root = head; - while let Some(parent) = stack[current_root].non_root_cycle_participant { - current_root = parent; - } - assert!(stack[current_root].nested_goals.contains(&input)); - } - - if !nested_goals.is_empty() { - assert!(provisional_result.is_some() || has_been_used.is_some()); - for entry in stack.iter().take(depth.as_usize()) { - assert_eq!(nested_goals.get(&entry.input), None); - } - } - } - - for (&input, entry) in &self.provisional_cache { - let ProvisionalCacheEntry { stack_depth, with_coinductive_stack, with_inductive_stack } = - entry; - assert!( - stack_depth.is_some() - || with_coinductive_stack.is_some() - || with_inductive_stack.is_some() - ); - - if let &Some(stack_depth) = stack_depth { - assert_eq!(stack[stack_depth].input, input); - } - - let check_detached = |detached_entry: &DetachedEntry| { - let DetachedEntry { head, result: _ } = *detached_entry; - assert_ne!(stack[head].has_been_used, None); - }; - - if let Some(with_coinductive_stack) = with_coinductive_stack { - check_detached(with_coinductive_stack); - } - - if let Some(with_inductive_stack) = with_inductive_stack { - check_detached(with_inductive_stack); - } - } - } -} diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs index 47d5e0dace71..099c66f6bdc8 100644 --- a/compiler/rustc_type_ir/src/solve/inspect.rs +++ b/compiler/rustc_type_ir/src/solve/inspect.rs @@ -69,9 +69,7 @@ pub struct CanonicalGoalEvaluation { #[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] pub enum CanonicalGoalEvaluationKind { Overflow, - CycleInStack, - ProvisionalCacheHit, - Evaluation { final_revision: I::CanonicalGoalEvaluationStepRef }, + Evaluation { final_revision: CanonicalGoalEvaluationStep }, } #[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)] diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 444fd01f0128..00fc6ba1c5c8 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -340,11 +340,3 @@ impl MaybeCause { } } } - -#[derive_where(PartialEq, Eq, Debug; I: Interner)] -pub struct CacheData { - pub result: QueryResult, - pub proof_tree: Option, - pub additional_depth: usize, - pub encountered_overflow: bool, -} diff --git a/config.example.toml b/config.example.toml index 1a7bdc4737fb..1b7de662e84a 100644 --- a/config.example.toml +++ b/config.example.toml @@ -87,6 +87,9 @@ # library provided by LLVM. #static-libstdcpp = false +# Enable LLVM to use zstd for compression. +#libzstd = false + # Whether to use Ninja to build LLVM. This runs much faster than make. #ninja = true diff --git a/library/Cargo.lock b/library/Cargo.lock index 223b61456c26..b36399d880e5 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -58,9 +58,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.114" +version = "0.1.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb58b199190fcfe0846f55a3b545cd6b07a34bdd5930a476ff856f3ebcc5558a" +checksum = "92afe7344b64cccf3662ca26d5d1c0828ab826f04206b97d856e3625e390e4b5" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 479eb0a2ba74..bdf16257c7cd 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -10,10 +10,7 @@ edition = "2021" [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.114", features = ['rustc-dep-of-std'] } - -[target.'cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))'.dependencies] -compiler_builtins = { version = "0.1.114", features = ["no-f16-f128"] } +compiler_builtins = { version = "0.1.118", features = ['rustc-dep-of-std'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index cc5f33c36854..88701370c105 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -1433,6 +1433,20 @@ pub struct Iter<'a, T: 'a> { iter: slice::Iter<'a, T>, } +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] +impl Default for Iter<'_, T> { + /// Creates an empty `binary_heap::Iter`. + /// + /// ``` + /// # use std::collections::binary_heap; + /// let iter: binary_heap::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { iter: Default::default() } + } +} + #[stable(feature = "collection_debug", since = "1.17.0")] impl fmt::Debug for Iter<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index d84654e36d77..f6f773cc42a4 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -2016,6 +2016,20 @@ impl Default for Range<'_, K, V> { } } +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] +impl Default for RangeMut<'_, K, V> { + /// Creates an empty `btree_map::RangeMut`. + /// + /// ``` + /// # use std::collections::btree_map; + /// let iter: btree_map::RangeMut<'_, u8, u8> = Default::default(); + /// assert_eq!(iter.count(), 0); + /// ``` + fn default() -> Self { + RangeMut { inner: Default::default(), _marker: PhantomData } + } +} + #[stable(feature = "map_values_mut", since = "1.10.0")] impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { type Item = &'a mut V; @@ -2050,6 +2064,20 @@ impl ExactSizeIterator for ValuesMut<'_, K, V> { #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ValuesMut<'_, K, V> {} +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] +impl Default for ValuesMut<'_, K, V> { + /// Creates an empty `btree_map::ValuesMut`. + /// + /// ``` + /// # use std::collections::btree_map; + /// let iter: btree_map::ValuesMut<'_, u8, u8> = Default::default(); + /// assert_eq!(iter.count(), 0); + /// ``` + fn default() -> Self { + ValuesMut { inner: Default::default() } + } +} + #[stable(feature = "map_into_keys_values", since = "1.54.0")] impl Iterator for IntoKeys { type Item = K; diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs index 2d283dac9a97..7be3de16b2da 100644 --- a/library/alloc/src/collections/vec_deque/into_iter.rs +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -121,6 +121,7 @@ impl Iterator for IntoIter { { match self.try_fold(init, |b, item| Ok::(f(b, item))) { Ok(b) => b, + #[cfg(bootstrap)] Err(e) => match e {}, } } @@ -242,6 +243,7 @@ impl DoubleEndedIterator for IntoIter { { match self.try_rfold(init, |b, item| Ok::(f(b, item))) { Ok(b) => b, + #[cfg(bootstrap)] Err(e) => match e {}, } } diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index 5a5e7f70854d..67b5b91c4d4b 100644 --- a/library/alloc/src/collections/vec_deque/iter.rs +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -28,6 +28,20 @@ impl fmt::Debug for Iter<'_, T> { } } +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] +impl Default for Iter<'_, T> { + /// Creates an empty `vec_deque::Iter`. + /// + /// ``` + /// # use std::collections::vec_deque; + /// let iter: vec_deque::Iter<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + Iter { i1: Default::default(), i2: Default::default() } + } +} + // FIXME(#26925) Remove in favor of `#[derive(Clone)]` #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Iter<'_, T> { diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs index 5061931afb7b..2726e3e42529 100644 --- a/library/alloc/src/collections/vec_deque/iter_mut.rs +++ b/library/alloc/src/collections/vec_deque/iter_mut.rs @@ -28,6 +28,20 @@ impl fmt::Debug for IterMut<'_, T> { } } +#[stable(feature = "default_iters_sequel", since = "CURRENT_RUSTC_VERSION")] +impl Default for IterMut<'_, T> { + /// Creates an empty `vec_deque::IterMut`. + /// + /// ``` + /// # use std::collections::vec_deque; + /// let iter: vec_deque::IterMut<'_, u8> = Default::default(); + /// assert_eq!(iter.len(), 0); + /// ``` + fn default() -> Self { + IterMut { i1: Default::default(), i2: Default::default() } + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Iterator for IterMut<'a, T> { type Item = &'a mut T; diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 5b84df9ecef3..9c8fa7ceff4e 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -1,7 +1,7 @@ #![unstable(feature = "raw_vec_internals", reason = "unstable const warnings", issue = "none")] -use core::alloc::LayoutError; -use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties}; +use core::marker::PhantomData; +use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::ptr::{self, NonNull, Unique}; use core::{cmp, hint}; @@ -40,6 +40,13 @@ struct Cap(usize); impl Cap { const ZERO: Cap = unsafe { Cap(0) }; + + /// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. + /// + /// # Safety: cap must be <= `isize::MAX`. + unsafe fn new(cap: usize) -> Self { + if T::IS_ZST { Cap::ZERO } else { unsafe { Self(cap) } } + } } /// A low-level utility for more ergonomically allocating, reallocating, and deallocating @@ -66,7 +73,19 @@ impl Cap { /// `Box<[T]>`, since `capacity()` won't yield the length. #[allow(missing_debug_implementations)] pub(crate) struct RawVec { - ptr: Unique, + inner: RawVecInner, + _marker: PhantomData, +} + +/// Like a `RawVec`, but only generic over the allocator, not the type. +/// +/// As such, all the methods need the layout passed-in as a parameter. +/// +/// Having this separation reduces the amount of code we need to monomorphize, +/// as most operations don't need the actual type, just its layout. +#[allow(missing_debug_implementations)] +struct RawVecInner { + ptr: Unique, /// Never used for ZSTs; it's `capacity()`'s responsibility to return usize::MAX in that case. /// /// # Safety @@ -90,8 +109,9 @@ impl RawVec { /// `RawVec` with capacity `usize::MAX`. Useful for implementing /// delayed allocation. #[must_use] + #[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")] pub const fn new() -> Self { - Self::new_in(Global) + Self { inner: RawVecInner::new::(), _marker: PhantomData } } /// Creates a `RawVec` (on the system heap) with exactly the @@ -113,10 +133,7 @@ impl RawVec { #[must_use] #[inline] pub fn with_capacity(capacity: usize) -> Self { - match Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global) { - Ok(res) => res, - Err(err) => handle_error(err), - } + Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } } /// Like `with_capacity`, but guarantees the buffer is zeroed. @@ -124,29 +141,56 @@ impl RawVec { #[must_use] #[inline] pub fn with_capacity_zeroed(capacity: usize) -> Self { - Self::with_capacity_zeroed_in(capacity, Global) + Self { + inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), + _marker: PhantomData, + } + } +} + +impl RawVecInner { + #[must_use] + #[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")] + const fn new() -> Self { + Self::new_in(Global, core::mem::align_of::()) + } + + #[cfg(not(any(no_global_oom_handling, test)))] + #[must_use] + #[inline] + fn with_capacity(capacity: usize, elem_layout: Layout) -> Self { + match Self::try_allocate_in(capacity, AllocInit::Uninitialized, Global, elem_layout) { + Ok(res) => res, + Err(err) => handle_error(err), + } + } +} + +// Tiny Vecs are dumb. Skip to: +// - 8 if the element size is 1, because any heap allocators is likely +// to round up a request of less than 8 bytes to at least 8 bytes. +// - 4 if elements are moderate-sized (<= 1 KiB). +// - 1 otherwise, to avoid wasting too much space for very short Vecs. +const fn min_non_zero_cap(size: usize) -> usize { + if size == 1 { + 8 + } else if size <= 1024 { + 4 + } else { + 1 } } impl RawVec { - // Tiny Vecs are dumb. Skip to: - // - 8 if the element size is 1, because any heap allocators is likely - // to round up a request of less than 8 bytes to at least 8 bytes. - // - 4 if elements are moderate-sized (<= 1 KiB). - // - 1 otherwise, to avoid wasting too much space for very short Vecs. - pub(crate) const MIN_NON_ZERO_CAP: usize = if mem::size_of::() == 1 { - 8 - } else if mem::size_of::() <= 1024 { - 4 - } else { - 1 - }; + #[cfg(not(no_global_oom_handling))] + pub(crate) const MIN_NON_ZERO_CAP: usize = min_non_zero_cap(size_of::()); /// Like `new`, but parameterized over the choice of allocator for /// the returned `RawVec`. + #[inline] + #[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")] pub const fn new_in(alloc: A) -> Self { - // `cap: 0` means "unallocated". zero-sized types are ignored. - Self { ptr: Unique::dangling(), cap: Cap::ZERO, alloc } + Self { inner: RawVecInner::new_in(alloc, align_of::()), _marker: PhantomData } } /// Like `with_capacity`, but parameterized over the choice of @@ -154,9 +198,9 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { - match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc) { - Ok(res) => res, - Err(err) => handle_error(err), + Self { + inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), + _marker: PhantomData, } } @@ -164,7 +208,10 @@ impl RawVec { /// allocator for the returned `RawVec`. #[inline] pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { - Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc) + match RawVecInner::try_with_capacity_in(capacity, alloc, T::LAYOUT) { + Ok(inner) => Ok(Self { inner, _marker: PhantomData }), + Err(e) => Err(e), + } } /// Like `with_capacity_zeroed`, but parameterized over the choice @@ -172,9 +219,9 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { - match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc) { - Ok(res) => res, - Err(err) => handle_error(err), + Self { + inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), + _marker: PhantomData, } } @@ -200,45 +247,7 @@ impl RawVec { let me = ManuallyDrop::new(self); unsafe { let slice = ptr::slice_from_raw_parts_mut(me.ptr() as *mut MaybeUninit, len); - Box::from_raw_in(slice, ptr::read(&me.alloc)) - } - } - - fn try_allocate_in( - capacity: usize, - init: AllocInit, - alloc: A, - ) -> Result { - // Don't allocate here because `Drop` will not deallocate when `capacity` is 0. - - if T::IS_ZST || capacity == 0 { - Ok(Self::new_in(alloc)) - } else { - // We avoid `unwrap_or_else` here because it bloats the amount of - // LLVM IR generated. - let layout = match Layout::array::(capacity) { - Ok(layout) => layout, - Err(_) => return Err(CapacityOverflow.into()), - }; - - if let Err(err) = alloc_guard(layout.size()) { - return Err(err); - } - - let result = match init { - AllocInit::Uninitialized => alloc.allocate(layout), - #[cfg(not(no_global_oom_handling))] - AllocInit::Zeroed => alloc.allocate_zeroed(layout), - }; - let ptr = match result { - Ok(ptr) => ptr, - Err(_) => return Err(AllocError { layout, non_exhaustive: () }.into()), - }; - - // Allocators currently return a `NonNull<[u8]>` whose length - // matches the size requested. If that ever changes, the capacity - // here should change to `ptr.len() / mem::size_of::()`. - Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }) + Box::from_raw_in(slice, ptr::read(&me.inner.alloc)) } } @@ -254,8 +263,15 @@ impl RawVec { /// guaranteed. #[inline] pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { - let cap = if T::IS_ZST { Cap::ZERO } else { unsafe { Cap(capacity) } }; - Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } + // SAFETY: Precondition passed to the caller + unsafe { + let ptr = ptr.cast(); + let capacity = Cap::new::(capacity); + Self { + inner: RawVecInner::from_raw_parts_in(ptr, capacity, alloc), + _marker: PhantomData, + } + } } /// A convenience method for hoisting the non-null precondition out of [`RawVec::from_raw_parts_in`]. @@ -264,9 +280,13 @@ impl RawVec { /// /// See [`RawVec::from_raw_parts_in`]. #[inline] - pub(crate) unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { - let cap = if T::IS_ZST { Cap::ZERO } else { unsafe { Cap(capacity) } }; - Self { ptr: Unique::from(ptr), cap, alloc } + pub unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { + // SAFETY: Precondition passed to the caller + unsafe { + let ptr = ptr.cast(); + let capacity = Cap::new::(capacity); + Self { inner: RawVecInner::from_nonnull_in(ptr, capacity, alloc), _marker: PhantomData } + } } /// Gets a raw pointer to the start of the allocation. Note that this is @@ -274,43 +294,26 @@ impl RawVec { /// be careful. #[inline] pub fn ptr(&self) -> *mut T { - self.ptr.as_ptr() + self.inner.ptr() } #[inline] pub fn non_null(&self) -> NonNull { - NonNull::from(self.ptr) + self.inner.non_null() } /// Gets the capacity of the allocation. /// /// This will always be `usize::MAX` if `T` is zero-sized. - #[inline(always)] + #[inline] pub fn capacity(&self) -> usize { - if T::IS_ZST { usize::MAX } else { self.cap.0 } + self.inner.capacity(size_of::()) } /// Returns a shared reference to the allocator backing this `RawVec`. + #[inline] pub fn allocator(&self) -> &A { - &self.alloc - } - - fn current_memory(&self) -> Option<(NonNull, Layout)> { - if T::IS_ZST || self.cap.0 == 0 { - None - } else { - // We could use Layout::array here which ensures the absence of isize and usize overflows - // and could hypothetically handle differences between stride and size, but this memory - // has already been allocated so we know it can't overflow and currently Rust does not - // support such types. So we can do better by skipping some checks and avoid an unwrap. - const { assert!(mem::size_of::() % mem::align_of::() == 0) }; - unsafe { - let align = mem::align_of::(); - let size = mem::size_of::().unchecked_mul(self.cap.0); - let layout = Layout::from_size_align_unchecked(size, align); - Some((self.ptr.cast().into(), layout)) - } - } + self.inner.allocator() } /// Ensures that the buffer contains at least enough space to hold `len + @@ -335,24 +338,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn reserve(&mut self, len: usize, additional: usize) { - // Callers expect this function to be very cheap when there is already sufficient capacity. - // Therefore, we move all the resizing and error-handling logic from grow_amortized and - // handle_reserve behind a call, while making sure that this function is likely to be - // inlined as just a comparison and a call if the comparison fails. - #[cold] - fn do_reserve_and_handle( - slf: &mut RawVec, - len: usize, - additional: usize, - ) { - if let Err(err) = slf.grow_amortized(len, additional) { - handle_error(err); - } - } - - if self.needs_to_grow(len, additional) { - do_reserve_and_handle(self, len, additional); - } + self.inner.reserve(len, additional, T::LAYOUT) } /// A specialized version of `self.reserve(len, 1)` which requires the @@ -360,21 +346,12 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline(never)] pub fn grow_one(&mut self) { - if let Err(err) = self.grow_amortized(self.cap.0, 1) { - handle_error(err); - } + self.inner.grow_one(T::LAYOUT) } /// The same as `reserve`, but returns on errors instead of panicking or aborting. pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { - if self.needs_to_grow(len, additional) { - self.grow_amortized(len, additional)?; - } - unsafe { - // Inform the optimizer that the reservation has succeeded or wasn't needed - hint::assert_unchecked(!self.needs_to_grow(len, additional)); - } - Ok(()) + self.inner.try_reserve(len, additional, T::LAYOUT) } /// Ensures that the buffer contains at least enough space to hold `len + @@ -396,9 +373,7 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] pub fn reserve_exact(&mut self, len: usize, additional: usize) { - if let Err(err) = self.try_reserve_exact(len, additional) { - handle_error(err); - } + self.inner.reserve_exact(len, additional, T::LAYOUT) } /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. @@ -407,14 +382,7 @@ impl RawVec { len: usize, additional: usize, ) -> Result<(), TryReserveError> { - if self.needs_to_grow(len, additional) { - self.grow_exact(len, additional)?; - } - unsafe { - // Inform the optimizer that the reservation has succeeded or wasn't needed - hint::assert_unchecked(!self.needs_to_grow(len, additional)); - } - Ok(()) + self.inner.try_reserve_exact(len, additional, T::LAYOUT) } /// Shrinks the buffer down to the specified capacity. If the given amount @@ -430,22 +398,230 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] pub fn shrink_to_fit(&mut self, cap: usize) { - if let Err(err) = self.shrink(cap) { - handle_error(err); - } + self.inner.shrink_to_fit(cap, T::LAYOUT) } } -impl RawVec { - /// Returns if the buffer needs to grow to fulfill the needed extra capacity. - /// Mainly used to make inlining reserve-calls possible without inlining `grow`. - fn needs_to_grow(&self, len: usize, additional: usize) -> bool { - additional > self.capacity().wrapping_sub(len) +unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { + /// Frees the memory owned by the `RawVec` *without* trying to drop its contents. + fn drop(&mut self) { + // SAFETY: We are in a Drop impl, self.inner will not be used again. + unsafe { self.inner.deallocate(T::LAYOUT) } + } +} + +impl RawVecInner { + #[inline] + #[rustc_const_stable(feature = "raw_vec_internals_const", since = "1.81")] + const fn new_in(alloc: A, align: usize) -> Self { + let ptr = unsafe { core::mem::transmute(align) }; + // `cap: 0` means "unallocated". zero-sized types are ignored. + Self { ptr, cap: Cap::ZERO, alloc } } - /// # Safety: - /// - /// `cap` must not exceed `isize::MAX`. + #[cfg(not(no_global_oom_handling))] + #[inline] + fn with_capacity_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { + match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) { + Ok(this) => { + unsafe { + // Make it more obvious that a subsquent Vec::reserve(capacity) will not allocate. + hint::assert_unchecked(!this.needs_to_grow(0, capacity, elem_layout)); + } + this + } + Err(err) => handle_error(err), + } + } + + #[inline] + fn try_with_capacity_in( + capacity: usize, + alloc: A, + elem_layout: Layout, + ) -> Result { + Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn with_capacity_zeroed_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { + match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc, elem_layout) { + Ok(res) => res, + Err(err) => handle_error(err), + } + } + + fn try_allocate_in( + capacity: usize, + init: AllocInit, + alloc: A, + elem_layout: Layout, + ) -> Result { + // We avoid `unwrap_or_else` here because it bloats the amount of + // LLVM IR generated. + let layout = match layout_array(capacity, elem_layout) { + Ok(layout) => layout, + Err(_) => return Err(CapacityOverflow.into()), + }; + + // Don't allocate here because `Drop` will not deallocate when `capacity` is 0. + if layout.size() == 0 { + return Ok(Self::new_in(alloc, elem_layout.align())); + } + + if let Err(err) = alloc_guard(layout.size()) { + return Err(err); + } + + let result = match init { + AllocInit::Uninitialized => alloc.allocate(layout), + #[cfg(not(no_global_oom_handling))] + AllocInit::Zeroed => alloc.allocate_zeroed(layout), + }; + let ptr = match result { + Ok(ptr) => ptr, + Err(_) => return Err(AllocError { layout, non_exhaustive: () }.into()), + }; + + // Allocators currently return a `NonNull<[u8]>` whose length + // matches the size requested. If that ever changes, the capacity + // here should change to `ptr.len() / mem::size_of::()`. + Ok(Self { ptr: Unique::from(ptr.cast()), cap: unsafe { Cap(capacity) }, alloc }) + } + + #[inline] + unsafe fn from_raw_parts_in(ptr: *mut u8, cap: Cap, alloc: A) -> Self { + Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } + } + + #[inline] + unsafe fn from_nonnull_in(ptr: NonNull, cap: Cap, alloc: A) -> Self { + Self { ptr: Unique::from(ptr), cap, alloc } + } + + #[inline] + fn ptr(&self) -> *mut T { + self.non_null::().as_ptr() + } + + #[inline] + fn non_null(&self) -> NonNull { + self.ptr.cast().into() + } + + #[inline] + fn capacity(&self, elem_size: usize) -> usize { + if elem_size == 0 { usize::MAX } else { self.cap.0 } + } + + #[inline] + fn allocator(&self) -> &A { + &self.alloc + } + + #[inline] + fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { + if elem_layout.size() == 0 || self.cap.0 == 0 { + None + } else { + // We could use Layout::array here which ensures the absence of isize and usize overflows + // and could hypothetically handle differences between stride and size, but this memory + // has already been allocated so we know it can't overflow and currently Rust does not + // support such types. So we can do better by skipping some checks and avoid an unwrap. + unsafe { + let alloc_size = elem_layout.size().unchecked_mul(self.cap.0); + let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align()); + Some((self.ptr.into(), layout)) + } + } + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) { + // Callers expect this function to be very cheap when there is already sufficient capacity. + // Therefore, we move all the resizing and error-handling logic from grow_amortized and + // handle_reserve behind a call, while making sure that this function is likely to be + // inlined as just a comparison and a call if the comparison fails. + #[cold] + fn do_reserve_and_handle( + slf: &mut RawVecInner, + len: usize, + additional: usize, + elem_layout: Layout, + ) { + if let Err(err) = slf.grow_amortized(len, additional, elem_layout) { + handle_error(err); + } + } + + if self.needs_to_grow(len, additional, elem_layout) { + do_reserve_and_handle(self, len, additional, elem_layout); + } + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn grow_one(&mut self, elem_layout: Layout) { + if let Err(err) = self.grow_amortized(self.cap.0, 1, elem_layout) { + handle_error(err); + } + } + + fn try_reserve( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + if self.needs_to_grow(len, additional, elem_layout) { + self.grow_amortized(len, additional, elem_layout)?; + } + unsafe { + // Inform the optimizer that the reservation has succeeded or wasn't needed + hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout)); + } + Ok(()) + } + + #[cfg(not(no_global_oom_handling))] + fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) { + if let Err(err) = self.try_reserve_exact(len, additional, elem_layout) { + handle_error(err); + } + } + + fn try_reserve_exact( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + if self.needs_to_grow(len, additional, elem_layout) { + self.grow_exact(len, additional, elem_layout)?; + } + unsafe { + // Inform the optimizer that the reservation has succeeded or wasn't needed + hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout)); + } + Ok(()) + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) { + if let Err(err) = self.shrink(cap, elem_layout) { + handle_error(err); + } + } + + #[inline] + fn needs_to_grow(&self, len: usize, additional: usize, elem_layout: Layout) -> bool { + additional > self.capacity(elem_layout.size()).wrapping_sub(len) + } + + #[inline] unsafe fn set_ptr_and_cap(&mut self, ptr: NonNull<[u8]>, cap: usize) { // Allocators currently return a `NonNull<[u8]>` whose length matches // the size requested. If that ever changes, the capacity here should @@ -454,18 +630,16 @@ impl RawVec { self.cap = unsafe { Cap(cap) }; } - // This method is usually instantiated many times. So we want it to be as - // small as possible, to improve compile times. But we also want as much of - // its contents to be statically computable as possible, to make the - // generated code run faster. Therefore, this method is carefully written - // so that all of the code that depends on `T` is within it, while as much - // of the code that doesn't depend on `T` as possible is in functions that - // are non-generic over `T`. - fn grow_amortized(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { + fn grow_amortized( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { // This is ensured by the calling contexts. debug_assert!(additional > 0); - if T::IS_ZST { + if elem_layout.size() == 0 { // Since we return a capacity of `usize::MAX` when `elem_size` is // 0, getting to here necessarily means the `RawVec` is overfull. return Err(CapacityOverflow.into()); @@ -477,33 +651,34 @@ impl RawVec { // This guarantees exponential growth. The doubling cannot overflow // because `cap <= isize::MAX` and the type of `cap` is `usize`. let cap = cmp::max(self.cap.0 * 2, required_cap); - let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap); + let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); - let new_layout = Layout::array::(cap); + let new_layout = layout_array(cap, elem_layout)?; - // `finish_grow` is non-generic over `T`. - let ptr = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; + let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items + unsafe { self.set_ptr_and_cap(ptr, cap) }; Ok(()) } - // The constraints on this method are much the same as those on - // `grow_amortized`, but this method is usually instantiated less often so - // it's less critical. - fn grow_exact(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { - if T::IS_ZST { + fn grow_exact( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + if elem_layout.size() == 0 { // Since we return a capacity of `usize::MAX` when the type size is // 0, getting to here necessarily means the `RawVec` is overfull. return Err(CapacityOverflow.into()); } let cap = len.checked_add(additional).ok_or(CapacityOverflow)?; - let new_layout = Layout::array::(cap); + let new_layout = layout_array(cap, elem_layout)?; - // `finish_grow` is non-generic over `T`. - let ptr = finish_grow(new_layout, self.current_memory(), &mut self.alloc)?; - // SAFETY: `finish_grow` would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items + let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?; + // SAFETY: finish_grow would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items unsafe { self.set_ptr_and_cap(ptr, cap); } @@ -512,10 +687,10 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] - fn shrink(&mut self, cap: usize) -> Result<(), TryReserveError> { - assert!(cap <= self.capacity(), "Tried to shrink to a larger capacity"); + fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> { + assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity"); // SAFETY: Just checked this isn't trying to grow - unsafe { self.shrink_unchecked(cap) } + unsafe { self.shrink_unchecked(cap, elem_layout) } } /// `shrink`, but without the capacity check. @@ -529,23 +704,27 @@ impl RawVec { /// # Safety /// `cap <= self.capacity()` #[cfg(not(no_global_oom_handling))] - unsafe fn shrink_unchecked(&mut self, cap: usize) -> Result<(), TryReserveError> { - let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) }; - // See current_memory() why this assert is here - const { assert!(mem::size_of::() % mem::align_of::() == 0) }; + unsafe fn shrink_unchecked( + &mut self, + cap: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + let (ptr, layout) = + if let Some(mem) = self.current_memory(elem_layout) { mem } else { return Ok(()) }; // If shrinking to 0, deallocate the buffer. We don't reach this point // for the T::IS_ZST case since current_memory() will have returned // None. if cap == 0 { unsafe { self.alloc.deallocate(ptr, layout) }; - self.ptr = Unique::dangling(); + self.ptr = + unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) }; self.cap = Cap::ZERO; } else { let ptr = unsafe { - // `Layout::array` cannot overflow here because it would have + // Layout cannot overflow here because it would have // overflowed earlier when capacity was larger. - let new_size = mem::size_of::().unchecked_mul(cap); + let new_size = elem_layout.size().unchecked_mul(cap); let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); self.alloc .shrink(ptr, layout, new_layout) @@ -558,24 +737,32 @@ impl RawVec { } Ok(()) } + + /// # Safety + /// + /// This function deallocates the owned allocation, but does not update `ptr` or `cap` to + /// prevent double-free or use-after-free. Essentially, do not do anything with the caller + /// after this function returns. + /// Ideally this function would take `self` by move, but it cannot because it exists to be + /// called from a `Drop` impl. + unsafe fn deallocate(&mut self, elem_layout: Layout) { + if let Some((ptr, layout)) = self.current_memory(elem_layout) { + unsafe { + self.alloc.deallocate(ptr, layout); + } + } + } } -// This function is outside `RawVec` to minimize compile times. See the comment -// above `RawVec::grow_amortized` for details. (The `A` parameter isn't -// significant, because the number of different `A` types seen in practice is -// much smaller than the number of `T` types.) #[inline(never)] fn finish_grow( - new_layout: Result, + new_layout: Layout, current_memory: Option<(NonNull, Layout)>, alloc: &mut A, ) -> Result, TryReserveError> where A: Allocator, { - // Check for the error here to minimize the size of `RawVec::grow_*`. - let new_layout = new_layout.map_err(|_| CapacityOverflow)?; - alloc_guard(new_layout.size())?; let memory = if let Some((ptr, old_layout)) = current_memory { @@ -592,15 +779,6 @@ where memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }.into()) } -unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { - /// Frees the memory owned by the `RawVec` *without* trying to drop its contents. - fn drop(&mut self) { - if let Some((ptr, layout)) = self.current_memory() { - unsafe { self.alloc.deallocate(ptr, layout) } - } - } -} - // Central function for reserve error handling. #[cfg(not(no_global_oom_handling))] #[cold] @@ -627,3 +805,8 @@ fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { Ok(()) } } + +#[inline] +fn layout_array(cap: usize, elem_layout: Layout) -> Result { + elem_layout.repeat(cap).map(|(layout, _pad)| layout).map_err(|_| CapacityOverflow.into()) +} diff --git a/library/alloc/src/raw_vec/tests.rs b/library/alloc/src/raw_vec/tests.rs index 48c6e5f46f8d..d78ded104fb0 100644 --- a/library/alloc/src/raw_vec/tests.rs +++ b/library/alloc/src/raw_vec/tests.rs @@ -43,9 +43,9 @@ fn allocator_param() { let a = BoundedAlloc { fuel: Cell::new(500) }; let mut v: RawVec = RawVec::with_capacity_in(50, a); - assert_eq!(v.alloc.fuel.get(), 450); + assert_eq!(v.inner.alloc.fuel.get(), 450); v.reserve(50, 150); // (causes a realloc, thus using 50 + 150 = 200 units of fuel) - assert_eq!(v.alloc.fuel.get(), 250); + assert_eq!(v.inner.alloc.fuel.get(), 250); } #[test] @@ -86,7 +86,7 @@ struct ZST; fn zst_sanity(v: &RawVec) { assert_eq!(v.capacity(), usize::MAX); assert_eq!(v.ptr(), core::ptr::Unique::::dangling().as_ptr()); - assert_eq!(v.current_memory(), None); + assert_eq!(v.inner.current_memory(T::LAYOUT), None); } #[test] @@ -106,22 +106,11 @@ fn zst() { let v: RawVec = RawVec::with_capacity_in(100, Global); zst_sanity(&v); - let v: RawVec = RawVec::try_allocate_in(0, AllocInit::Uninitialized, Global).unwrap(); - zst_sanity(&v); - - let v: RawVec = RawVec::try_allocate_in(100, AllocInit::Uninitialized, Global).unwrap(); - zst_sanity(&v); - - let mut v: RawVec = - RawVec::try_allocate_in(usize::MAX, AllocInit::Uninitialized, Global).unwrap(); + let mut v: RawVec = RawVec::with_capacity_in(usize::MAX, Global); zst_sanity(&v); // Check all these operations work as expected with zero-sized elements. - assert!(!v.needs_to_grow(100, usize::MAX - 100)); - assert!(v.needs_to_grow(101, usize::MAX - 100)); - zst_sanity(&v); - v.reserve(100, usize::MAX - 100); //v.reserve(101, usize::MAX - 100); // panics, in `zst_reserve_panic` below zst_sanity(&v); @@ -138,12 +127,12 @@ fn zst() { assert_eq!(v.try_reserve_exact(101, usize::MAX - 100), cap_err); zst_sanity(&v); - assert_eq!(v.grow_amortized(100, usize::MAX - 100), cap_err); - assert_eq!(v.grow_amortized(101, usize::MAX - 100), cap_err); + assert_eq!(v.inner.grow_amortized(100, usize::MAX - 100, ZST::LAYOUT), cap_err); + assert_eq!(v.inner.grow_amortized(101, usize::MAX - 100, ZST::LAYOUT), cap_err); zst_sanity(&v); - assert_eq!(v.grow_exact(100, usize::MAX - 100), cap_err); - assert_eq!(v.grow_exact(101, usize::MAX - 100), cap_err); + assert_eq!(v.inner.grow_exact(100, usize::MAX - 100, ZST::LAYOUT), cap_err); + assert_eq!(v.inner.grow_exact(101, usize::MAX - 100, ZST::LAYOUT), cap_err); zst_sanity(&v); } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 7dcf344cdc5e..9d7048703269 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -178,15 +178,25 @@ impl [T] { /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) /// worst-case. /// - /// If `T: Ord` does not implement a total order the resulting order is unspecified. All - /// original elements will remain in the slice and any possible modifications via interior - /// mutability are observed in the input. Same is true if `T: Ord` panics. + /// If the implementation of [`Ord`] for `T` does not implement a [total order] the resulting + /// order of elements in the slice is unspecified. All original elements will remain in the + /// slice and any possible modifications via interior mutability are observed in the input. Same + /// is true if the implementation of [`Ord`] for `T` panics. /// /// When applicable, unstable sorting is preferred because it is generally faster than stable /// sorting and it doesn't allocate auxiliary memory. See /// [`sort_unstable`](slice::sort_unstable). The exception are partially sorted slices, which /// may be better served with `slice::sort`. /// + /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] require + /// additional precautions. For example, `f32::NAN != f32::NAN`, which doesn't fulfill the + /// reflexivity requirement of [`Ord`]. By using an alternative comparison function with + /// `slice::sort_by` such as [`f32::total_cmp`] or [`f64::total_cmp`] that defines a [total + /// order] users can sort slices containing floating-point values. Alternatively, if all values + /// in the slice are guaranteed to be in a subset for which [`PartialOrd::partial_cmp`] forms a + /// [total order], it's possible to sort the slice with `sort_by(|a, b| + /// a.partial_cmp(b).unwrap())`. + /// /// # Current implementation /// /// The current implementation is based on [driftsort] by Orson Peters and Lukas Bergdoll, which @@ -198,18 +208,21 @@ impl [T] { /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it /// clamps at `self.len() / 2`. /// - /// If `T: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5, 4, 1, -3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// /// v.sort(); - /// assert!(v == [-5, -3, 1, 2, 4]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// ``` /// /// [driftsort]: https://github.com/Voultapher/driftsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] @@ -221,30 +234,19 @@ impl [T] { stable_sort(self, T::lt); } - /// Sorts the slice with a comparator function, preserving initial order of equal elements. + /// Sorts the slice with a comparison function, preserving initial order of equal elements. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) /// worst-case. /// - /// The comparator function should define a total ordering for the elements in the slice. If the - /// ordering is not total, the order of the elements is unspecified. + /// If the comparison function `compare` does not implement a [total order] the resulting order + /// of elements in the slice is unspecified. All original elements will remain in the slice and + /// any possible modifications via interior mutability are observed in the input. Same is true + /// if `compare` panics. /// - /// If the comparator function does not implement a total order the resulting order is - /// unspecified. All original elements will remain in the slice and any possible modifications - /// via interior mutability are observed in the input. Same is true if the comparator function - /// panics. A total order (for all `a`, `b` and `c`): - /// - /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and - /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. - /// - /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use - /// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`. - /// - /// ``` - /// let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0]; - /// floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); - /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); - /// ``` + /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor + /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and + /// examples see the [`Ord`] documentation. /// /// # Current implementation /// @@ -257,21 +259,24 @@ impl [T] { /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it /// clamps at `self.len() / 2`. /// - /// If `T: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if `compare` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [5, 4, 1, 3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// v.sort_by(|a, b| a.cmp(b)); - /// assert!(v == [1, 2, 3, 4, 5]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// /// // reverse sorting /// v.sort_by(|a, b| b.cmp(a)); - /// assert!(v == [5, 4, 3, 2, 1]); + /// assert_eq!(v, [4, 2, 1, -3, -5]); /// ``` /// /// [driftsort]: https://github.com/Voultapher/driftsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] @@ -288,9 +293,10 @@ impl [T] { /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*)) /// worst-case, where the key function is *O*(*m*). /// - /// If `K: Ord` does not implement a total order the resulting order is unspecified. - /// All original elements will remain in the slice and any possible modifications via interior - /// mutability are observed in the input. Same is true if `K: Ord` panics. + /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting + /// order of elements in the slice is unspecified. All original elements will remain in the + /// slice and any possible modifications via interior mutability are observed in the input. Same + /// is true if the implementation of [`Ord`] for `K` panics. /// /// # Current implementation /// @@ -303,18 +309,21 @@ impl [T] { /// handled without allocation, medium sized slices allocate `self.len()` and beyond that it /// clamps at `self.len() / 2`. /// - /// If `K: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5i32, 4, 1, -3, 2]; + /// let mut v = [4i32, -5, 1, -3, 2]; /// /// v.sort_by_key(|k| k.abs()); - /// assert!(v == [1, 2, -3, 4, -5]); + /// assert_eq!(v, [1, 2, -3, 4, -5]); /// ``` /// /// [driftsort]: https://github.com/Voultapher/driftsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "slice_sort_by_key", since = "1.7.0")] @@ -336,9 +345,10 @@ impl [T] { /// storage to remember the results of key evaluation. The order of calls to the key function is /// unspecified and may change in future versions of the standard library. /// - /// If `K: Ord` does not implement a total order the resulting order is unspecified. - /// All original elements will remain in the slice and any possible modifications via interior - /// mutability are observed in the input. Same is true if `K: Ord` panics. + /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting + /// order of elements in the slice is unspecified. All original elements will remain in the + /// slice and any possible modifications via interior mutability are observed in the input. Same + /// is true if the implementation of [`Ord`] for `K` panics. /// /// For simple key functions (e.g., functions that are property accesses or basic operations), /// [`sort_by_key`](slice::sort_by_key) is likely to be faster. @@ -355,16 +365,22 @@ impl [T] { /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the /// length of the slice. /// + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. + /// /// # Examples /// /// ``` - /// let mut v = [-5i32, 4, 32, -3, 2]; + /// let mut v = [4i32, -5, 1, -3, 2, 10]; /// + /// // Strings are sorted by lexicographical order. /// v.sort_by_cached_key(|k| k.to_string()); - /// assert!(v == [-3, -5, 2, 32, 4]); + /// assert_eq!(v, [-3, -5, 1, 10, 2, 4]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[cfg(not(no_global_oom_handling))] #[rustc_allow_incoherent_impl] #[stable(feature = "slice_sort_by_cached_key", since = "1.34.0")] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 5c826b9993f8..61c713c9e81c 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -889,6 +889,7 @@ impl Guard<'_, T> { } impl Drop for Guard<'_, T> { + #[inline] fn drop(&mut self) { debug_assert!(self.initialized <= self.array_mut.len()); diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index 34a05ac38884..375358dddf5c 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -3,7 +3,7 @@ //! suggestions from rustc if you get anything slightly wrong in here, and overall //! helps with clarity as we're also referring to `char` intentionally in here. -use crate::fmt::{self, Write}; +use crate::fmt; use crate::mem::transmute; /// One of the 128 Unicode characters from U+0000 through U+007F, @@ -583,9 +583,10 @@ impl fmt::Display for AsciiChar { #[unstable(feature = "ascii_char", issue = "110998")] impl fmt::Debug for AsciiChar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[inline] - fn backslash(a: AsciiChar) -> ([AsciiChar; 4], u8) { - ([AsciiChar::ReverseSolidus, a, AsciiChar::Null, AsciiChar::Null], 2) + use AsciiChar::{Apostrophe, Null, ReverseSolidus as Backslash}; + + fn backslash(a: AsciiChar) -> ([AsciiChar; 6], usize) { + ([Apostrophe, Backslash, a, Apostrophe, Null, Null], 4) } let (buf, len) = match self { @@ -595,24 +596,17 @@ impl fmt::Debug for AsciiChar { AsciiChar::LineFeed => backslash(AsciiChar::SmallN), AsciiChar::ReverseSolidus => backslash(AsciiChar::ReverseSolidus), AsciiChar::Apostrophe => backslash(AsciiChar::Apostrophe), - _ => { - let byte = self.to_u8(); - if !byte.is_ascii_control() { - ([*self, AsciiChar::Null, AsciiChar::Null, AsciiChar::Null], 1) - } else { - const HEX_DIGITS: [AsciiChar; 16] = *b"0123456789abcdef".as_ascii().unwrap(); + _ if self.to_u8().is_ascii_control() => { + const HEX_DIGITS: [AsciiChar; 16] = *b"0123456789abcdef".as_ascii().unwrap(); - let hi = HEX_DIGITS[usize::from(byte >> 4)]; - let lo = HEX_DIGITS[usize::from(byte & 0xf)]; - ([AsciiChar::ReverseSolidus, AsciiChar::SmallX, hi, lo], 4) - } + let byte = self.to_u8(); + let hi = HEX_DIGITS[usize::from(byte >> 4)]; + let lo = HEX_DIGITS[usize::from(byte & 0xf)]; + ([Apostrophe, Backslash, AsciiChar::SmallX, hi, lo, Apostrophe], 6) } + _ => ([Apostrophe, *self, Apostrophe, Null, Null, Null], 3), }; - f.write_char('\'')?; - for byte in &buf[..len as usize] { - f.write_str(byte.as_str())?; - } - f.write_char('\'') + f.write_str(buf[..len].as_str()) } } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index e9eacbcd25a0..6f1e0cb74719 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1528,6 +1528,12 @@ extern "rust-intrinsic" { #[rustc_diagnostic_item = "intrinsics_unaligned_volatile_store"] pub fn unaligned_volatile_store(dst: *mut T, val: T); + /// Returns the square root of an `f16` + /// + /// The stabilized version of this intrinsic is + /// [`f16::sqrt`](../../std/primitive.f16.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf16(x: f16) -> f16; /// Returns the square root of an `f32` /// /// The stabilized version of this intrinsic is @@ -1540,6 +1546,12 @@ extern "rust-intrinsic" { /// [`f64::sqrt`](../../std/primitive.f64.html#method.sqrt) #[rustc_nounwind] pub fn sqrtf64(x: f64) -> f64; + /// Returns the square root of an `f128` + /// + /// The stabilized version of this intrinsic is + /// [`f128::sqrt`](../../std/primitive.f128.html#method.sqrt) + #[rustc_nounwind] + pub fn sqrtf128(x: f128) -> f128; /// Raises an `f16` to an integer power. /// @@ -1566,6 +1578,12 @@ extern "rust-intrinsic" { #[rustc_nounwind] pub fn powif128(a: f128, x: i32) -> f128; + /// Returns the sine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::sin`](../../std/primitive.f16.html#method.sin) + #[rustc_nounwind] + pub fn sinf16(x: f16) -> f16; /// Returns the sine of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1578,7 +1596,19 @@ extern "rust-intrinsic" { /// [`f64::sin`](../../std/primitive.f64.html#method.sin) #[rustc_nounwind] pub fn sinf64(x: f64) -> f64; + /// Returns the sine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::sin`](../../std/primitive.f128.html#method.sin) + #[rustc_nounwind] + pub fn sinf128(x: f128) -> f128; + /// Returns the cosine of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::cos`](../../std/primitive.f16.html#method.cos) + #[rustc_nounwind] + pub fn cosf16(x: f16) -> f16; /// Returns the cosine of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1591,7 +1621,19 @@ extern "rust-intrinsic" { /// [`f64::cos`](../../std/primitive.f64.html#method.cos) #[rustc_nounwind] pub fn cosf64(x: f64) -> f64; + /// Returns the cosine of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::cos`](../../std/primitive.f128.html#method.cos) + #[rustc_nounwind] + pub fn cosf128(x: f128) -> f128; + /// Raises an `f16` to an `f16` power. + /// + /// The stabilized version of this intrinsic is + /// [`f16::powf`](../../std/primitive.f16.html#method.powf) + #[rustc_nounwind] + pub fn powf16(a: f16, x: f16) -> f16; /// Raises an `f32` to an `f32` power. /// /// The stabilized version of this intrinsic is @@ -1604,7 +1646,19 @@ extern "rust-intrinsic" { /// [`f64::powf`](../../std/primitive.f64.html#method.powf) #[rustc_nounwind] pub fn powf64(a: f64, x: f64) -> f64; + /// Raises an `f128` to an `f128` power. + /// + /// The stabilized version of this intrinsic is + /// [`f128::powf`](../../std/primitive.f128.html#method.powf) + #[rustc_nounwind] + pub fn powf128(a: f128, x: f128) -> f128; + /// Returns the exponential of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp`](../../std/primitive.f16.html#method.exp) + #[rustc_nounwind] + pub fn expf16(x: f16) -> f16; /// Returns the exponential of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1617,7 +1671,19 @@ extern "rust-intrinsic" { /// [`f64::exp`](../../std/primitive.f64.html#method.exp) #[rustc_nounwind] pub fn expf64(x: f64) -> f64; + /// Returns the exponential of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp`](../../std/primitive.f128.html#method.exp) + #[rustc_nounwind] + pub fn expf128(x: f128) -> f128; + /// Returns 2 raised to the power of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::exp2`](../../std/primitive.f16.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f16(x: f16) -> f16; /// Returns 2 raised to the power of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1630,7 +1696,19 @@ extern "rust-intrinsic" { /// [`f64::exp2`](../../std/primitive.f64.html#method.exp2) #[rustc_nounwind] pub fn exp2f64(x: f64) -> f64; + /// Returns 2 raised to the power of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::exp2`](../../std/primitive.f128.html#method.exp2) + #[rustc_nounwind] + pub fn exp2f128(x: f128) -> f128; + /// Returns the natural logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ln`](../../std/primitive.f16.html#method.ln) + #[rustc_nounwind] + pub fn logf16(x: f16) -> f16; /// Returns the natural logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1643,7 +1721,19 @@ extern "rust-intrinsic" { /// [`f64::ln`](../../std/primitive.f64.html#method.ln) #[rustc_nounwind] pub fn logf64(x: f64) -> f64; + /// Returns the natural logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ln`](../../std/primitive.f128.html#method.ln) + #[rustc_nounwind] + pub fn logf128(x: f128) -> f128; + /// Returns the base 10 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log10`](../../std/primitive.f16.html#method.log10) + #[rustc_nounwind] + pub fn log10f16(x: f16) -> f16; /// Returns the base 10 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1656,7 +1746,19 @@ extern "rust-intrinsic" { /// [`f64::log10`](../../std/primitive.f64.html#method.log10) #[rustc_nounwind] pub fn log10f64(x: f64) -> f64; + /// Returns the base 10 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log10`](../../std/primitive.f128.html#method.log10) + #[rustc_nounwind] + pub fn log10f128(x: f128) -> f128; + /// Returns the base 2 logarithm of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::log2`](../../std/primitive.f16.html#method.log2) + #[rustc_nounwind] + pub fn log2f16(x: f16) -> f16; /// Returns the base 2 logarithm of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1669,7 +1771,19 @@ extern "rust-intrinsic" { /// [`f64::log2`](../../std/primitive.f64.html#method.log2) #[rustc_nounwind] pub fn log2f64(x: f64) -> f64; + /// Returns the base 2 logarithm of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::log2`](../../std/primitive.f128.html#method.log2) + #[rustc_nounwind] + pub fn log2f128(x: f128) -> f128; + /// Returns `a * b + c` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf16(a: f16, b: f16, c: f16) -> f16; /// Returns `a * b + c` for `f32` values. /// /// The stabilized version of this intrinsic is @@ -1682,7 +1796,19 @@ extern "rust-intrinsic" { /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) #[rustc_nounwind] pub fn fmaf64(a: f64, b: f64, c: f64) -> f64; + /// Returns `a * b + c` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) + #[rustc_nounwind] + pub fn fmaf128(a: f128, b: f128, c: f128) -> f128; + /// Returns the absolute value of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::abs`](../../std/primitive.f16.html#method.abs) + #[rustc_nounwind] + pub fn fabsf16(x: f16) -> f16; /// Returns the absolute value of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1695,7 +1821,25 @@ extern "rust-intrinsic" { /// [`f64::abs`](../../std/primitive.f64.html#method.abs) #[rustc_nounwind] pub fn fabsf64(x: f64) -> f64; + /// Returns the absolute value of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::abs`](../../std/primitive.f128.html#method.abs) + #[rustc_nounwind] + pub fn fabsf128(x: f128) -> f128; + /// Returns the minimum of two `f16` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f16::min`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn minnumf16(x: f16, y: f16) -> f16; /// Returns the minimum of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1720,6 +1864,31 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn minnumf64(x: f64, y: f64) -> f64; + /// Returns the minimum of two `f128` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f128::min`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn minnumf128(x: f128, y: f128) -> f128; + + /// Returns the maximum of two `f16` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f16::max`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn maxnumf16(x: f16, y: f16) -> f16; /// Returns the maximum of two `f32` values. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -1744,7 +1913,25 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] #[rustc_nounwind] pub fn maxnumf64(x: f64, y: f64) -> f64; + /// Returns the maximum of two `f128` values. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is + /// [`f128::max`] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + pub fn maxnumf128(x: f128, y: f128) -> f128; + /// Copies the sign from `y` to `x` for `f16` values. + /// + /// The stabilized version of this intrinsic is + /// [`f16::copysign`](../../std/primitive.f16.html#method.copysign) + #[rustc_nounwind] + pub fn copysignf16(x: f16, y: f16) -> f16; /// Copies the sign from `y` to `x` for `f32` values. /// /// The stabilized version of this intrinsic is @@ -1757,7 +1944,19 @@ extern "rust-intrinsic" { /// [`f64::copysign`](../../std/primitive.f64.html#method.copysign) #[rustc_nounwind] pub fn copysignf64(x: f64, y: f64) -> f64; + /// Copies the sign from `y` to `x` for `f128` values. + /// + /// The stabilized version of this intrinsic is + /// [`f128::copysign`](../../std/primitive.f128.html#method.copysign) + #[rustc_nounwind] + pub fn copysignf128(x: f128, y: f128) -> f128; + /// Returns the largest integer less than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::floor`](../../std/primitive.f16.html#method.floor) + #[rustc_nounwind] + pub fn floorf16(x: f16) -> f16; /// Returns the largest integer less than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1770,7 +1969,19 @@ extern "rust-intrinsic" { /// [`f64::floor`](../../std/primitive.f64.html#method.floor) #[rustc_nounwind] pub fn floorf64(x: f64) -> f64; + /// Returns the largest integer less than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::floor`](../../std/primitive.f128.html#method.floor) + #[rustc_nounwind] + pub fn floorf128(x: f128) -> f128; + /// Returns the smallest integer greater than or equal to an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::ceil`](../../std/primitive.f16.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf16(x: f16) -> f16; /// Returns the smallest integer greater than or equal to an `f32`. /// /// The stabilized version of this intrinsic is @@ -1783,7 +1994,19 @@ extern "rust-intrinsic" { /// [`f64::ceil`](../../std/primitive.f64.html#method.ceil) #[rustc_nounwind] pub fn ceilf64(x: f64) -> f64; + /// Returns the smallest integer greater than or equal to an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::ceil`](../../std/primitive.f128.html#method.ceil) + #[rustc_nounwind] + pub fn ceilf128(x: f128) -> f128; + /// Returns the integer part of an `f16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::trunc`](../../std/primitive.f16.html#method.trunc) + #[rustc_nounwind] + pub fn truncf16(x: f16) -> f16; /// Returns the integer part of an `f32`. /// /// The stabilized version of this intrinsic is @@ -1796,7 +2019,25 @@ extern "rust-intrinsic" { /// [`f64::trunc`](../../std/primitive.f64.html#method.trunc) #[rustc_nounwind] pub fn truncf64(x: f64) -> f64; + /// Returns the integer part of an `f128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::trunc`](../../std/primitive.f128.html#method.trunc) + #[rustc_nounwind] + pub fn truncf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf16` and `roundevenf16`. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round_ties_even`](../../std/primitive.f16.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, /// so this rounds half-way cases to the number with an even least significant digit. /// @@ -1821,7 +2062,25 @@ extern "rust-intrinsic" { /// [`f64::round_ties_even`](../../std/primitive.f64.html#method.round_ties_even) #[rustc_nounwind] pub fn rintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// May raise an inexact floating-point exception if the argument is not an integer. + /// However, Rust assumes floating-point exceptions cannot be observed, so these exceptions + /// cannot actually be utilized from Rust code. + /// In other words, this intrinsic is equivalent in behavior to `nearbyintf128` and `roundevenf128`. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round_ties_even`](../../std/primitive.f128.html#method.round_ties_even) + #[rustc_nounwind] + pub fn rintf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Changing the rounding mode is not possible in Rust, /// so this rounds half-way cases to the number with an even least significant digit. /// @@ -1834,7 +2093,19 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn nearbyintf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Changing the rounding mode is not possible in Rust, + /// so this rounds half-way cases to the number with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn nearbyintf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f16::round`](../../std/primitive.f16.html#method.round) + #[rustc_nounwind] + pub fn roundf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases away from zero. /// /// The stabilized version of this intrinsic is @@ -1847,7 +2118,19 @@ extern "rust-intrinsic" { /// [`f64::round`](../../std/primitive.f64.html#method.round) #[rustc_nounwind] pub fn roundf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases away from zero. + /// + /// The stabilized version of this intrinsic is + /// [`f128::round`](../../std/primitive.f128.html#method.round) + #[rustc_nounwind] + pub fn roundf128(x: f128) -> f128; + /// Returns the nearest integer to an `f16`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf16(x: f16) -> f16; /// Returns the nearest integer to an `f32`. Rounds half-way cases to the number /// with an even least significant digit. /// @@ -1860,6 +2143,12 @@ extern "rust-intrinsic" { /// This intrinsic does not have a stable counterpart. #[rustc_nounwind] pub fn roundevenf64(x: f64) -> f64; + /// Returns the nearest integer to an `f128`. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This intrinsic does not have a stable counterpart. + #[rustc_nounwind] + pub fn roundevenf128(x: f128) -> f128; /// Float addition that allows optimizations based on algebraic rules. /// May assume inputs are finite. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e74900ff7471..07daa32afa8a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -192,6 +192,7 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))] #![feature(abi_unadjusted)] #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] @@ -225,7 +226,6 @@ #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] #![feature(marker_trait_attr)] -#![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(multiple_supertrait_upcastable)] #![feature(must_not_suspend)] @@ -391,7 +391,7 @@ pub mod net; pub mod option; pub mod panic; pub mod panicking; -#[unstable(feature = "core_pattern_types", issue = "none")] +#[unstable(feature = "core_pattern_types", issue = "123646")] pub mod pat; pub mod pin; #[unstable(feature = "new_range_api", issue = "125687")] diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index ea2dcdce6e89..7a9ca4011be8 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -5,6 +5,7 @@ #![stable(feature = "rust1", since = "1.0.0")] +use crate::alloc::Layout; use crate::marker::DiscriminantKind; use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; @@ -1238,6 +1239,10 @@ pub trait SizedTypeProperties: Sized { #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] const IS_ZST: bool = size_of::() == 0; + + #[doc(hidden)] + #[unstable(feature = "sized_type_properties", issue = "none")] + const LAYOUT: Layout = Layout::new::(); } #[doc(hidden)] #[unstable(feature = "sized_type_properties", issue = "none")] diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 6a24748fd9e8..0c04f47fe7df 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -686,6 +686,182 @@ impl f128 { self * RADS_PER_DEG } + /// Returns the maximum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. + /// This also matches the behavior of libm’s fmax. + /// + /// ``` + /// #![feature(f128)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.max(y), y); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn max(self, other: f128) -> f128 { + intrinsics::maxnumf128(self, other) + } + + /// Returns the minimum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids minNum's problems with associativity. + /// This also matches the behavior of libm’s fmin. + /// + /// ``` + /// #![feature(f128)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.min(y), x); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn min(self, other: f128) -> f128 { + intrinsics::minnumf128(self, other) + } + + /// Returns the maximum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f128::max`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_minimum_maximum)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.maximum(y), y); + /// assert!(x.maximum(f128::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f128) for more info. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn maximum(self, other: f128) -> f128 { + if self > other { + self + } else if other > self { + other + } else if self == other { + if self.is_sign_positive() && other.is_sign_negative() { self } else { other } + } else { + self + other + } + } + + /// Returns the minimum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f128::min`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_minimum_maximum)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// let x = 1.0f128; + /// let y = 2.0f128; + /// + /// assert_eq!(x.minimum(y), x); + /// assert!(x.minimum(f128::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f128) for more info. + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn minimum(self, other: f128) -> f128 { + if self < other { + self + } else if other < self { + other + } else if self == other { + if self.is_sign_negative() && other.is_sign_positive() { self } else { other } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + self + other + } + } + + /// Calculates the middle point of `self` and `rhs`. + /// + /// This returns NaN when *either* argument is NaN or if a combination of + /// +inf and -inf is provided as arguments. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(num_midpoint)] + /// # // Using aarch64 because `reliable_f128_math` is needed + /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// + /// assert_eq!(1f128.midpoint(4.0), 2.5); + /// assert_eq!((-5.5f128).midpoint(8.0), 1.25); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f128", issue = "116909")] + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f128) -> f128 { + const LO: f128 = f128::MIN_POSITIVE * 2.; + const HI: f128 = f128::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs_private(); + let abs_b = b.abs_private(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else if abs_a < LO { + // Not safe to halve `a` (would underflow) + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve `b` (would underflow) + (a / 2.) + b + } else { + // Safe to halve `a` and `b` + (a / 2.) + (b / 2.) + } + } + /// Rounds toward zero and converts to any primitive integer type, /// assuming that the value is finite and fits in that type. /// diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 054897b3c96b..e5b1148e1921 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -720,6 +720,177 @@ impl f16 { self * RADS_PER_DEG } + /// Returns the maximum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. + /// This also matches the behavior of libm’s fmax. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.max(y), y); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn max(self, other: f16) -> f16 { + intrinsics::maxnumf16(self, other) + } + + /// Returns the minimum of the two numbers, ignoring NaN. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; + /// this function handles all NaNs the same way and avoids minNum's problems with associativity. + /// This also matches the behavior of libm’s fmin. + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.min(y), x); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn min(self, other: f16) -> f16 { + intrinsics::minnumf16(self, other) + } + + /// Returns the maximum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f16::max`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_minimum_maximum)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.maximum(y), y); + /// assert!(x.maximum(f16::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f16) for more info. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn maximum(self, other: f16) -> f16 { + if self > other { + self + } else if other > self { + other + } else if self == other { + if self.is_sign_positive() && other.is_sign_negative() { self } else { other } + } else { + self + other + } + } + + /// Returns the minimum of the two numbers, propagating NaN. + /// + /// This returns NaN when *either* argument is NaN, as opposed to + /// [`f16::min`] which only returns NaN when *both* arguments are NaN. + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_minimum_maximum)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// let x = 1.0f16; + /// let y = 2.0f16; + /// + /// assert_eq!(x.minimum(y), x); + /// assert!(x.minimum(f16::NAN).is_nan()); + /// # } + /// ``` + /// + /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser + /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. + /// Note that this follows the semantics specified in IEEE 754-2019. + /// + /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN + /// operand is conserved; see [explanation of NaN as a special value](f16) for more info. + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "float_minimum_maximum", issue = "91079")] + #[must_use = "this returns the result of the comparison, without modifying either input"] + pub fn minimum(self, other: f16) -> f16 { + if self < other { + self + } else if other < self { + other + } else if self == other { + if self.is_sign_negative() && other.is_sign_positive() { self } else { other } + } else { + // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + self + other + } + } + + /// Calculates the middle point of `self` and `rhs`. + /// + /// This returns NaN when *either* argument is NaN or if a combination of + /// +inf and -inf is provided as arguments. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(num_midpoint)] + /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// + /// assert_eq!(1f16.midpoint(4.0), 2.5); + /// assert_eq!((-5.5f16).midpoint(8.0), 1.25); + /// # } + /// ``` + #[inline] + #[unstable(feature = "f16", issue = "116909")] + // #[unstable(feature = "num_midpoint", issue = "110840")] + pub fn midpoint(self, other: f16) -> f16 { + const LO: f16 = f16::MIN_POSITIVE * 2.; + const HI: f16 = f16::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs_private(); + let abs_b = b.abs_private(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else if abs_a < LO { + // Not safe to halve `a` (would underflow) + a + (b / 2.) + } else if abs_b < LO { + // Not safe to halve `b` (would underflow) + (a / 2.) + b + } else { + // Safe to halve `a` and `b` + (a / 2.) + (b / 2.) + } + } + /// Rounds toward zero and converts to any primitive integer type, /// assuming that the value is finite and fits in that type. /// diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 08d863f17caf..7710e23edf0b 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -797,6 +797,7 @@ impl f32 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { @@ -845,6 +846,7 @@ impl f32 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { @@ -1042,6 +1044,7 @@ impl f32 { /// assert_eq!(1f32.midpoint(4.0), 2.5); /// assert_eq!((-5.5f32).midpoint(8.0), 1.25); /// ``` + #[inline] #[unstable(feature = "num_midpoint", issue = "110840")] pub fn midpoint(self, other: f32) -> f32 { cfg_if! { @@ -1070,13 +1073,13 @@ impl f32 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 5d33eea6d011..a89859be7efe 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -805,6 +805,7 @@ impl f64 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_up(self) -> Self { @@ -853,6 +854,7 @@ impl f64 { /// [`INFINITY`]: Self::INFINITY /// [`MIN`]: Self::MIN /// [`MAX`]: Self::MAX + #[inline] #[unstable(feature = "float_next_up_down", issue = "91399")] #[rustc_const_unstable(feature = "float_next_up_down", issue = "91399")] pub const fn next_down(self) -> Self { @@ -1051,6 +1053,7 @@ impl f64 { /// assert_eq!(1f64.midpoint(4.0), 2.5); /// assert_eq!((-5.5f64).midpoint(8.0), 1.25); /// ``` + #[inline] #[unstable(feature = "num_midpoint", issue = "110840")] pub fn midpoint(self, other: f64) -> f64 { const LO: f64 = f64::MIN_POSITIVE * 2.; @@ -1064,13 +1067,13 @@ impl f64 { // Overflow is impossible (a + b) / 2. } else if abs_a < LO { - // Not safe to halve a + // Not safe to halve `a` (would underflow) a + (b / 2.) } else if abs_b < LO { - // Not safe to halve b + // Not safe to halve `b` (would underflow) (a / 2.) + b } else { - // Not safe to halve a and b + // Safe to halve `a` and `b` (a / 2.) + (b / 2.) } } diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs index a10c45933428..1f89d960be67 100644 --- a/library/core/src/pat.rs +++ b/library/core/src/pat.rs @@ -6,7 +6,7 @@ /// ``` #[macro_export] #[rustc_builtin_macro(pattern_type)] -#[unstable(feature = "core_pattern_type", issue = "none")] +#[unstable(feature = "core_pattern_type", issue = "123646")] macro_rules! pattern_type { ($($arg:tt)*) => { /* compiler built-in */ diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 5989bcbcc520..09ebef89fb0c 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1244,6 +1244,9 @@ mod prim_f64 {} /// actually implement it. For x86-64 and AArch64, ISA support is not even specified, /// so it will always be a software implementation significantly slower than `f64`. /// +/// _Note: `f128` support is incomplete. Many platforms will not be able to link math functions. On +/// x86 in particular, these functions do link but their results are always incorrect._ +/// /// *[See also the `std::f128::consts` module](crate::f128::consts).* /// /// [wikipedia]: https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 44db227b79ef..d6be37a76bb9 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1169,9 +1169,7 @@ impl NonNull { /// `align`. /// /// If it is not possible to align the pointer, the implementation returns - /// `usize::MAX`. It is permissible for the implementation to *always* - /// return `usize::MAX`. Only your algorithm's performance can depend - /// on getting a usable offset here, not its correctness. + /// `usize::MAX`. /// /// The offset is expressed in number of `T` elements, and not bytes. /// @@ -1179,6 +1177,15 @@ impl NonNull { /// beyond the allocation that the pointer points into. It is up to the caller to ensure that /// the returned offset is correct in all terms other than alignment. /// + /// When this is called during compile-time evaluation (which is unstable), the implementation + /// may return `usize::MAX` in cases where that can never happen at runtime. This is because the + /// actual alignment of pointers is not known yet during compile-time, so an offset with + /// guaranteed alignment can sometimes not be computed. For example, a buffer declared as `[u8; + /// N]` might be allocated at an odd or an even address, but at compile-time this is not yet + /// known, so the execution has to be correct for either choice. It is therefore impossible to + /// find an offset that is guaranteed to be 2-aligned. (This behavior is subject to change, as usual + /// for unstable APIs.) + /// /// # Panics /// /// The function panics if `align` is not a power-of-two. diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index b1440214d795..c76157720b70 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -28,6 +28,7 @@ pub mod memchr; issue = "none", reason = "exposed from core to be reused in std;" )] +#[doc(hidden)] pub mod sort; mod ascii; @@ -2880,9 +2881,19 @@ impl [T] { /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. /// - /// If `T: Ord` does not implement a total order the resulting order is unspecified. All - /// original elements will remain in the slice and any possible modifications via interior - /// mutability are observed in the input. Same is true if `T: Ord` panics. + /// If the implementation of [`Ord`] for `T` does not implement a [total order] the resulting + /// order of elements in the slice is unspecified. All original elements will remain in the + /// slice and any possible modifications via interior mutability are observed in the input. Same + /// is true if the implementation of [`Ord`] for `T` panics. + /// + /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] require + /// additional precautions. For example, `f32::NAN != f32::NAN`, which doesn't fulfill the + /// reflexivity requirement of [`Ord`]. By using an alternative comparison function with + /// `slice::sort_unstable_by` such as [`f32::total_cmp`] or [`f64::total_cmp`] that defines a + /// [total order] users can sort slices containing floating-point values. Alternatively, if all + /// values in the slice are guaranteed to be in a subset for which [`PartialOrd::partial_cmp`] + /// forms a [total order], it's possible to sort the slice with `sort_unstable_by(|a, b| + /// a.partial_cmp(b).unwrap())`. /// /// # Current implementation /// @@ -2894,18 +2905,21 @@ impl [T] { /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. /// - /// If `T: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5, 4, 1, -3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// /// v.sort_unstable(); - /// assert!(v == [-5, -3, 1, 2, 4]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable(&mut self) @@ -2915,31 +2929,20 @@ impl [T] { sort::unstable::sort(self, &mut T::lt); } - /// Sorts the slice with a comparator function, **without** preserving the initial order of + /// Sorts the slice with a comparison function, **without** preserving the initial order of /// equal elements. /// /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. /// - /// The comparator function should define a total ordering for the elements in the slice. If the - /// ordering is not total, the order of the elements is unspecified. + /// If the comparison function `compare` does not implement a [total order] the resulting order + /// of elements in the slice is unspecified. All original elements will remain in the slice and + /// any possible modifications via interior mutability are observed in the input. Same is true + /// if `compare` panics. /// - /// If the comparator function does not implement a total order the resulting order is - /// unspecified. All original elements will remain in the slice and any possible modifications - /// via interior mutability are observed in the input. Same is true if the comparator function - /// panics. A total order (for all `a`, `b` and `c`): - /// - /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and - /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. - /// - /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use - /// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`. - /// - /// ``` - /// let mut floats = [5f64, 4.0, 1.0, 3.0, 2.0]; - /// floats.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); - /// assert_eq!(floats, [1.0, 2.0, 3.0, 4.0, 5.0]); - /// ``` + /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor + /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and + /// examples see the [`Ord`] documentation. /// /// # Current implementation /// @@ -2951,21 +2954,24 @@ impl [T] { /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. /// - /// If `T: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if `compare` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [5, 4, 1, 3, 2]; + /// let mut v = [4, -5, 1, -3, 2]; /// v.sort_unstable_by(|a, b| a.cmp(b)); - /// assert!(v == [1, 2, 3, 4, 5]); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// /// // reverse sorting /// v.sort_unstable_by(|a, b| b.cmp(a)); - /// assert!(v == [5, 4, 3, 2, 1]); + /// assert_eq!(v, [4, 2, 1, -3, -5]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable_by(&mut self, mut compare: F) @@ -2981,9 +2987,10 @@ impl [T] { /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not /// allocate), and *O*(*n* \* log(*n*)) worst-case. /// - /// If `K: Ord` does not implement a total order the resulting order is unspecified. - /// All original elements will remain in the slice and any possible modifications via interior - /// mutability are observed in the input. Same is true if `K: Ord` panics. + /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting + /// order of elements in the slice is unspecified. All original elements will remain in the + /// slice and any possible modifications via interior mutability are observed in the input. Same + /// is true if the implementation of [`Ord`] for `K` panics. /// /// # Current implementation /// @@ -2995,18 +3002,21 @@ impl [T] { /// It is typically faster than stable sorting, except in a few special cases, e.g., when the /// slice is partially sorted. /// - /// If `K: Ord` does not implement a total order, the implementation may panic. + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order]. /// /// # Examples /// /// ``` - /// let mut v = [-5i32, 4, 1, -3, 2]; + /// let mut v = [4i32, -5, 1, -3, 2]; /// /// v.sort_unstable_by_key(|k| k.abs()); - /// assert!(v == [1, 2, -3, 4, -5]); + /// assert_eq!(v, [1, 2, -3, 4, -5]); /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] pub fn sort_unstable_by_key(&mut self, mut f: F) @@ -3038,15 +3048,14 @@ impl [T] { /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime /// for all inputs. /// - /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice is nearly fully sorted, where `slice::sort` may be faster. - /// /// [`sort_unstable`]: slice::sort_unstable /// /// # Panics /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order]. + /// /// # Examples /// /// ``` @@ -3069,6 +3078,7 @@ impl [T] { /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable(&mut self, index: usize) -> (&mut [T], &mut T, &mut [T]) @@ -3099,15 +3109,14 @@ impl [T] { /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime /// for all inputs. /// - /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice is nearly fully sorted, where `slice::sort` may be faster. - /// /// [`sort_unstable`]: slice::sort_unstable /// /// # Panics /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// + /// May panic if `compare` does not implement a [total order]. + /// /// # Examples /// /// ``` @@ -3130,6 +3139,7 @@ impl [T] { /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable_by( @@ -3164,15 +3174,14 @@ impl [T] { /// Median of Medians using Tukey's Ninther for pivot selection, which guarantees linear runtime /// for all inputs. /// - /// It is typically faster than stable sorting, except in a few special cases, e.g., when the - /// slice is nearly fully sorted, where `slice::sort` may be faster. - /// /// [`sort_unstable`]: slice::sort_unstable /// /// # Panics /// /// Panics when `index >= len()`, meaning it always panics on empty slices. /// + /// May panic if `K: Ord` does not implement a total order. + /// /// # Examples /// /// ``` @@ -3195,6 +3204,7 @@ impl [T] { /// ``` /// /// [ipnsort]: https://github.com/Voultapher/sort-research-rs/tree/main/ipnsort + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] pub fn select_nth_unstable_by_key( diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 5064c5a0ae55..db0c5c72822c 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -831,9 +831,9 @@ unsafe fn bidirectional_merge bool>( right = right.add((!left_nonempty) as usize); } - // We now should have consumed the full input exactly once. This can - // only fail if the comparison operator fails to be Ord, in which case - // we will panic and never access the inconsistent state in dst. + // We now should have consumed the full input exactly once. This can only fail if the + // user-provided comparison function fails to implement a strict weak ordering. In that case + // we panic and never access the inconsistent state in dst. if left != left_end || right != right_end { panic_on_ord_violation(); } @@ -842,7 +842,21 @@ unsafe fn bidirectional_merge bool>( #[inline(never)] fn panic_on_ord_violation() -> ! { - panic!("Ord violation"); + // This is indicative of a logic bug in the user-provided comparison function or Ord + // implementation. They are expected to implement a total order as explained in the Ord + // documentation. + // + // By panicking we inform the user, that they have a logic bug in their program. If a strict + // weak ordering is not given, the concept of comparison based sorting cannot yield a sorted + // result. E.g.: a < b < c < a + // + // The Ord documentation requires users to implement a total order. Arguably that's + // unnecessarily strict in the context of sorting. Issues only arise if the weaker requirement + // of a strict weak ordering is violated. + // + // The panic message talks about a total order because that's what the Ord documentation talks + // about and requires, so as to not confuse users. + panic!("user-provided comparison function does not correctly implement a total order"); } #[must_use] diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index ed735e1ebfbc..932e01f4401e 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -8,7 +8,7 @@ use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; pub(crate) mod heapsort; pub(crate) mod quicksort; -/// Unstable sort called ipnsort by Lukas Bergdoll. +/// Unstable sort called ipnsort by Lukas Bergdoll and Orson Peters. /// Design document: /// /// diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 8ce3eb2ea392..29932c0d1ffe 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -502,6 +502,8 @@ impl Waker { #[must_use] #[stable(feature = "futures_api", since = "1.36.0")] pub fn will_wake(&self, other: &Waker) -> bool { + // We optimize this by comparing vtable addresses instead of vtable contents. + // This is permitted since the function is documented as best-effort. let RawWaker { data: a_data, vtable: a_vtable } = self.waker; let RawWaker { data: b_data, vtable: b_vtable } = other.waker; a_data == b_data && ptr::eq(a_vtable, b_vtable) @@ -761,7 +763,11 @@ impl LocalWaker { #[must_use] #[unstable(feature = "local_waker", issue = "118959")] pub fn will_wake(&self, other: &LocalWaker) -> bool { - self.waker == other.waker + // We optimize this by comparing vtable addresses instead of vtable contents. + // This is permitted since the function is documented as best-effort. + let RawWaker { data: a_data, vtable: a_vtable } = self.waker; + let RawWaker { data: b_data, vtable: b_vtable } = other.waker; + a_data == b_data && ptr::eq(a_vtable, b_vtable) } /// Creates a new `LocalWaker` from [`RawWaker`]. diff --git a/library/core/tests/ascii_char.rs b/library/core/tests/ascii_char.rs new file mode 100644 index 000000000000..75b5fd4b9e61 --- /dev/null +++ b/library/core/tests/ascii_char.rs @@ -0,0 +1,28 @@ +use core::ascii::Char; +use core::fmt::Write; + +/// Tests Display implementation for ascii::Char. +#[test] +fn test_display() { + let want = (0..128u8).map(|b| b as char).collect::(); + let mut got = String::with_capacity(128); + for byte in 0..128 { + write!(&mut got, "{}", Char::from_u8(byte).unwrap()).unwrap(); + } + assert_eq!(want, got); +} + +/// Tests Debug implementation for ascii::Char. +#[test] +fn test_debug_control() { + for byte in 0..128u8 { + let mut want = format!("{:?}", byte as char); + // `char` uses `'\u{#}'` representation where ascii::char uses `'\x##'`. + // Transform former into the latter. + if let Some(rest) = want.strip_prefix("'\\u{") { + want = format!("'\\x{:0>2}'", rest.strip_suffix("}'").unwrap()); + } + let chr = core::ascii::Char::from_u8(byte).unwrap(); + assert_eq!(want, format!("{chr:?}"), "byte: {byte}"); + } +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 1e336bf96b8f..8872b4cbfd5b 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -122,6 +122,7 @@ mod alloc; mod any; mod array; mod ascii; +mod ascii_char; mod asserting; mod async_iter; mod atomic; diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index fe601855cc1e..2ce284c85e2d 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -compiler_builtins = { version = "0.1.114" } +compiler_builtins = { version = "0.1.118" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } hashbrown = { version = "0.14", default-features = false, features = [ diff --git a/library/std/build.rs b/library/std/build.rs index 9b58dd53ba20..35a5977b6eba 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -11,6 +11,7 @@ fn main() { .expect("CARGO_CFG_TARGET_POINTER_WIDTH was not set") .parse() .unwrap(); + let is_miri = env::var_os("CARGO_CFG_MIRI").is_some(); println!("cargo:rustc-check-cfg=cfg(netbsd10)"); if target_os == "netbsd" && env::var("RUSTC_STD_NETBSD10").is_ok() { @@ -85,7 +86,14 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(reliable_f16)"); println!("cargo:rustc-check-cfg=cfg(reliable_f128)"); + // This is a step beyond only having the types and basic functions available. Math functions + // aren't consistently available or correct. + println!("cargo:rustc-check-cfg=cfg(reliable_f16_math)"); + println!("cargo:rustc-check-cfg=cfg(reliable_f128_math)"); + let has_reliable_f16 = match (target_arch.as_str(), target_os.as_str()) { + // We can always enable these in Miri as that is not affected by codegen bugs. + _ if is_miri => true, // Selection failure until recent LLVM // FIXME(llvm19): can probably be removed at the version bump ("loongarch64", _) => false, @@ -113,6 +121,8 @@ fn main() { }; let has_reliable_f128 = match (target_arch.as_str(), target_os.as_str()) { + // We can always enable these in Miri as that is not affected by codegen bugs. + _ if is_miri => true, // Unsupported ("arm64ec", _) => false, // ABI and precision bugs @@ -130,10 +140,46 @@ fn main() { _ => false, }; + // These are currently empty, but will fill up as some platforms move from completely + // unreliable to reliable basics but unreliable math. + + // LLVM is currenlty adding missing routines, + let has_reliable_f16_math = has_reliable_f16 + && match (target_arch.as_str(), target_os.as_str()) { + // FIXME: Disabled on Miri as the intrinsics are not implemented yet. + _ if is_miri => false, + // Currently nothing special. Hooray! + // This will change as platforms gain better better support for standard ops but math + // lags behind. + _ => true, + }; + + let has_reliable_f128_math = has_reliable_f128 + && match (target_arch.as_str(), target_os.as_str()) { + // FIXME: Disabled on Miri as the intrinsics are not implemented yet. + _ if is_miri => false, + // LLVM lowers `fp128` math to `long double` symbols even on platforms where + // `long double` is not IEEE binary128. See + // . + // + // This rules out anything that doesn't have `long double` = `binary128`; <= 32 bits + // (ld is `f64`), anything other than Linux (Windows and MacOS use `f64`), and `x86` + // (ld is 80-bit extended precision). + ("x86_64", _) => false, + (_, "linux") if target_pointer_width == 64 => true, + _ => false, + }; + if has_reliable_f16 { println!("cargo:rustc-cfg=reliable_f16"); } if has_reliable_f128 { println!("cargo:rustc-cfg=reliable_f128"); } + if has_reliable_f16_math { + println!("cargo:rustc-cfg=reliable_f16_math"); + } + if has_reliable_f128_math { + println!("cargo:rustc-cfg=reliable_f128_math"); + } } diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index a5b00d57cefd..f6df6259137b 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -12,25 +12,180 @@ pub use core::f128::consts; #[cfg(not(test))] use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; #[cfg(not(test))] impl f128 { - /// Raises a number to an integer power. + /// Returns the largest integer less than or equal to `self`. /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. + /// This function always returns the precise result. /// - /// # Unspecified precision + /// # Examples /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f128 { - unsafe { intrinsics::powif128(self, n) } + pub fn floor(self) -> f128 { + unsafe { intrinsics::floorf128(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.01_f128; + /// let g = 4.0_f128; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f128 { + unsafe { intrinsics::ceilf128(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = -3.7_f128; + /// let i = 3.5_f128; + /// let j = 4.5_f128; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f128 { + unsafe { intrinsics::roundf128(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.3_f128; + /// let g = -3.3_f128; + /// let h = 3.5_f128; + /// let i = 4.5_f128; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f128 { + unsafe { intrinsics::rintf128(self) } + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.7_f128; + /// let g = 3.0_f128; + /// let h = -3.7_f128; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f128 { + unsafe { intrinsics::truncf128(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 3.6_f128; + /// let y = -3.6_f128; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f128::EPSILON); + /// assert!(abs_difference_y <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f128 { + self - self.trunc() } /// Computes the absolute value of `self`. @@ -41,7 +196,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(reliable_f128)] { // FIXME(f16_f128): reliable_f128 + /// # #[cfg(reliable_f128)] { /// /// let x = 3.5_f128; /// let y = -3.5_f128; @@ -61,4 +216,1129 @@ impl f128 { // We don't do this now because LLVM has lowering bugs for f128 math. Self::from_bits(self.to_bits() & !(1 << 127)) } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.5_f128; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f128::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f128::NAN.signum().is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn signum(self) -> f128 { + if self.is_nan() { Self::NAN } else { 1.0_f128.copysign(self) } + } + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise + /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of + /// `sign` is returned. Note, however, that conserving the sign bit on NaN + /// across arithmetical operations is not generally guaranteed. + /// See [explanation of NaN as a special value](primitive@f128) for more info. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 3.5_f128; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f128); + /// assert_eq!(f.copysign(-0.42), -3.5_f128); + /// assert_eq!((-f).copysign(0.42), 3.5_f128); + /// assert_eq!((-f).copysign(-0.42), -3.5_f128); + /// + /// assert!(f128::NAN.copysign(1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn copysign(self, sign: f128) -> f128 { + unsafe { intrinsics::copysignf128(self, sign) } + } + + /// 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 architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let m = 10.0_f128; + /// let x = 4.0_f128; + /// let b = 60.0_f128; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f128 + f128::EPSILON; + /// let one_minus_eps = 1.0_f128 - f128::EPSILON; + /// let minus_one = -1.0_f128; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f128::EPSILON * f128::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f128, b: f128) -> f128 { + unsafe { intrinsics::fmaf128(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f128) -> f128 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let a: f128 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f128::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f128) -> f128 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f128 { + unsafe { intrinsics::powif128(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powf(self, n: f128) -> f128 { + unsafe { intrinsics::powf128(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let positive = 4.0_f128; + /// let negative = -4.0_f128; + /// let negative_zero = -0.0_f128; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f128 { + unsafe { intrinsics::sqrtf128(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let one = 1.0f128; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp(self) -> f128 { + unsafe { intrinsics::expf128(self) } + } + + /// Returns `2^(self)`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 2.0f128; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp2(self) -> f128 { + unsafe { intrinsics::exp2f128(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let one = 1.0f128; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln(self) -> f128 { + unsafe { intrinsics::logf128(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation details; + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let five = 5.0f128; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log(self, base: f128) -> f128 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let two = 2.0f128; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log2(self) -> f128 { + unsafe { intrinsics::log2f128(self) } + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let ten = 10.0f128; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log10(self) -> f128 { + unsafe { intrinsics::log10f128(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// + /// This function currently corresponds to the `cbrtf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 8.0f128; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f128 { + unsafe { cmath::cbrtf128(self) } + } + + /// Compute the distance between the origin and a point (`x`, `y`) on the + /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a + /// right-angle triangle with other sides having length `x.abs()` and + /// `y.abs()`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// + /// This function currently corresponds to the `hypotf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0f128; + /// let y = 3.0f128; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn hypot(self, other: f128) -> f128 { + unsafe { cmath::hypotf128(self, other) } + } + + /// Computes the sine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sin(self) -> f128 { + unsafe { intrinsics::sinf128(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0 * std::f128::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cos(self) -> f128 { + unsafe { intrinsics::cosf128(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanf128` from libc on Unix and + /// Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tan(self) -> f128 { + unsafe { cmath::tanf128(self) } + } + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `asinf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = std::f128::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f128::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsin")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asin(self) -> f128 { + unsafe { cmath::asinf128(self) } + } + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `acosf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = std::f128::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f128::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acos(self) -> f128 { + unsafe { cmath::acosf128(self) } + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atanf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let f = 1.0f128; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctan")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan(self) -> f128 { + unsafe { cmath::atanf128(self) } + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atan2f128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f128; + /// let y1 = -3.0f128; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f128; + /// let y2 = 3.0f128; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f128::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f128::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f128::EPSILON); + /// assert!(abs_difference_2 <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan2(self, other: f128) -> f128 { + unsafe { cmath::atan2f128(self, other) } + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `(f128::sin(x), + /// f128::cos(x))`. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = std::f128::consts::FRAC_PI_4; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 <= f128::EPSILON); + /// assert!(abs_difference_1 <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "sincos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + pub fn sin_cos(self) -> (f128, f128) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `expm1f128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1e-8_f128; + /// + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp_m1(self) -> f128 { + unsafe { cmath::expm1f128(self) } + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `log1pf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1e-8_f128; + /// + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// # } + /// ``` + #[inline] + #[doc(alias = "log1p")] + #[must_use = "method returns a new number and does not mutate the original value"] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + pub fn ln_1p(self) -> f128 { + unsafe { cmath::log1pf128(self) } + } + + /// Hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `sinhf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = ((e * e) - 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sinh(self) -> f128 { + unsafe { cmath::sinhf128(self) } + } + + /// Hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `coshf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = ((e * e) + 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cosh(self) -> f128 { + unsafe { cmath::coshf128(self) } + } + + /// Hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanhf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let x = 1.0f128; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tanh(self) -> f128 { + unsafe { cmath::tanhf128(self) } + } + + /// Inverse hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1.0f128; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsinh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asinh(self) -> f128 { + let ax = self.abs(); + let ix = 1.0 / ax; + (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 1.0f128; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccosh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acosh(self) -> f128 { + if self < 1.0 { + Self::NAN + } else { + (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() + } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let e = std::f128::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 1e-5); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctanh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atanh(self) -> f128 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tgammaf128` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 5.0f128; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn gamma(self) -> f128 { + unsafe { cmath::tgammaf128(self) } + } + + /// Natural logarithm of the absolute value of the gamma function + /// + /// The integer part of the tuple indicates the sign of the gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `lgammaf128_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0f128; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f128::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_gamma(self) -> (f128, i32) { + let mut signgamp: i32 = 0; + let x = unsafe { cmath::lgammaf128_r(self, &mut signgamp) }; + (x, signgamp) + } } diff --git a/library/std/src/f128/tests.rs b/library/std/src/f128/tests.rs index 162c8dbad81a..7051c051bf72 100644 --- a/library/std/src/f128/tests.rs +++ b/library/std/src/f128/tests.rs @@ -4,6 +4,21 @@ use crate::f128::consts; use crate::num::{FpCategory as Fp, *}; +// Note these tolerances make sense around zero, but not for more extreme exponents. + +/// For operations that are near exact, usually not involving math of different +/// signs. +const TOL_PRECISE: f128 = 1e-28; + +/// Default tolerances. Works for values that should be near precise but not exact. Roughly +/// the precision carried by `100 * 100`. +const TOL: f128 = 1e-12; + +/// Tolerances for math that is allowed to be imprecise, usually due to multiple chained +/// operations. +#[cfg(reliable_f128_math)] +const TOL_IMPR: f128 = 1e-10; + /// Smallest number const TINY_BITS: u128 = 0x1; @@ -41,7 +56,33 @@ fn test_num_f128() { test_num(10f128, 2f128); } -// FIXME(f16_f128): add min and max tests when available +#[test] +#[cfg(reliable_f128_math)] +fn test_min_nan() { + assert_eq!(f128::NAN.min(2.0), 2.0); + assert_eq!(2.0f128.min(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_max_nan() { + assert_eq!(f128::NAN.max(2.0), 2.0); + assert_eq!(2.0f128.max(f128::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_minimum() { + assert!(f128::NAN.minimum(2.0).is_nan()); + assert!(2.0f128.minimum(f128::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_maximum() { + assert!(f128::NAN.maximum(2.0).is_nan()); + assert!(2.0f128.maximum(f128::NAN).is_nan()); +} #[test] fn test_nan() { @@ -191,9 +232,100 @@ fn test_classify() { assert_eq!(1e-4932f128.classify(), Fp::Subnormal); } -// FIXME(f16_f128): add missing math functions when available +#[test] +#[cfg(reliable_f128_math)] +fn test_floor() { + assert_approx_eq!(1.0f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.floor(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.floor(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).floor(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).floor(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).floor(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).floor(), -2.0f128, TOL_PRECISE); +} #[test] +#[cfg(reliable_f128_math)] +fn test_ceil() { + assert_approx_eq!(1.0f128.ceil(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.ceil(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.ceil(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).ceil(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).ceil(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).ceil(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_round() { + assert_approx_eq!(2.5f128.round(), 3.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.0f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.round_ties_even(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.round_ties_even(), 2.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.round_ties_even(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).round_ties_even(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).round_ties_even(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).round_ties_even(), -2.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).round_ties_even(), -2.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_trunc() { + assert_approx_eq!(1.0f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.trunc(), 1.0f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.trunc(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).trunc(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).trunc(), -1.0f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).trunc(), -1.0f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_fract() { + assert_approx_eq!(1.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!(1.3f128.fract(), 0.3f128, TOL_PRECISE); + assert_approx_eq!(1.5f128.fract(), 0.5f128, TOL_PRECISE); + assert_approx_eq!(1.7f128.fract(), 0.7f128, TOL_PRECISE); + assert_approx_eq!(0.0f128.fract(), 0.0f128, TOL_PRECISE); + assert_approx_eq!((-0.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.0f128).fract(), -0.0f128, TOL_PRECISE); + assert_approx_eq!((-1.3f128).fract(), -0.3f128, TOL_PRECISE); + assert_approx_eq!((-1.5f128).fract(), -0.5f128, TOL_PRECISE); + assert_approx_eq!((-1.7f128).fract(), -0.7f128, TOL_PRECISE); +} + +#[test] +#[cfg(reliable_f128_math)] fn test_abs() { assert_eq!(f128::INFINITY.abs(), f128::INFINITY); assert_eq!(1f128.abs(), 1f128); @@ -293,6 +425,24 @@ fn test_next_down() { } #[test] +#[cfg(reliable_f128_math)] +fn test_mul_add() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(12.3f128.mul_add(4.5, 6.7), 62.05, TOL_PRECISE); + assert_approx_eq!((-12.3f128).mul_add(-4.5, -6.7), 48.65, TOL_PRECISE); + assert_approx_eq!(0.0f128.mul_add(8.9, 1.2), 1.2, TOL_PRECISE); + assert_approx_eq!(3.4f128.mul_add(-0.0, 5.6), 5.6, TOL_PRECISE); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f128.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_recip() { let nan: f128 = f128::NAN; let inf: f128 = f128::INFINITY; @@ -301,11 +451,161 @@ fn test_recip() { assert_eq!(2.0f128.recip(), 0.5); assert_eq!((-0.4f128).recip(), -2.5); assert_eq!(0.0f128.recip(), inf); + assert_approx_eq!( + f128::MAX.recip(), + 8.40525785778023376565669454330438228902076605e-4933, + 1e-4900 + ); assert!(nan.recip().is_nan()); assert_eq!(inf.recip(), 0.0); assert_eq!(neg_inf.recip(), 0.0); } +// Many math functions allow for less accurate results, so the next tolerance up is used + +#[test] +#[cfg(reliable_f128_math)] +fn test_powi() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powi(1), 1.0); + assert_approx_eq!((-3.1f128).powi(2), 9.6100000000000005506706202140776519387, TOL); + assert_approx_eq!(5.9f128.powi(-2), 0.028727377190462507313100483690639638451, TOL); + assert_eq!(8.3f128.powi(0), 1.0); + assert!(nan.powi(2).is_nan()); + assert_eq!(inf.powi(3), inf); + assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_powf() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(1.0f128.powf(1.0), 1.0); + assert_approx_eq!(3.4f128.powf(4.5), 246.40818323761892815995637964326426756, TOL_IMPR); + assert_approx_eq!(2.7f128.powf(-3.2), 0.041652009108526178281070304373500889273, TOL_IMPR); + assert_approx_eq!((-3.1f128).powf(2.0), 9.6100000000000005506706202140776519387, TOL_IMPR); + assert_approx_eq!(5.9f128.powf(-2.0), 0.028727377190462507313100483690639638451, TOL_IMPR); + assert_eq!(8.3f128.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_sqrt_domain() { + assert!(f128::NAN.sqrt().is_nan()); + assert!(f128::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f128).sqrt().is_nan()); + assert_eq!((-0.0f128).sqrt(), -0.0); + assert_eq!(0.0f128.sqrt(), 0.0); + assert_eq!(1.0f128.sqrt(), 1.0); + assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f128.exp()); + assert_approx_eq!(consts::E, 1.0f128.exp(), TOL); + assert_approx_eq!(148.41315910257660342111558004055227962348775, 5.0f128.exp(), TOL); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f128.exp2()); + assert_eq!(1.0, 0.0f128.exp2()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(1.0f128.exp().ln(), 1.0, TOL); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f128).ln().is_nan()); + assert_eq!((-0.0f128).ln(), neg_inf); + assert_eq!(0.0f128.ln(), neg_inf); + assert_approx_eq!(4.0f128.ln(), 1.3862943611198906188344642429163531366, TOL); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log(10.0), 1.0); + assert_approx_eq!(2.3f128.log(3.5), 0.66485771361478710036766645911922010272, TOL); + assert_eq!(1.0f128.exp().log(1.0f128.exp()), 1.0); + assert!(1.0f128.log(1.0).is_nan()); + assert!(1.0f128.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f128).log(0.1).is_nan()); + assert_eq!((-0.0f128).log(2.0), neg_inf); + assert_eq!(0.0f128.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log2() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_approx_eq!(10.0f128.log2(), 3.32192809488736234787031942948939017, TOL); + assert_approx_eq!(2.3f128.log2(), 1.2016338611696504130002982471978765921, TOL); + assert_approx_eq!(1.0f128.exp().log2(), 1.4426950408889634073599246810018921381, TOL); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f128).log2().is_nan()); + assert_eq!((-0.0f128).log2(), neg_inf); + assert_eq!(0.0f128.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_log10() { + let nan: f128 = f128::NAN; + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + assert_eq!(10.0f128.log10(), 1.0); + assert_approx_eq!(2.3f128.log10(), 0.36172783601759284532595218865859309898, TOL); + assert_approx_eq!(1.0f128.exp().log10(), 0.43429448190325182765112891891660508222, TOL); + assert_eq!(1.0f128.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f128).log10().is_nan()); + assert_eq!((-0.0f128).log10(), neg_inf); + assert_eq!(0.0f128.log10(), neg_inf); +} + #[test] fn test_to_degrees() { let pi: f128 = consts::PI; @@ -313,8 +613,8 @@ fn test_to_degrees() { let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; assert_eq!(0.0f128.to_degrees(), 0.0); - assert_approx_eq!((-5.8f128).to_degrees(), -332.315521); - assert_eq!(pi.to_degrees(), 180.0); + assert_approx_eq!((-5.8f128).to_degrees(), -332.31552117587745090765431723855668471, TOL); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL); assert!(nan.to_degrees().is_nan()); assert_eq!(inf.to_degrees(), inf); assert_eq!(neg_inf.to_degrees(), neg_inf); @@ -328,19 +628,122 @@ fn test_to_radians() { let inf: f128 = f128::INFINITY; let neg_inf: f128 = f128::NEG_INFINITY; assert_eq!(0.0f128.to_radians(), 0.0); - assert_approx_eq!(154.6f128.to_radians(), 2.698279); - assert_approx_eq!((-332.31f128).to_radians(), -5.799903); + assert_approx_eq!(154.6f128.to_radians(), 2.6982790235832334267135442069489767804, TOL); + assert_approx_eq!((-332.31f128).to_radians(), -5.7999036373023566567593094812182763013, TOL); // check approx rather than exact because round trip for pi doesn't fall on an exactly // representable value (unlike `f32` and `f64`). - assert_approx_eq!(180.0f128.to_radians(), pi); + assert_approx_eq!(180.0f128.to_radians(), pi, TOL_PRECISE); assert!(nan.to_radians().is_nan()); assert_eq!(inf.to_radians(), inf); assert_eq!(neg_inf.to_radians(), neg_inf); } +#[test] +#[cfg(reliable_f128_math)] +fn test_asinh() { + // Lower accuracy results are allowed, use increased tolerances + assert_eq!(0.0f128.asinh(), 0.0f128); + assert_eq!((-0.0f128).asinh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f128).asinh().is_sign_negative()); + + // issue 63271 + assert_approx_eq!(2.0f128.asinh(), 1.443635475178810342493276740273105f128, TOL_IMPR); + assert_approx_eq!((-2.0f128).asinh(), -1.443635475178810342493276740273105f128, TOL_IMPR); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!( + (-67452098.07139316f128).asinh(), + -18.720075426274544393985484294000831757220, + TOL_IMPR + ); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.sinh().asinh(), TOL_IMPR); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f128, 1e-15f128.sinh().asinh() * 1e15f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_acosh() { + assert_eq!(1.0f128.acosh(), 0.0f128); + assert!(0.999f128.acosh().is_nan()); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f128.acosh(), 1.31695789692481670862504634730796844f128, TOL_IMPR); + assert_approx_eq!(3.0f128.acosh(), 1.76274717403908605046521864995958461f128, TOL_IMPR); + + // test for low accuracy from issue 104548 + assert_approx_eq!(60.0f128, 60.0f128.cosh().acosh(), TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_atanh() { + assert_eq!(0.0f128.atanh(), 0.0f128); + assert_eq!((-0.0f128).atanh(), -0.0f128); + + let inf: f128 = f128::INFINITY; + let neg_inf: f128 = f128::NEG_INFINITY; + let nan: f128 = f128::NAN; + assert_eq!(1.0f128.atanh(), inf); + assert_eq!((-1.0f128).atanh(), neg_inf); + assert!(2f128.atanh().atanh().is_nan()); + assert!((-2f128).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f128.atanh(), 0.54930614433405484569762261846126285f128, TOL_IMPR); + assert_approx_eq!((-0.5f128).atanh(), -0.54930614433405484569762261846126285f128, TOL_IMPR); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(2.0f128.gamma(), 1.0f128, TOL_IMPR); + assert_approx_eq!(3.0f128.gamma(), 2.0f128, TOL_IMPR); + assert_approx_eq!(4.0f128.gamma(), 6.0f128, TOL_IMPR); + assert_approx_eq!(5.0f128.gamma(), 24.0f128, TOL_IMPR); + assert_approx_eq!(0.5f128.gamma(), consts::PI.sqrt(), TOL_IMPR); + assert_approx_eq!((-0.5f128).gamma(), -2.0 * consts::PI.sqrt(), TOL_IMPR); + assert_eq!(0.0f128.gamma(), f128::INFINITY); + assert_eq!((-0.0f128).gamma(), f128::NEG_INFINITY); + assert!((-1.0f128).gamma().is_nan()); + assert!((-2.0f128).gamma().is_nan()); + assert!(f128::NAN.gamma().is_nan()); + assert!(f128::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f128::INFINITY.gamma(), f128::INFINITY); + assert_eq!(1760.9f128.gamma(), f128::INFINITY); +} + +#[test] +#[cfg(reliable_f128_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(1.0f128.ln_gamma().1, 1); + assert_approx_eq!(2.0f128.ln_gamma().0, 0.0f128, TOL_IMPR); + assert_eq!(2.0f128.ln_gamma().1, 1); + assert_approx_eq!(3.0f128.ln_gamma().0, 2.0f128.ln(), TOL_IMPR); + assert_eq!(3.0f128.ln_gamma().1, 1); + assert_approx_eq!((-0.5f128).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_IMPR); + assert_eq!((-0.5f128).ln_gamma().1, -1); +} + #[test] fn test_real_consts() { - // FIXME(f16_f128): add math tests when available use super::consts; let pi: f128 = consts::PI; @@ -351,29 +754,34 @@ fn test_real_consts() { let frac_pi_8: f128 = consts::FRAC_PI_8; let frac_1_pi: f128 = consts::FRAC_1_PI; let frac_2_pi: f128 = consts::FRAC_2_PI; - // let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; - // let sqrt2: f128 = consts::SQRT_2; - // let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; - // let e: f128 = consts::E; - // let log2_e: f128 = consts::LOG2_E; - // let log10_e: f128 = consts::LOG10_E; - // let ln_2: f128 = consts::LN_2; - // let ln_10: f128 = consts::LN_10; - assert_approx_eq!(frac_pi_2, pi / 2f128); - assert_approx_eq!(frac_pi_3, pi / 3f128); - assert_approx_eq!(frac_pi_4, pi / 4f128); - assert_approx_eq!(frac_pi_6, pi / 6f128); - assert_approx_eq!(frac_pi_8, pi / 8f128); - assert_approx_eq!(frac_1_pi, 1f128 / pi); - assert_approx_eq!(frac_2_pi, 2f128 / pi); - // assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt()); - // assert_approx_eq!(sqrt2, 2f128.sqrt()); - // assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt()); - // assert_approx_eq!(log2_e, e.log2()); - // assert_approx_eq!(log10_e, e.log10()); - // assert_approx_eq!(ln_2, 2f128.ln()); - // assert_approx_eq!(ln_10, 10f128.ln()); + assert_approx_eq!(frac_pi_2, pi / 2f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_3, pi / 3f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_4, pi / 4f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_6, pi / 6f128, TOL_PRECISE); + assert_approx_eq!(frac_pi_8, pi / 8f128, TOL_PRECISE); + assert_approx_eq!(frac_1_pi, 1f128 / pi, TOL_PRECISE); + assert_approx_eq!(frac_2_pi, 2f128 / pi, TOL_PRECISE); + + #[cfg(reliable_f128_math)] + { + let frac_2_sqrtpi: f128 = consts::FRAC_2_SQRT_PI; + let sqrt2: f128 = consts::SQRT_2; + let frac_1_sqrt2: f128 = consts::FRAC_1_SQRT_2; + let e: f128 = consts::E; + let log2_e: f128 = consts::LOG2_E; + let log10_e: f128 = consts::LOG10_E; + let ln_2: f128 = consts::LN_2; + let ln_10: f128 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f128 / pi.sqrt(), TOL_PRECISE); + assert_approx_eq!(sqrt2, 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(frac_1_sqrt2, 1f128 / 2f128.sqrt(), TOL_PRECISE); + assert_approx_eq!(log2_e, e.log2(), TOL_PRECISE); + assert_approx_eq!(log10_e, e.log10(), TOL_PRECISE); + assert_approx_eq!(ln_2, 2f128.ln(), TOL_PRECISE); + assert_approx_eq!(ln_10, 10f128.ln(), TOL_PRECISE); + } } #[test] @@ -382,10 +790,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); - assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); - assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); - assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); + assert_approx_eq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40029000000000000000000000000000), 12.5, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0, TOL_PRECISE); + assert_approx_eq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25, TOL_PRECISE); // Check that NaNs roundtrip their bits regardless of signaling-ness // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index e3024defed73..10908332762d 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -12,25 +12,180 @@ pub use core::f16::consts; #[cfg(not(test))] use crate::intrinsics; +#[cfg(not(test))] +use crate::sys::cmath; #[cfg(not(test))] impl f16 { - /// Raises a number to an integer power. + /// Returns the largest integer less than or equal to `self`. /// - /// Using this function is generally faster than using `powf`. - /// It might have a different sequence of rounding operations than `powf`, - /// so the results are not guaranteed to agree. + /// This function always returns the precise result. /// - /// # Unspecified precision + /// # Examples /// - /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and - /// can even differ within the same execution from one invocation to the next. + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// assert_eq!(h.floor(), -4.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - pub fn powi(self, n: i32) -> f16 { - unsafe { intrinsics::powif16(self, n) } + pub fn floor(self) -> f16 { + unsafe { intrinsics::floorf16(self) } + } + + /// Returns the smallest integer greater than or equal to `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.01_f16; + /// let g = 4.0_f16; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "ceiling")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ceil(self) -> f16 { + unsafe { intrinsics::ceilf16(self) } + } + + /// Returns the nearest integer to `self`. If a value is half-way between two + /// integers, round away from `0.0`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = -3.7_f16; + /// let i = 3.5_f16; + /// let j = 4.5_f16; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// assert_eq!(h.round(), -4.0); + /// assert_eq!(i.round(), 4.0); + /// assert_eq!(j.round(), 5.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round(self) -> f16 { + unsafe { intrinsics::roundf16(self) } + } + + /// Returns the nearest integer to a number. Rounds half-way cases to the number + /// with an even least significant digit. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.3_f16; + /// let g = -3.3_f16; + /// let h = 3.5_f16; + /// let i = 4.5_f16; + /// + /// assert_eq!(f.round_ties_even(), 3.0); + /// assert_eq!(g.round_ties_even(), -3.0); + /// assert_eq!(h.round_ties_even(), 4.0); + /// assert_eq!(i.round_ties_even(), 4.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn round_ties_even(self) -> f16 { + unsafe { intrinsics::rintf16(self) } + } + + /// Returns the integer part of `self`. + /// This means that non-integer numbers are always truncated towards zero. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.7_f16; + /// let g = 3.0_f16; + /// let h = -3.7_f16; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), 3.0); + /// assert_eq!(h.trunc(), -3.0); + /// # } + /// ``` + #[inline] + #[doc(alias = "truncate")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn trunc(self) -> f16 { + unsafe { intrinsics::truncf16(self) } + } + + /// Returns the fractional part of `self`. + /// + /// This function always returns the precise result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 3.6_f16; + /// let y = -3.6_f16; + /// let abs_difference_x = (x.fract() - 0.6).abs(); + /// let abs_difference_y = (y.fract() - (-0.6)).abs(); + /// + /// assert!(abs_difference_x <= f16::EPSILON); + /// assert!(abs_difference_y <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn fract(self) -> f16 { + self - self.trunc() } /// Computes the absolute value of `self`. @@ -60,4 +215,1127 @@ impl f16 { // FIXME(f16_f128): replace with `intrinsics::fabsf16` when available Self::from_bits(self.to_bits() & !(1 << 15)) } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - NaN if the number is NaN + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.5_f16; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f16::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f16::NAN.signum().is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn signum(self) -> f16 { + if self.is_nan() { Self::NAN } else { 1.0_f16.copysign(self) } + } + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise + /// equal to `-self`. If `self` is a NaN, then a NaN with the sign bit of + /// `sign` is returned. Note, however, that conserving the sign bit on NaN + /// across arithmetical operations is not generally guaranteed. + /// See [explanation of NaN as a special value](primitive@f16) for more info. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 3.5_f16; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f16); + /// assert_eq!(f.copysign(-0.42), -3.5_f16); + /// assert_eq!((-f).copysign(0.42), 3.5_f16); + /// assert_eq!((-f).copysign(-0.42), -3.5_f16); + /// + /// assert!(f16::NAN.copysign(1.0).is_nan()); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn copysign(self, sign: f16) -> f16 { + unsafe { intrinsics::copysignf16(self, sign) } + } + + /// 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 architecture has a dedicated `fma` CPU instruction. However, + /// this is not always true, and will be heavily dependant on designing + /// algorithms with specific target hardware in mind. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as + /// `fusedMultiplyAdd` and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let m = 10.0_f16; + /// let x = 4.0_f16; + /// let b = 60.0_f16; + /// + /// assert_eq!(m.mul_add(x, b), 100.0); + /// assert_eq!(m * x + b, 100.0); + /// + /// let one_plus_eps = 1.0_f16 + f16::EPSILON; + /// let one_minus_eps = 1.0_f16 - f16::EPSILON; + /// let minus_one = -1.0_f16; + /// + /// // The exact result (1 + eps) * (1 - eps) = 1 - eps * eps. + /// assert_eq!(one_plus_eps.mul_add(one_minus_eps, minus_one), -f16::EPSILON * f16::EPSILON); + /// // Different rounding with the non-fused multiply and add. + /// assert_eq!(one_plus_eps * one_minus_eps + minus_one, 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn mul_add(self, a: f16, b: f16) -> f16 { + unsafe { intrinsics::fmaf16(self, a, b) } + } + + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * rhs + self.rem_euclid(rhs)`. + /// In other words, the result is `self / rhs` rounded to the integer `n` + /// such that `self >= n * rhs`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn div_euclid(self, rhs: f16) -> f16 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == rhs.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` + /// approximately. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let a: f16 = 7.0; + /// let b = 4.0; + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); + /// // limitation due to round-off error + /// assert!((-f16::EPSILON).rem_euclid(3.0) != 0.0); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[doc(alias = "modulo", alias = "mod")] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn rem_euclid(self, rhs: f16) -> f16 { + let r = self % rhs; + if r < 0.0 { r + rhs.abs() } else { r } + } + + /// Raises a number to an integer power. + /// + /// Using this function is generally faster than using `powf`. + /// It might have a different sequence of rounding operations than `powf`, + /// so the results are not guaranteed to agree. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powi(self, n: i32) -> f16 { + unsafe { intrinsics::powif16(self, n) } + } + + /// Raises a number to a floating point power. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn powf(self, n: f16) -> f16 { + unsafe { intrinsics::powf16(self, n) } + } + + /// Returns the square root of a number. + /// + /// Returns NaN if `self` is a negative number other than `-0.0`. + /// + /// # Precision + /// + /// The result of this operation is guaranteed to be the rounded + /// infinite-precision result. It is specified by IEEE 754 as `squareRoot` + /// and guaranteed not to change. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let positive = 4.0_f16; + /// let negative = -4.0_f16; + /// let negative_zero = -0.0_f16; + /// + /// assert_eq!(positive.sqrt(), 2.0); + /// assert!(negative.sqrt().is_nan()); + /// assert!(negative_zero.sqrt() == negative_zero); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sqrt(self) -> f16 { + unsafe { intrinsics::sqrtf16(self) } + } + + /// Returns `e^(self)`, (the exponential function). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let one = 1.0f16; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp(self) -> f16 { + unsafe { intrinsics::expf16(self) } + } + + /// Returns `2^(self)`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 2.0f16; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp2(self) -> f16 { + unsafe { intrinsics::exp2f16(self) } + } + + /// Returns the natural logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let one = 1.0f16; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln(self) -> f16 { + unsafe { intrinsics::logf16(self) } + } + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// The result might not be correctly rounded owing to implementation details; + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let five = 5.0f16; + /// + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log(self, base: f16) -> f16 { + self.ln() / base.ln() + } + + /// Returns the base 2 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let two = 2.0f16; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log2(self) -> f16 { + unsafe { intrinsics::log2f16(self) } + } + + /// Returns the base 10 logarithm of the number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let ten = 10.0f16; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn log10(self) -> f16 { + unsafe { intrinsics::log10f16(self) } + } + + /// Returns the cube root of a number. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `cbrtf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 8.0f16; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cbrt(self) -> f16 { + (unsafe { cmath::cbrtf(self as f32) }) as f16 + } + + /// Compute the distance between the origin and a point (`x`, `y`) on the + /// Euclidean plane. Equivalently, compute the length of the hypotenuse of a + /// right-angle triangle with other sides having length `x.abs()` and + /// `y.abs()`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `hypotf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0f16; + /// let y = 3.0f16; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn hypot(self, other: f16) -> f16 { + (unsafe { cmath::hypotf(self as f32, other as f32) }) as f16 + } + + /// Computes the sine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_2; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sin(self) -> f16 { + unsafe { intrinsics::sinf16(self) } + } + + /// Computes the cosine of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0 * std::f16::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cos(self) -> f16 { + unsafe { intrinsics::cosf16(self) } + } + + /// Computes the tangent of a number (in radians). + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanf` from libc on Unix and + /// Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_4; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tan(self) -> f16 { + (unsafe { cmath::tanf(self as f32) }) as f16 + } + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `asinf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = std::f16::consts::FRAC_PI_2; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - std::f16::consts::FRAC_PI_2).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsin")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asin(self) -> f16 { + (unsafe { cmath::asinf(self as f32) }) as f16 + } + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `acosf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = std::f16::consts::FRAC_PI_4; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - std::f16::consts::FRAC_PI_4).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acos(self) -> f16 { + (unsafe { cmath::acosf(self as f32) }) as f16 + } + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atanf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let f = 1.0f16; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctan")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan(self) -> f16 { + (unsafe { cmath::atanf(self as f32) }) as f16 + } + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`) in radians. + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `atan2f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// // Positive angles measured counter-clockwise + /// // from positive x axis + /// // -pi/4 radians (45 deg clockwise) + /// let x1 = 3.0f16; + /// let y1 = -3.0f16; + /// + /// // 3pi/4 radians (135 deg counter-clockwise) + /// let x2 = -3.0f16; + /// let y2 = 3.0f16; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-std::f16::consts::FRAC_PI_4)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - (3.0 * std::f16::consts::FRAC_PI_4)).abs(); + /// + /// assert!(abs_difference_1 <= f16::EPSILON); + /// assert!(abs_difference_2 <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atan2(self, other: f16) -> f16 { + (unsafe { cmath::atan2f(self as f32, other as f32) }) as f16 + } + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `(f16::sin(x), + /// f16::cos(x))`. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = std::f16::consts::FRAC_PI_4; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 <= f16::EPSILON); + /// assert!(abs_difference_1 <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "sincos")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + pub fn sin_cos(self) -> (f16, f16) { + (self.sin(), self.cos()) + } + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `expm1f` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1e-4_f16; + /// + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); + /// + /// assert!(abs_difference < 1e-4); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn exp_m1(self) -> f16 { + (unsafe { cmath::expm1f(self as f32) }) as f16 + } + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `log1pf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1e-4_f16; + /// + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); + /// + /// assert!(abs_difference < 1e-4); + /// # } + /// ``` + #[inline] + #[doc(alias = "log1p")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_1p(self) -> f16 { + (unsafe { cmath::log1pf(self as f32) }) as f16 + } + + /// Hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `sinhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = ((e * e) - 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn sinh(self) -> f16 { + (unsafe { cmath::sinhf(self as f32) }) as f16 + } + + /// Hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `coshf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = ((e * e) + 1.0) / (2.0 * e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn cosh(self) -> f16 { + (unsafe { cmath::coshf(self as f32) }) as f16 + } + + /// Hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tanhf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let x = 1.0f16; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2)) / (1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn tanh(self) -> f16 { + (unsafe { cmath::tanhf(self as f32) }) as f16 + } + + /// Inverse hyperbolic sine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1.0f16; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arcsinh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn asinh(self) -> f16 { + let ax = self.abs(); + let ix = 1.0 / ax; + (ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self) + } + + /// Inverse hyperbolic cosine function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 1.0f16; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[doc(alias = "arccosh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn acosh(self) -> f16 { + if self < 1.0 { + Self::NAN + } else { + (self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln() + } + } + + /// Inverse hyperbolic tangent function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let e = std::f16::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference <= 0.01); + /// # } + /// ``` + #[inline] + #[doc(alias = "arctanh")] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn atanh(self) -> f16 { + 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() + } + + /// Gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `tgammaf` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 5.0f16; + /// + /// let abs_difference = (x.gamma() - 24.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn gamma(self) -> f16 { + (unsafe { cmath::tgammaf(self as f32) }) as f16 + } + + /// Natural logarithm of the absolute value of the gamma function + /// + /// The integer part of the tuple indicates the sign of the gamma function. + /// + /// # Unspecified precision + /// + /// The precision of this function is non-deterministic. This means it varies by platform, + /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// This function currently corresponds to the `lgamma_r` from libc on Unix + /// and Windows. Note that this might change in the future. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(float_gamma)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0f16; + /// + /// let abs_difference = (x.ln_gamma().0 - 0.0).abs(); + /// + /// assert!(abs_difference <= f16::EPSILON); + /// # } + /// ``` + #[inline] + #[rustc_allow_incoherent_impl] + #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] + pub fn ln_gamma(self) -> (f16, i32) { + let mut signgamp: i32 = 0; + let x = (unsafe { cmath::lgammaf_r(self as f32, &mut signgamp) }) as f16; + (x, signgamp) + } } diff --git a/library/std/src/f16/tests.rs b/library/std/src/f16/tests.rs index f73bdf68e829..684ee3f3855b 100644 --- a/library/std/src/f16/tests.rs +++ b/library/std/src/f16/tests.rs @@ -4,11 +4,21 @@ use crate::f16::consts; use crate::num::{FpCategory as Fp, *}; -// We run out of precision pretty quickly with f16 -// const F16_APPROX_L1: f16 = 0.001; -const F16_APPROX_L2: f16 = 0.01; -// const F16_APPROX_L3: f16 = 0.1; -const F16_APPROX_L4: f16 = 0.5; +/// Tolerance for results on the order of 10.0e-2 +#[allow(unused)] +const TOL_N2: f16 = 0.0001; + +/// Tolerance for results on the order of 10.0e+0 +#[allow(unused)] +const TOL_0: f16 = 0.01; + +/// Tolerance for results on the order of 10.0e+2 +#[allow(unused)] +const TOL_P2: f16 = 0.5; + +/// Tolerance for results on the order of 10.0e+4 +#[allow(unused)] +const TOL_P4: f16 = 10.0; /// Smallest number const TINY_BITS: u16 = 0x1; @@ -47,7 +57,33 @@ fn test_num_f16() { test_num(10f16, 2f16); } -// FIXME(f16_f128): add min and max tests when available +#[test] +#[cfg(reliable_f16_math)] +fn test_min_nan() { + assert_eq!(f16::NAN.min(2.0), 2.0); + assert_eq!(2.0f16.min(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_max_nan() { + assert_eq!(f16::NAN.max(2.0), 2.0); + assert_eq!(2.0f16.max(f16::NAN), 2.0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_minimum() { + assert!(f16::NAN.minimum(2.0).is_nan()); + assert!(2.0f16.minimum(f16::NAN).is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_maximum() { + assert!(f16::NAN.maximum(2.0).is_nan()); + assert!(2.0f16.maximum(f16::NAN).is_nan()); +} #[test] fn test_nan() { @@ -197,9 +233,100 @@ fn test_classify() { assert_eq!(1e-5f16.classify(), Fp::Subnormal); } -// FIXME(f16_f128): add missing math functions when available +#[test] +#[cfg(reliable_f16_math)] +fn test_floor() { + assert_approx_eq!(1.0f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.floor(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.floor(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).floor(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).floor(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.5f16).floor(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).floor(), -2.0f16, TOL_0); +} #[test] +#[cfg(reliable_f16_math)] +fn test_ceil() { + assert_approx_eq!(1.0f16.ceil(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.5f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.ceil(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.ceil(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).ceil(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).ceil(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).ceil(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round() { + assert_approx_eq!(2.5f16.round(), 3.0f16, TOL_0); + assert_approx_eq!(1.0f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_round_ties_even() { + assert_approx_eq!(2.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.0f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.round_ties_even(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(1.7f16.round_ties_even(), 2.0f16, TOL_0); + assert_approx_eq!(0.0f16.round_ties_even(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).round_ties_even(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).round_ties_even(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).round_ties_even(), -2.0f16, TOL_0); + assert_approx_eq!((-1.7f16).round_ties_even(), -2.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_trunc() { + assert_approx_eq!(1.0f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.3f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.5f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(1.7f16.trunc(), 1.0f16, TOL_0); + assert_approx_eq!(0.0f16.trunc(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).trunc(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.3f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.5f16).trunc(), -1.0f16, TOL_0); + assert_approx_eq!((-1.7f16).trunc(), -1.0f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_fract() { + assert_approx_eq!(1.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!(1.3f16.fract(), 0.3f16, TOL_0); + assert_approx_eq!(1.5f16.fract(), 0.5f16, TOL_0); + assert_approx_eq!(1.7f16.fract(), 0.7f16, TOL_0); + assert_approx_eq!(0.0f16.fract(), 0.0f16, TOL_0); + assert_approx_eq!((-0.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.0f16).fract(), -0.0f16, TOL_0); + assert_approx_eq!((-1.3f16).fract(), -0.3f16, TOL_0); + assert_approx_eq!((-1.5f16).fract(), -0.5f16, TOL_0); + assert_approx_eq!((-1.7f16).fract(), -0.7f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_abs() { assert_eq!(f16::INFINITY.abs(), f16::INFINITY); assert_eq!(1f16.abs(), 1f16); @@ -299,6 +426,24 @@ fn test_next_down() { } #[test] +#[cfg(reliable_f16_math)] +fn test_mul_add() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(12.3f16.mul_add(4.5, 6.7), 62.05, TOL_P2); + assert_approx_eq!((-12.3f16).mul_add(-4.5, -6.7), 48.65, TOL_P2); + assert_approx_eq!(0.0f16.mul_add(8.9, 1.2), 1.2, TOL_0); + assert_approx_eq!(3.4f16.mul_add(-0.0, 5.6), 5.6, TOL_0); + assert!(nan.mul_add(7.8, 9.0).is_nan()); + assert_eq!(inf.mul_add(7.8, 9.0), inf); + assert_eq!(neg_inf.mul_add(7.8, 9.0), neg_inf); + assert_eq!(8.9f16.mul_add(inf, 3.2), inf); + assert_eq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] fn test_recip() { let nan: f16 = f16::NAN; let inf: f16 = f16::INFINITY; @@ -307,11 +452,157 @@ fn test_recip() { assert_eq!(2.0f16.recip(), 0.5); assert_eq!((-0.4f16).recip(), -2.5); assert_eq!(0.0f16.recip(), inf); + assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); assert!(nan.recip().is_nan()); assert_eq!(inf.recip(), 0.0); assert_eq!(neg_inf.recip(), 0.0); } +#[test] +#[cfg(reliable_f16_math)] +fn test_powi() { + // FIXME(llvm19): LLVM misoptimizes `powi.f16` + // + // let nan: f16 = f16::NAN; + // let inf: f16 = f16::INFINITY; + // let neg_inf: f16 = f16::NEG_INFINITY; + // assert_eq!(1.0f16.powi(1), 1.0); + // assert_approx_eq!((-3.1f16).powi(2), 9.61, TOL_0); + // assert_approx_eq!(5.9f16.powi(-2), 0.028727, TOL_N2); + // assert_eq!(8.3f16.powi(0), 1.0); + // assert!(nan.powi(2).is_nan()); + // assert_eq!(inf.powi(3), inf); + // assert_eq!(neg_inf.powi(2), inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_powf() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(1.0f16.powf(1.0), 1.0); + assert_approx_eq!(3.4f16.powf(4.5), 246.408183, TOL_P2); + assert_approx_eq!(2.7f16.powf(-3.2), 0.041652, TOL_N2); + assert_approx_eq!((-3.1f16).powf(2.0), 9.61, TOL_P2); + assert_approx_eq!(5.9f16.powf(-2.0), 0.028727, TOL_N2); + assert_eq!(8.3f16.powf(0.0), 1.0); + assert!(nan.powf(2.0).is_nan()); + assert_eq!(inf.powf(2.0), inf); + assert_eq!(neg_inf.powf(3.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_sqrt_domain() { + assert!(f16::NAN.sqrt().is_nan()); + assert!(f16::NEG_INFINITY.sqrt().is_nan()); + assert!((-1.0f16).sqrt().is_nan()); + assert_eq!((-0.0f16).sqrt(), -0.0); + assert_eq!(0.0f16.sqrt(), 0.0); + assert_eq!(1.0f16.sqrt(), 1.0); + assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp() { + assert_eq!(1.0, 0.0f16.exp()); + assert_approx_eq!(2.718282, 1.0f16.exp(), TOL_0); + assert_approx_eq!(148.413159, 5.0f16.exp(), TOL_0); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp()); + assert_eq!(0.0, neg_inf.exp()); + assert!(nan.exp().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_exp2() { + assert_eq!(32.0, 5.0f16.exp2()); + assert_eq!(1.0, 0.0f16.exp2()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf, inf.exp2()); + assert_eq!(0.0, neg_inf.exp2()); + assert!(nan.exp2().is_nan()); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(1.0f16.exp().ln(), 1.0, TOL_0); + assert!(nan.ln().is_nan()); + assert_eq!(inf.ln(), inf); + assert!(neg_inf.ln().is_nan()); + assert!((-2.3f16).ln().is_nan()); + assert_eq!((-0.0f16).ln(), neg_inf); + assert_eq!(0.0f16.ln(), neg_inf); + assert_approx_eq!(4.0f16.ln(), 1.386294, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log(10.0), 1.0); + assert_approx_eq!(2.3f16.log(3.5), 0.664858, TOL_0); + assert_eq!(1.0f16.exp().log(1.0f16.exp()), 1.0); + assert!(1.0f16.log(1.0).is_nan()); + assert!(1.0f16.log(-13.9).is_nan()); + assert!(nan.log(2.3).is_nan()); + assert_eq!(inf.log(10.0), inf); + assert!(neg_inf.log(8.8).is_nan()); + assert!((-2.3f16).log(0.1).is_nan()); + assert_eq!((-0.0f16).log(2.0), neg_inf); + assert_eq!(0.0f16.log(7.0), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log2() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_approx_eq!(10.0f16.log2(), 3.321928, TOL_0); + assert_approx_eq!(2.3f16.log2(), 1.201634, TOL_0); + assert_approx_eq!(1.0f16.exp().log2(), 1.442695, TOL_0); + assert!(nan.log2().is_nan()); + assert_eq!(inf.log2(), inf); + assert!(neg_inf.log2().is_nan()); + assert!((-2.3f16).log2().is_nan()); + assert_eq!((-0.0f16).log2(), neg_inf); + assert_eq!(0.0f16.log2(), neg_inf); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_log10() { + let nan: f16 = f16::NAN; + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + assert_eq!(10.0f16.log10(), 1.0); + assert_approx_eq!(2.3f16.log10(), 0.361728, TOL_0); + assert_approx_eq!(1.0f16.exp().log10(), 0.434294, TOL_0); + assert_eq!(1.0f16.log10(), 0.0); + assert!(nan.log10().is_nan()); + assert_eq!(inf.log10(), inf); + assert!(neg_inf.log10().is_nan()); + assert!((-2.3f16).log10().is_nan()); + assert_eq!((-0.0f16).log10(), neg_inf); + assert_eq!(0.0f16.log10(), neg_inf); +} + #[test] fn test_to_degrees() { let pi: f16 = consts::PI; @@ -319,8 +610,8 @@ fn test_to_degrees() { let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; assert_eq!(0.0f16.to_degrees(), 0.0); - assert_approx_eq!((-5.8f16).to_degrees(), -332.315521); - assert_approx_eq!(pi.to_degrees(), 180.0, F16_APPROX_L4); + assert_approx_eq!((-5.8f16).to_degrees(), -332.315521, TOL_P2); + assert_approx_eq!(pi.to_degrees(), 180.0, TOL_P2); assert!(nan.to_degrees().is_nan()); assert_eq!(inf.to_degrees(), inf); assert_eq!(neg_inf.to_degrees(), neg_inf); @@ -334,14 +625,112 @@ fn test_to_radians() { let inf: f16 = f16::INFINITY; let neg_inf: f16 = f16::NEG_INFINITY; assert_eq!(0.0f16.to_radians(), 0.0); - assert_approx_eq!(154.6f16.to_radians(), 2.698279); - assert_approx_eq!((-332.31f16).to_radians(), -5.799903); - assert_approx_eq!(180.0f16.to_radians(), pi, F16_APPROX_L2); + assert_approx_eq!(154.6f16.to_radians(), 2.698279, TOL_0); + assert_approx_eq!((-332.31f16).to_radians(), -5.799903, TOL_0); + assert_approx_eq!(180.0f16.to_radians(), pi, TOL_0); assert!(nan.to_radians().is_nan()); assert_eq!(inf.to_radians(), inf); assert_eq!(neg_inf.to_radians(), neg_inf); } +#[test] +#[cfg(reliable_f16_math)] +fn test_asinh() { + assert_eq!(0.0f16.asinh(), 0.0f16); + assert_eq!((-0.0f16).asinh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.asinh(), inf); + assert_eq!(neg_inf.asinh(), neg_inf); + assert!(nan.asinh().is_nan()); + assert!((-0.0f16).asinh().is_sign_negative()); + // issue 63271 + assert_approx_eq!(2.0f16.asinh(), 1.443635475178810342493276740273105f16, TOL_0); + assert_approx_eq!((-2.0f16).asinh(), -1.443635475178810342493276740273105f16, TOL_0); + // regression test for the catastrophic cancellation fixed in 72486 + assert_approx_eq!((-200.0f16).asinh(), -5.991470797049389, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.sinh().asinh(), TOL_0); + // mul needed for approximate comparison to be meaningful + assert_approx_eq!(1.0f16, 1e-3f16.sinh().asinh() * 1e3f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_acosh() { + assert_eq!(1.0f16.acosh(), 0.0f16); + assert!(0.999f16.acosh().is_nan()); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(inf.acosh(), inf); + assert!(neg_inf.acosh().is_nan()); + assert!(nan.acosh().is_nan()); + assert_approx_eq!(2.0f16.acosh(), 1.31695789692481670862504634730796844f16, TOL_0); + assert_approx_eq!(3.0f16.acosh(), 1.76274717403908605046521864995958461f16, TOL_0); + + // test for low accuracy from issue 104548 + assert_approx_eq!(10.0f16, 10.0f16.cosh().acosh(), TOL_P2); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_atanh() { + assert_eq!(0.0f16.atanh(), 0.0f16); + assert_eq!((-0.0f16).atanh(), -0.0f16); + + let inf: f16 = f16::INFINITY; + let neg_inf: f16 = f16::NEG_INFINITY; + let nan: f16 = f16::NAN; + assert_eq!(1.0f16.atanh(), inf); + assert_eq!((-1.0f16).atanh(), neg_inf); + assert!(2f16.atanh().atanh().is_nan()); + assert!((-2f16).atanh().atanh().is_nan()); + assert!(inf.atanh().is_nan()); + assert!(neg_inf.atanh().is_nan()); + assert!(nan.atanh().is_nan()); + assert_approx_eq!(0.5f16.atanh(), 0.54930614433405484569762261846126285f16, TOL_0); + assert_approx_eq!((-0.5f16).atanh(), -0.54930614433405484569762261846126285f16, TOL_0); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_gamma() { + // precision can differ among platforms + assert_approx_eq!(1.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(2.0f16.gamma(), 1.0f16, TOL_0); + assert_approx_eq!(3.0f16.gamma(), 2.0f16, TOL_0); + assert_approx_eq!(4.0f16.gamma(), 6.0f16, TOL_0); + assert_approx_eq!(5.0f16.gamma(), 24.0f16, TOL_0); + assert_approx_eq!(0.5f16.gamma(), consts::PI.sqrt(), TOL_0); + assert_approx_eq!((-0.5f16).gamma(), -2.0 * consts::PI.sqrt(), TOL_0); + assert_eq!(0.0f16.gamma(), f16::INFINITY); + assert_eq!((-0.0f16).gamma(), f16::NEG_INFINITY); + assert!((-1.0f16).gamma().is_nan()); + assert!((-2.0f16).gamma().is_nan()); + assert!(f16::NAN.gamma().is_nan()); + assert!(f16::NEG_INFINITY.gamma().is_nan()); + assert_eq!(f16::INFINITY.gamma(), f16::INFINITY); + assert_eq!(171.71f16.gamma(), f16::INFINITY); +} + +#[test] +#[cfg(reliable_f16_math)] +fn test_ln_gamma() { + assert_approx_eq!(1.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(1.0f16.ln_gamma().1, 1); + assert_approx_eq!(2.0f16.ln_gamma().0, 0.0f16, TOL_0); + assert_eq!(2.0f16.ln_gamma().1, 1); + assert_approx_eq!(3.0f16.ln_gamma().0, 2.0f16.ln(), TOL_0); + assert_eq!(3.0f16.ln_gamma().1, 1); + assert_approx_eq!((-0.5f16).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln(), TOL_0); + assert_eq!((-0.5f16).ln_gamma().1, -1); +} + #[test] fn test_real_consts() { // FIXME(f16_f128): add math tests when available @@ -355,29 +744,34 @@ fn test_real_consts() { let frac_pi_8: f16 = consts::FRAC_PI_8; let frac_1_pi: f16 = consts::FRAC_1_PI; let frac_2_pi: f16 = consts::FRAC_2_PI; - // let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; - // let sqrt2: f16 = consts::SQRT_2; - // let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; - // let e: f16 = consts::E; - // let log2_e: f16 = consts::LOG2_E; - // let log10_e: f16 = consts::LOG10_E; - // let ln_2: f16 = consts::LN_2; - // let ln_10: f16 = consts::LN_10; - assert_approx_eq!(frac_pi_2, pi / 2f16); - assert_approx_eq!(frac_pi_3, pi / 3f16); - assert_approx_eq!(frac_pi_4, pi / 4f16); - assert_approx_eq!(frac_pi_6, pi / 6f16); - assert_approx_eq!(frac_pi_8, pi / 8f16); - assert_approx_eq!(frac_1_pi, 1f16 / pi); - assert_approx_eq!(frac_2_pi, 2f16 / pi); - // assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt()); - // assert_approx_eq!(sqrt2, 2f16.sqrt()); - // assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt()); - // assert_approx_eq!(log2_e, e.log2()); - // assert_approx_eq!(log10_e, e.log10()); - // assert_approx_eq!(ln_2, 2f16.ln()); - // assert_approx_eq!(ln_10, 10f16.ln()); + assert_approx_eq!(frac_pi_2, pi / 2f16, TOL_0); + assert_approx_eq!(frac_pi_3, pi / 3f16, TOL_0); + assert_approx_eq!(frac_pi_4, pi / 4f16, TOL_0); + assert_approx_eq!(frac_pi_6, pi / 6f16, TOL_0); + assert_approx_eq!(frac_pi_8, pi / 8f16, TOL_0); + assert_approx_eq!(frac_1_pi, 1f16 / pi, TOL_0); + assert_approx_eq!(frac_2_pi, 2f16 / pi, TOL_0); + + #[cfg(reliable_f16_math)] + { + let frac_2_sqrtpi: f16 = consts::FRAC_2_SQRT_PI; + let sqrt2: f16 = consts::SQRT_2; + let frac_1_sqrt2: f16 = consts::FRAC_1_SQRT_2; + let e: f16 = consts::E; + let log2_e: f16 = consts::LOG2_E; + let log10_e: f16 = consts::LOG10_E; + let ln_2: f16 = consts::LN_2; + let ln_10: f16 = consts::LN_10; + + assert_approx_eq!(frac_2_sqrtpi, 2f16 / pi.sqrt(), TOL_0); + assert_approx_eq!(sqrt2, 2f16.sqrt(), TOL_0); + assert_approx_eq!(frac_1_sqrt2, 1f16 / 2f16.sqrt(), TOL_0); + assert_approx_eq!(log2_e, e.log2(), TOL_0); + assert_approx_eq!(log10_e, e.log10(), TOL_0); + assert_approx_eq!(ln_2, 2f16.ln(), TOL_0); + assert_approx_eq!(ln_10, 10f16.ln(), TOL_0); + } } #[test] @@ -386,10 +780,10 @@ fn test_float_bits_conv() { assert_eq!((12.5f16).to_bits(), 0x4a40); assert_eq!((1337f16).to_bits(), 0x6539); assert_eq!((-14.25f16).to_bits(), 0xcb20); - assert_approx_eq!(f16::from_bits(0x3c00), 1.0); - assert_approx_eq!(f16::from_bits(0x4a40), 12.5); - assert_approx_eq!(f16::from_bits(0x6539), 1337.0); - assert_approx_eq!(f16::from_bits(0xcb20), -14.25); + assert_approx_eq!(f16::from_bits(0x3c00), 1.0, TOL_0); + assert_approx_eq!(f16::from_bits(0x4a40), 12.5, TOL_0); + assert_approx_eq!(f16::from_bits(0x6539), 1337.0, TOL_P4); + assert_approx_eq!(f16::from_bits(0xcb20), -14.25, TOL_0); // Check that NaNs roundtrip their bits regardless of signaling-ness let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index c82228fca4bc..9f4d244b5479 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -155,7 +155,7 @@ mod break_keyword {} /// const WORDS: &str = "hello convenience!"; /// ``` /// -/// `const` items looks remarkably similar to `static` items, which introduces some confusion as +/// `const` items look remarkably similar to `static` items, which introduces some confusion as /// to which one should be used at which times. To put it simply, constants are inlined wherever /// they're used, making using them identical to simply replacing the name of the `const` with its /// value. Static variables, on the other hand, point to a single location in memory, which all diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 67405d788bd9..93a74ef739b9 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -272,6 +272,7 @@ // // Language features: // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(min_exhaustive_patterns))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -299,7 +300,6 @@ #![feature(link_cfg)] #![feature(linkage)] #![feature(macro_metavar_expr_concat)] -#![feature(min_exhaustive_patterns)] #![feature(min_specialization)] #![feature(must_not_suspend)] #![feature(needs_panic_runtime)] @@ -586,7 +586,7 @@ pub mod net; pub mod num; pub mod os; pub mod panic; -#[unstable(feature = "core_pattern_types", issue = "none")] +#[unstable(feature = "core_pattern_types", issue = "123646")] pub mod pat; pub mod path; #[unstable(feature = "anonymous_pipe", issue = "127154")] diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index ba519afc62b0..1b0d7f3dbf2c 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -382,7 +382,7 @@ macro_rules! assert_approx_eq { let diff = (*a - *b).abs(); assert!( diff < $lim, - "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, actual {diff:?})", + "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", lim = $lim ); }}; diff --git a/library/std/src/sync/rwlock/tests.rs b/library/std/src/sync/rwlock/tests.rs index 12bb0fbf0503..37a2e41641ac 100644 --- a/library/std/src/sync/rwlock/tests.rs +++ b/library/std/src/sync/rwlock/tests.rs @@ -21,6 +21,10 @@ fn smoke() { } #[test] +// FIXME: On macOS we use a provenance-incorrect implementation and Miri +// catches that issue with a chance of around 1/1000. +// See for details. +#[cfg_attr(all(miri, target_os = "macos"), ignore)] fn frob() { const N: u32 = 10; const M: usize = if cfg!(miri) { 100 } else { 1000 }; diff --git a/library/std/src/sys/cmath.rs b/library/std/src/sys/cmath.rs index 99df503b82de..2997e908fa1b 100644 --- a/library/std/src/sys/cmath.rs +++ b/library/std/src/sys/cmath.rs @@ -28,6 +28,21 @@ extern "C" { pub fn lgamma_r(n: f64, s: &mut i32) -> f64; pub fn lgammaf_r(n: f32, s: &mut i32) -> f32; + pub fn acosf128(n: f128) -> f128; + pub fn asinf128(n: f128) -> f128; + pub fn atanf128(n: f128) -> f128; + pub fn atan2f128(a: f128, b: f128) -> f128; + pub fn cbrtf128(n: f128) -> f128; + pub fn coshf128(n: f128) -> f128; + pub fn expm1f128(n: f128) -> f128; + pub fn hypotf128(x: f128, y: f128) -> f128; + pub fn log1pf128(n: f128) -> f128; + pub fn sinhf128(n: f128) -> f128; + pub fn tanf128(n: f128) -> f128; + pub fn tanhf128(n: f128) -> f128; + pub fn tgammaf128(n: f128) -> f128; + pub fn lgammaf128_r(n: f128, s: &mut i32) -> f128; + cfg_if::cfg_if! { if #[cfg(not(all(target_os = "windows", target_env = "msvc", target_arch = "x86")))] { pub fn acosf(n: f32) -> f32; diff --git a/library/std/src/sys/pal/unix/process/process_unix/tests.rs b/library/std/src/sys/pal/unix/process/process_unix/tests.rs index e5e1f956bc35..f4d6ac6b4e34 100644 --- a/library/std/src/sys/pal/unix/process/process_unix/tests.rs +++ b/library/std/src/sys/pal/unix/process/process_unix/tests.rs @@ -24,7 +24,20 @@ fn exitstatus_display_tests() { // The purpose of this test is to test our string formatting, not our understanding of the wait // status magic numbers. So restrict these to Linux. if cfg!(target_os = "linux") { + #[cfg(any(target_arch = "mips", target_arch = "mips64"))] + t(0x0137f, "stopped (not terminated) by signal: 19 (SIGPWR)"); + + #[cfg(any(target_arch = "sparc", target_arch = "sparc64"))] + t(0x0137f, "stopped (not terminated) by signal: 19 (SIGCONT)"); + + #[cfg(not(any( + target_arch = "mips", + target_arch = "mips64", + target_arch = "sparc", + target_arch = "sparc64" + )))] t(0x0137f, "stopped (not terminated) by signal: 19 (SIGSTOP)"); + t(0x0ffff, "continued (WIFCONTINUED)"); } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 7d29d9a51721..0fa610eebb4e 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -3,12 +3,7 @@ use crate::mem::{self, ManuallyDrop}; use crate::num::NonZero; #[cfg(all(target_os = "linux", target_env = "gnu"))] use crate::sys::weak::dlsym; -#[cfg(any( - target_os = "solaris", - target_os = "illumos", - target_os = "nto", - target_os = "vxworks" -))] +#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))] use crate::sys::weak::weak; use crate::sys::{os, stack_overflow}; use crate::time::Duration; @@ -220,23 +215,16 @@ impl Thread { #[cfg(target_os = "vxworks")] pub fn set_name(name: &CStr) { // FIXME(libc): adding real STATUS, ERROR type eventually. - weak! { - fn taskNameSet( - libc::TASK_ID, *mut libc::c_char - ) -> libc::c_int + extern "C" { + fn taskNameSet(task_id: libc::TASK_ID, task_name: *mut libc::c_char) -> libc::c_int; } - // We can't assume taskNameSet is necessarily available. - // VX_TASK_NAME_LEN can be found set to 31, - // however older versions can be set to only 10. - // FIXME(vxworks): if the minimum supported VxWorks is >= 7, the maximum length can be changed to 31. - if let Some(f) = taskNameSet.get() { - const VX_TASK_NAME_LEN: usize = 10; + // VX_TASK_NAME_LEN is 31 in VxWorks 7. + const VX_TASK_NAME_LEN: usize = 31; - let name = truncate_cstr::<{ VX_TASK_NAME_LEN }>(name); - let status = unsafe { f(libc::taskIdSelf(), name.as_mut_ptr()) }; - debug_assert_eq!(res, libc::OK); - } + let mut name = truncate_cstr::<{ VX_TASK_NAME_LEN }>(name); + let res = unsafe { taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; + debug_assert_eq!(res, libc::OK); } #[cfg(any( @@ -489,9 +477,11 @@ pub fn available_parallelism() -> io::Result> { fn vxCpuEnabledGet() -> libc::cpuset_t; } - // always fetches a valid bitmask - let set = unsafe { vxCpuEnabledGet() }; - Ok(NonZero::new_unchecked(set.count_ones() as usize)) + // SAFETY: `vxCpuEnabledGet` always fetches a mask with at least one bit set + unsafe{ + let set = vxCpuEnabledGet(); + Ok(NonZero::new_unchecked(set.count_ones() as usize)) + } } else { // FIXME: implement on Redox, l4re Err(io::const_io_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform")) diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index 258a03474513..11316004412e 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/126298 +Last change is for: https://github.com/rust-lang/rust/pull/125642 diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 011c289d932d..d04e2fbeb785 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -89,6 +89,25 @@ fn main() { rustc_real }; + // Get the name of the crate we're compiling, if any. + let crate_name = parse_value_from_args(&orig_args, "--crate-name"); + + // When statically linking `std` into `rustc_driver`, remove `-C prefer-dynamic` + if env::var("RUSTC_LINK_STD_INTO_RUSTC_DRIVER").unwrap() == "1" + && crate_name == Some("rustc_driver") + && stage != "0" + { + if let Some(pos) = args.iter().enumerate().position(|(i, a)| { + a == "-C" && args.get(i + 1).map(|a| a == "prefer-dynamic").unwrap_or(false) + }) { + args.remove(pos); + args.remove(pos); + } + if let Some(pos) = args.iter().position(|a| a == "-Cprefer-dynamic") { + args.remove(pos); + } + } + let mut cmd = match env::var_os("RUSTC_WRAPPER_REAL") { Some(wrapper) if !wrapper.is_empty() => { let mut cmd = Command::new(wrapper); @@ -99,9 +118,6 @@ fn main() { }; cmd.args(&args).env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); - // Get the name of the crate we're compiling, if any. - let crate_name = parse_value_from_args(&orig_args, "--crate-name"); - if let Some(crate_name) = crate_name { if let Some(target) = env::var_os("RUSTC_TIME") { if target == "all" diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 81e591be6997..c5a1ab788016 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -194,6 +194,7 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { let supported_platforms = [ // tier 1 ("aarch64-unknown-linux-gnu", false), + ("aarch64-apple-darwin", false), ("i686-pc-windows-gnu", false), ("i686-pc-windows-msvc", false), ("i686-unknown-linux-gnu", false), @@ -202,7 +203,6 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool { ("x86_64-pc-windows-gnu", true), ("x86_64-pc-windows-msvc", true), // tier 2 with host tools - ("aarch64-apple-darwin", false), ("aarch64-pc-windows-msvc", false), ("aarch64-unknown-linux-musl", false), ("arm-unknown-linux-gnueabi", false), @@ -368,9 +368,7 @@ impl Step for Llvm { cfg.define("LLVM_PROFDATA_FILE", path); } - // Disable zstd to avoid a dependency on libzstd.so. - cfg.define("LLVM_ENABLE_ZSTD", "OFF"); - + // Libraries for ELF section compression. if !target.is_windows() { cfg.define("LLVM_ENABLE_ZLIB", "ON"); } else { @@ -824,6 +822,14 @@ fn configure_llvm(builder: &Builder<'_>, target: TargetSelection, cfg: &mut cmak } } + // Libraries for ELF section compression. + if builder.config.llvm_libzstd { + cfg.define("LLVM_ENABLE_ZSTD", "FORCE_ON"); + cfg.define("LLVM_USE_STATIC_ZSTD", "TRUE"); + } else { + cfg.define("LLVM_ENABLE_ZSTD", "OFF"); + } + if let Some(ref linker) = builder.config.llvm_use_linker { cfg.define("LLVM_USE_LINKER", linker); } diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index fde1693646a8..65d635c0bd69 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -212,11 +212,13 @@ impl Step for GenerateCopyright { let license_metadata = builder.ensure(CollectLicenseMetadata); // Temporary location, it will be moved to the proper one once it's accurate. - let dest = builder.out.join("COPYRIGHT.md"); + let dest = builder.out.join("COPYRIGHT.html"); let mut cmd = builder.tool_cmd(Tool::GenerateCopyright); cmd.env("LICENSE_METADATA", &license_metadata); cmd.env("DEST", &dest); + cmd.env("OUT_DIR", &builder.out); + cmd.env("CARGO", &builder.initial_cargo); cmd.run(builder); dest diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index c40aa718e7c6..597d7733abe7 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -520,6 +520,14 @@ impl Step for Miri { builder.ensure(compile::Std::new(target_compiler, host)); let host_sysroot = builder.sysroot(target_compiler); + // Miri has its own "target dir" for ui test dependencies. Make sure it gets cleared + // properly when rustc changes. Similar to `Builder::cargo`, we skip this in dry runs to + // make sure the relevant compiler has been set up properly. + if !builder.config.dry_run() { + let ui_test_dep_dir = builder.stage_out(host_compiler, Mode::ToolStd).join("miri_ui"); + builder.clear_if_dirty(&ui_test_dep_dir, &builder.rustc(host_compiler)); + } + // Run `cargo test`. // This is with the Miri crate, so it uses the host compiler. let mut cargo = tool::prepare_tool_cargo( diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 84c23c059e97..ccdeb442af4a 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1106,6 +1106,12 @@ impl<'a> Builder<'a> { StepDescription::run(v, self, paths); } + /// Returns if `std` should be statically linked into `rustc_driver`. + /// It's currently not done on `windows-gnu` due to linker bugs. + pub fn link_std_into_rustc_driver(&self, target: TargetSelection) -> bool { + !target.triple.ends_with("-windows-gnu") + } + /// Obtain a compiler at a given stage and for a given host (i.e., this is the target that the /// compiler will run on, *not* the target it will build code for). Explicitly does not take /// `Compiler` since all `Compiler` instances are meant to be obtained through this function, @@ -2162,9 +2168,17 @@ impl<'a> Builder<'a> { // When we build Rust dylibs they're all intended for intermediate // usage, so make sure we pass the -Cprefer-dynamic flag instead of // linking all deps statically into the dylib. - if matches!(mode, Mode::Std | Mode::Rustc) { + if matches!(mode, Mode::Std) { rustflags.arg("-Cprefer-dynamic"); } + if matches!(mode, Mode::Rustc) && !self.link_std_into_rustc_driver(target) { + rustflags.arg("-Cprefer-dynamic"); + } + + cargo.env( + "RUSTC_LINK_STD_INTO_RUSTC_DRIVER", + if self.link_std_into_rustc_driver(target) { "1" } else { "0" }, + ); // When building incrementally we default to a lower ThinLTO import limit // (unless explicitly specified otherwise). This will produce a somewhat diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 14beef20bad6..915fcb449e86 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -218,6 +218,7 @@ pub struct Config { pub llvm_thin_lto: bool, pub llvm_release_debuginfo: bool, pub llvm_static_stdcpp: bool, + pub llvm_libzstd: bool, /// `None` if `llvm_from_ci` is true and we haven't yet downloaded llvm. #[cfg(not(test))] llvm_link_shared: Cell>, @@ -878,6 +879,7 @@ define_config! { plugins: Option = "plugins", ccache: Option = "ccache", static_libstdcpp: Option = "static-libstdcpp", + libzstd: Option = "libzstd", ninja: Option = "ninja", targets: Option = "targets", experimental_targets: Option = "experimental-targets", @@ -1153,6 +1155,7 @@ impl Config { llvm_optimize: true, ninja_in_file: true, llvm_static_stdcpp: false, + llvm_libzstd: false, backtrace: true, rust_optimize: RustOptimize::Bool(true), rust_optimize_tests: true, @@ -1325,7 +1328,11 @@ impl Config { // Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path, // but not if `config.toml` hasn't been created. let mut toml = if !using_default_path || toml_path.exists() { - config.config = Some(toml_path.clone()); + config.config = Some(if cfg!(not(feature = "bootstrap-self-test")) { + toml_path.canonicalize().unwrap() + } else { + toml_path.clone() + }); get_toml(&toml_path) } else { config.config = None; @@ -1787,6 +1794,7 @@ impl Config { plugins, ccache, static_libstdcpp, + libzstd, ninja, targets, experimental_targets, @@ -1821,6 +1829,7 @@ impl Config { set(&mut config.llvm_thin_lto, thin_lto); set(&mut config.llvm_release_debuginfo, release_debuginfo); set(&mut config.llvm_static_stdcpp, static_libstdcpp); + set(&mut config.llvm_libzstd, libzstd); if let Some(v) = link_shared { config.llvm_link_shared.set(Some(v)); } @@ -1871,6 +1880,7 @@ impl Config { check_ci_llvm!(optimize_toml); check_ci_llvm!(thin_lto); check_ci_llvm!(release_debuginfo); + check_ci_llvm!(libzstd); check_ci_llvm!(targets); check_ci_llvm!(experimental_targets); check_ci_llvm!(clang_cl); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 453fb39327d6..2062d435bfc9 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -986,7 +986,8 @@ impl Build { } /// Execute a command and return its output. - /// This method should be used for all command executions in bootstrap. + /// Note: Ideally, you should use one of the BootstrapCommand::run* functions to + /// execute commands. They internally call this method. #[track_caller] fn run( &self, @@ -1057,20 +1058,28 @@ Executed at: {executed_at}"#, CommandOutput::did_not_start(stdout, stderr) } }; + + let fail = |message: &str| { + if self.is_verbose() { + println!("{message}"); + } else { + println!("Command has failed. Rerun with -v to see more details."); + } + exit!(1); + }; + if !output.is_success() { match command.failure_behavior { BehaviorOnFailure::DelayFail => { if self.fail_fast { - println!("{message}"); - exit!(1); + fail(&message); } let mut failures = self.delayed_failures.borrow_mut(); failures.push(message); } BehaviorOnFailure::Exit => { - println!("{message}"); - exit!(1); + fail(&message); } BehaviorOnFailure::Ignore => { // If failures are allowed, either the error has been printed already diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index b8f70fdf6a80..84dac25188e1 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -220,4 +220,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "For tarball sources, default value for `rust.channel` will be taken from `src/ci/channel` file.", }, + ChangeInfo { + change_id: 125642, + severity: ChangeSeverity::Info, + summary: "New option `llvm.libzstd` to control whether llvm is built with zstd support.", + }, ]; diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index bfe2f084552e..3f7f6214cf68 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -317,6 +317,12 @@ impl<'a> Tarball<'a> { channel::write_commit_hash_file(&self.overlay_dir, &info.sha); channel::write_commit_info_file(&self.overlay_dir, info); } + + // Add config file if present. + if let Some(config) = &self.builder.config.config { + self.add_renamed_file(config, &self.overlay_dir, "builder-config"); + } + for file in self.overlay.legal_and_readme() { self.builder.install(&self.builder.src.join(file), &self.overlay_dir, 0o644); } diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 4aa1a3ccc2a5..61e9694f1e2a 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -62,6 +62,10 @@ COPY host-x86_64/dist-x86_64-linux/build-clang.sh /tmp/ RUN ./build-clang.sh ENV CC=clang CXX=clang++ +# rustc's LLVM needs zstd. +COPY scripts/zstd.sh /tmp/ +RUN ./zstd.sh + COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh @@ -79,6 +83,7 @@ ENV RUST_CONFIGURE_ARGS \ --set target.x86_64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ --set llvm.thin-lto=true \ --set llvm.ninja=false \ + --set llvm.libzstd=true \ --set rust.jemalloc \ --set rust.use-lld=true \ --set rust.lto=thin \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile index 9025e9bb0a3a..19683317126a 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile @@ -17,6 +17,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ pkg-config \ xz-utils \ mingw-w64 \ + zlib1g-dev \ + libzstd-dev \ && rm -rf /var/lib/apt/lists/* COPY scripts/sccache.sh /scripts/ diff --git a/src/ci/docker/scripts/fuchsia-test-runner.py b/src/ci/docker/scripts/fuchsia-test-runner.py index 00269e68422d..a5458b8645d7 100755 --- a/src/ci/docker/scripts/fuchsia-test-runner.py +++ b/src/ci/docker/scripts/fuchsia-test-runner.py @@ -571,6 +571,19 @@ class TestEnvironment: ) # Start repository server + # Note that we must first enable the repository server daemon. + check_call_with_logging( + [ + ffx_path, + "config", + "set", + "repository.server.enabled", + "true", + ], + env=ffx_env, + stdout_handler=self.subprocess_logger.debug, + stderr_handler=self.subprocess_logger.debug, + ) check_call_with_logging( [ ffx_path, diff --git a/src/ci/docker/scripts/zstd.sh b/src/ci/docker/scripts/zstd.sh new file mode 100755 index 000000000000..a3d37ccc3112 --- /dev/null +++ b/src/ci/docker/scripts/zstd.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -ex + +hide_output() { + set +x + on_err=" +echo ERROR: An error was encountered with the build. +cat /tmp/zstd_build.log +exit 1 +" + trap "$on_err" ERR + bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & + PING_LOOP_PID=$! + "$@" &> /tmp/zstd_build.log + trap - ERR + kill $PING_LOOP_PID + rm /tmp/zstd_build.log + set -x +} + +ZSTD=1.5.6 +curl -L https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz | tar xzf - + +cd zstd-$ZSTD +CFLAGS=-fPIC hide_output make -j$(nproc) VERBOSE=1 +hide_output make install + +cd .. +rm -rf zstd-$ZSTD diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 72ab8ab5ce9b..fa23e3e414d9 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -96,9 +96,7 @@ This modifier translates to `--whole-archive` for `ld`-like linkers, to `/WHOLEARCHIVE` for `link.exe`, and to `-force_load` for `ld64`. The modifier does nothing for linkers that don't support it. -The default for this modifier is `-whole-archive`. \ -NOTE: The default may currently be different in some cases for backward compatibility, -but it is not guaranteed. If you need whole archive semantics use `+whole-archive` explicitly. +The default for this modifier is `-whole-archive`. ### Linking modifiers: `bundle` diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 7fd1808a1f02..bd12172ecbb4 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -33,6 +33,7 @@ All tier 1 targets with host tools support the full standard library. target | notes -------|------- `aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+) +[`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) `i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+, Windows Server 2016+) [^x86_32-floats-return-ABI] `i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+) [^x86_32-floats-return-ABI] `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+) [^x86_32-floats-return-ABI] @@ -86,7 +87,6 @@ so Rustup may install the documentation for a similar tier 1 target instead. target | notes -------|------- -[`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) `aarch64-pc-windows-msvc` | ARM64 Windows MSVC `aarch64-unknown-linux-musl` | ARM64 Linux with musl 1.2.3 `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2, glibc 2.17) @@ -282,7 +282,7 @@ target | std | host | notes [`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | Armv7-A Linux with uClibc, hardfloat `armv7-unknown-freebsd` | ✓ | ✓ | Armv7-A FreeBSD [`armv7-unknown-netbsd-eabihf`](platform-support/netbsd.md) | ✓ | ✓ | Armv7-A NetBSD w/hard-float -[`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ? | | Armv7-A for VxWorks +[`armv7-wrs-vxworks-eabihf`](platform-support/vxworks.md) | ✓ | | Armv7-A for VxWorks [`armv7a-kmc-solid_asp3-eabi`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3 [`armv7a-kmc-solid_asp3-eabihf`](platform-support/kmc-solid.md) | ✓ | | ARM SOLID with TOPPERS/ASP3, hardfloat [`armv7a-none-eabihf`](platform-support/arm-none-eabi.md) | * | | Bare Armv7-A, hardfloat @@ -308,7 +308,7 @@ target | std | host | notes `i686-uwp-windows-gnu` | ✓ | | [^x86_32-floats-return-ABI] `i686-uwp-windows-msvc` | ✓ | | [^x86_32-floats-return-ABI] [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 32-bit Windows 7 support [^x86_32-floats-return-ABI] -[`i686-wrs-vxworks`](platform-support/vxworks.md) | ? | | [^x86_32-floats-return-ABI] +[`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [^x86_32-floats-return-ABI] [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) `mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.3 @@ -334,13 +334,13 @@ target | std | host | notes `powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.3 [`powerpc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD 32-bit powerpc systems [`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | | -[`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ? | | -[`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ? | | +[`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | | +[`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | `powerpc64-unknown-freebsd` | ✓ | ✓ | PPC64 FreeBSD (ELFv1 and ELFv2) `powerpc64le-unknown-freebsd` | | | PPC64LE FreeBSD `powerpc-unknown-freebsd` | | | PowerPC FreeBSD `powerpc64-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3 -`powerpc64-wrs-vxworks` | ? | | +[`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | `powerpc64le-unknown-linux-musl` | ? | | 64-bit PowerPC Linux with musl 1.2.3, Little Endian [`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64 [`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer) @@ -383,7 +383,7 @@ target | std | host | notes `x86_64-uwp-windows-gnu` | ✓ | | `x86_64-uwp-windows-msvc` | ✓ | | [`x86_64-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ | | 64-bit Windows 7 support -[`x86_64-wrs-vxworks`](platform-support/vxworks.md) | ? | | +[`x86_64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`x86_64h-apple-darwin`](platform-support/x86_64h-apple-darwin.md) | ✓ | ✓ | macOS with late-gen Intel (at least Haswell) [`x86_64-unknown-linux-none`](platform-support/x86_64-unknown-linux-none.md) | * | | 64-bit Linux with no libc [`xtensa-esp32-none-elf`](platform-support/xtensa.md) | * | | Xtensa ESP32 diff --git a/src/doc/rustc/src/platform-support/apple-darwin.md b/src/doc/rustc/src/platform-support/apple-darwin.md index 0fb86949a4b7..c3a7b81f411e 100644 --- a/src/doc/rustc/src/platform-support/apple-darwin.md +++ b/src/doc/rustc/src/platform-support/apple-darwin.md @@ -5,9 +5,6 @@ Apple macOS targets. **Tier: 1** - `x86_64-apple-darwin`: macOS on 64-bit x86. - -**Tier: 2 (with Host Tools)** - - `aarch64-apple-darwin`: macOS on ARM64 (M1-family or later Apple Silicon CPUs). ## Target maintainers diff --git a/src/doc/rustc/src/platform-support/vxworks.md b/src/doc/rustc/src/platform-support/vxworks.md index c0a818558e0b..f1860346c8f2 100644 --- a/src/doc/rustc/src/platform-support/vxworks.md +++ b/src/doc/rustc/src/platform-support/vxworks.md @@ -12,6 +12,7 @@ Target triplets available: - `i686-wrs-vxworks` - `armv7-wrs-vxworks-eabihf` - `powerpc-wrs-vxworks` +- `powerpc64-wrs-vxworks` - `powerpc-wrs-vxworks-spe` ## Target maintainers @@ -20,6 +21,12 @@ Target triplets available: ## Requirements +### OS version + +The minimum supported version is VxWorks 7. + +## Building + Rust for each target can be cross-compiled with its specific target vsb configuration. Std support is added but not yet fully tested. ## Building the target @@ -36,6 +43,7 @@ target = [ "i686-wrs-vxworks", "armv7-wrs-vxworks-eabihf", "powerpc-wrs-vxworks", + "powerpc64-wrs-vxworks", "powerpc-wrs-vxworks-spe", ] ``` diff --git a/src/doc/unstable-book/src/language-features/rustc-private.md b/src/doc/unstable-book/src/language-features/rustc-private.md new file mode 100644 index 000000000000..97fce5980e40 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/rustc-private.md @@ -0,0 +1,11 @@ +# `rustc_private` + +The tracking issue for this feature is: [#27812] + +[#27812]: https://github.com/rust-lang/rust/issues/27812 + +------------------------ + +This feature allows access to unstable internal compiler crates. + +Additionally it changes the linking behavior of crates which have this feature enabled. It will prevent linking to a dylib if there's a static variant of it already statically linked into another dylib dependency. This is required to successfully link to `rustc_driver`. diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index 227695cdadd5..e8f9dee07d3e 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -56,7 +56,7 @@ class StdStringProvider(printer_base): self._valobj = valobj vec = valobj["vec"] self._length = int(vec["len"]) - self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"]) + self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["inner"]["ptr"]) def to_string(self): return self._data_ptr.lazy_string(encoding="utf-8", length=self._length) @@ -74,7 +74,7 @@ class StdOsStringProvider(printer_base): vec = buf[ZERO_FIELD] if is_windows else buf self._length = int(vec["len"]) - self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["ptr"]) + self._data_ptr = unwrap_unique_or_non_null(vec["buf"]["inner"]["ptr"]) def to_string(self): return self._data_ptr.lazy_string(encoding="utf-8", length=self._length) @@ -96,6 +96,7 @@ class StdStrProvider(printer_base): def display_hint(): return "string" + def _enumerate_array_elements(element_ptrs): for (i, element_ptr) in enumerate(element_ptrs): key = "[{}]".format(i) @@ -112,6 +113,7 @@ def _enumerate_array_elements(element_ptrs): yield key, element + class StdSliceProvider(printer_base): def __init__(self, valobj): self._valobj = valobj @@ -130,11 +132,14 @@ class StdSliceProvider(printer_base): def display_hint(): return "array" + class StdVecProvider(printer_base): def __init__(self, valobj): self._valobj = valobj self._length = int(valobj["len"]) - self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) + self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["inner"]["ptr"]) + ptr_ty = gdb.Type.pointer(valobj.type.template_argument(0)) + self._data_ptr = self._data_ptr.reinterpret_cast(ptr_ty) def to_string(self): return "Vec(size={})".format(self._length) @@ -155,11 +160,13 @@ class StdVecDequeProvider(printer_base): self._head = int(valobj["head"]) self._size = int(valobj["len"]) # BACKCOMPAT: rust 1.75 - cap = valobj["buf"]["cap"] + cap = valobj["buf"]["inner"]["cap"] if cap.type.code != gdb.TYPE_CODE_INT: cap = cap[ZERO_FIELD] self._cap = int(cap) - self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["ptr"]) + self._data_ptr = unwrap_unique_or_non_null(valobj["buf"]["inner"]["ptr"]) + ptr_ty = gdb.Type.pointer(valobj.type.template_argument(0)) + self._data_ptr = self._data_ptr.reinterpret_cast(ptr_ty) def to_string(self): return "VecDeque(size={})".format(self._size) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index c6330117380b..8750d7682d13 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -389,11 +389,11 @@ class StdVecSyntheticProvider: def update(self): # type: () -> None self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() - self.buf = self.valobj.GetChildMemberWithName("buf") + self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName("inner") self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr")) - self.element_type = self.data_ptr.GetType().GetPointeeType() + self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) self.element_type_size = self.element_type.GetByteSize() def has_children(self): @@ -474,7 +474,7 @@ class StdVecDequeSyntheticProvider: # type: () -> None self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned() self.size = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() - self.buf = self.valobj.GetChildMemberWithName("buf") + self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName("inner") cap = self.buf.GetChildMemberWithName("cap") if cap.GetType().num_fields == 1: cap = cap.GetChildAtIndex(0) @@ -482,7 +482,7 @@ class StdVecDequeSyntheticProvider: self.data_ptr = unwrap_unique_or_non_null(self.buf.GetChildMemberWithName("ptr")) - self.element_type = self.data_ptr.GetType().GetPointeeType() + self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) self.element_type_size = self.element_type.GetByteSize() def has_children(self): diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis index da307809f7b0..49d82dfad820 100644 --- a/src/etc/natvis/liballoc.natvis +++ b/src/etc/natvis/liballoc.natvis @@ -4,10 +4,10 @@ {{ len={len} }} len - buf.cap.__0 + buf.inner.cap.__0 len - buf.ptr.pointer.pointer + ($T1*)buf.inner.ptr.pointer.pointer @@ -15,7 +15,7 @@ {{ len={len} }} len - buf.cap.__0 + buf.inner.cap.__0 len @@ -23,7 +23,7 @@ - buf.ptr.pointer.pointer[(i + head) % buf.cap.__0] + (($T1*)buf.inner.ptr.pointer.pointer)[(i + head) % buf.inner.cap.__0] i = i + 1 @@ -41,17 +41,17 @@ - {(char*)vec.buf.ptr.pointer.pointer,[vec.len]s8} - (char*)vec.buf.ptr.pointer.pointer,[vec.len]s8 + {(char*)vec.buf.inner.ptr.pointer.pointer,[vec.len]s8} + (char*)vec.buf.inner.ptr.pointer.pointer,[vec.len]s8 vec.len - vec.buf.cap.__0 + vec.buf.inner.cap.__0 - {(char*)vec.buf.ptr.pointer.pointer,[vec.len]s8} + {(char*)vec.buf.inner.ptr.pointer.pointer,[vec.len]s8} vec.len - (char*)vec.buf.ptr.pointer.pointer + (char*)vec.buf.inner.ptr.pointer.pointer diff --git a/src/etc/natvis/libstd.natvis b/src/etc/natvis/libstd.natvis index 4371b9953181..4719a479c476 100644 --- a/src/etc/natvis/libstd.natvis +++ b/src/etc/natvis/libstd.natvis @@ -104,14 +104,14 @@ - {(char*)inner.inner.bytes.buf.ptr.pointer.pointer,[inner.inner.bytes.len]} + {(char*)inner.inner.bytes.buf.inner.ptr.pointer.pointer,[inner.inner.bytes.len]} - {(char*)inner.inner.bytes.buf.ptr.pointer.pointer,[inner.inner.bytes.len]} + {(char*)inner.inner.bytes.buf.inner.ptr.pointer.pointer,[inner.inner.bytes.len]} inner.inner.bytes.len - (char*)inner.inner.bytes.buf.ptr.pointer.pointer + (char*)inner.inner.bytes.buf.inner.ptr.pointer.pointer diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 4850500a1bfa..ff5c16f2b3e9 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2446,6 +2446,10 @@ impl Impl { .map(|did| tcx.provided_trait_methods(did).map(|meth| meth.name).collect()) .unwrap_or_default() } + + pub(crate) fn is_negative_trait_impl(&self) -> bool { + matches!(self.polarity, ty::ImplPolarity::Negative) + } } #[derive(Clone, Debug)] diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index e4549796b3e8..9c7a9f8467f5 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -362,9 +362,10 @@ impl Options { } let color = config::parse_color(early_dcx, matches); - let config::JsonConfig { json_rendered, json_unused_externs, .. } = + let config::JsonConfig { json_rendered, json_unused_externs, json_color, .. } = config::parse_json(early_dcx, matches); - let error_format = config::parse_error_format(early_dcx, matches, color, json_rendered); + let error_format = + config::parse_error_format(early_dcx, matches, color, json_color, json_rendered); let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_default(); let codegen_options = CodegenOptions::build(early_dcx, matches); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index fea31e7ecbc9..08a4a3f3fb26 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -138,8 +138,8 @@ pub(crate) fn new_dcx( false, ); let emitter: Box = match error_format { - ErrorOutputType::HumanReadable(kind) => { - let (short, color_config) = kind.unzip(); + ErrorOutputType::HumanReadable(kind, color_config) => { + let short = kind.short(); Box::new( HumanEmitter::new(stderr_destination(color_config), fallback_bundle) .sm(source_map.map(|sm| sm as _)) @@ -150,7 +150,7 @@ pub(crate) fn new_dcx( .ui_testing(unstable_opts.ui_testing), ) } - ErrorOutputType::Json { pretty, json_rendered } => { + ErrorOutputType::Json { pretty, json_rendered, color_config } => { let source_map = source_map.unwrap_or_else(|| { Lrc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty())) }); @@ -161,6 +161,7 @@ pub(crate) fn new_dcx( fallback_bundle, pretty, json_rendered, + color_config, ) .ui_testing(unstable_opts.ui_testing) .diagnostic_width(diagnostic_width) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 41eb142dd1e2..08d6a5a52b21 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -422,8 +422,8 @@ fn run_test( path_for_rustdoc.to_str().expect("target path must be valid unicode") } }); - if let ErrorOutputType::HumanReadable(kind) = rustdoc_options.error_format { - let (short, color_config) = kind.unzip(); + if let ErrorOutputType::HumanReadable(kind, color_config) = rustdoc_options.error_format { + let short = kind.short(); if short { compiler.arg("--error-format").arg("short"); diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index b5ab6a35fdb9..6357cfee141f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1285,9 +1285,8 @@ impl clean::Impl { f.write_str(" ")?; if let Some(ref ty) = self.trait_ { - match self.polarity { - ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {} - ty::ImplPolarity::Negative => write!(f, "!")?, + if self.is_negative_trait_impl() { + write!(f, "!")?; } ty.print(cx).fmt(f)?; write!(f, " for ")?; diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 97d97808b931..bb8d39aaf1dc 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -924,6 +924,7 @@ pub(crate) struct TagIterator<'a, 'tcx> { data: &'a str, is_in_attribute_block: bool, extra: Option<&'a ExtraInfo<'tcx>>, + is_error: bool, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -950,13 +951,20 @@ struct Indices { impl<'a, 'tcx> TagIterator<'a, 'tcx> { pub(crate) fn new(data: &'a str, extra: Option<&'a ExtraInfo<'tcx>>) -> Self { - Self { inner: data.char_indices().peekable(), data, is_in_attribute_block: false, extra } + Self { + inner: data.char_indices().peekable(), + data, + is_in_attribute_block: false, + extra, + is_error: false, + } } - fn emit_error(&self, err: impl Into) { + fn emit_error(&mut self, err: impl Into) { if let Some(extra) = self.extra { extra.error_invalid_codeblock_attr(err); } + self.is_error = true; } fn skip_separators(&mut self) -> Option { @@ -1154,6 +1162,9 @@ impl<'a, 'tcx> Iterator for TagIterator<'a, 'tcx> { type Item = LangStringToken<'a>; fn next(&mut self) -> Option { + if self.is_error { + return None; + } let Some(start) = self.skip_separators() else { if self.is_in_attribute_block { self.emit_error("unclosed attribute block (`{}`): missing `}` at the end"); @@ -1342,14 +1353,15 @@ impl LangString { } }; - call(&mut TagIterator::new(string, extra)); + let mut tag_iter = TagIterator::new(string, extra); + call(&mut tag_iter); // ignore-foo overrides ignore if !ignores.is_empty() { data.ignore = Ignore::Some(ignores); } - data.rust &= !seen_custom_tag && (!seen_other_tags || seen_rust_tags); + data.rust &= !seen_custom_tag && (!seen_other_tags || seen_rust_tags) && !tag_iter.is_error; data } diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 971120d4991c..e490099a92e1 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -61,7 +61,7 @@ fn test_lang_string_parse() { ..Default::default() }); // error - t(LangString { original: "{rust}".into(), rust: true, ..Default::default() }); + t(LangString { original: "{rust}".into(), rust: false, ..Default::default() }); t(LangString { original: "{.rust}".into(), rust: true, @@ -233,7 +233,7 @@ fn test_lang_string_parse() { ..Default::default() }); // error - t(LangString { original: "{class=first=second}".into(), rust: true, ..Default::default() }); + t(LangString { original: "{class=first=second}".into(), rust: false, ..Default::default() }); // error t(LangString { original: "{class=first.second}".into(), @@ -261,7 +261,7 @@ fn test_lang_string_parse() { ..Default::default() }); // error - t(LangString { original: r#"{class=f"irst"}"#.into(), rust: true, ..Default::default() }); + t(LangString { original: r#"{class=f"irst"}"#.into(), rust: false, ..Default::default() }); } #[test] diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 9074e40a5361..7ce637d3ab42 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1780,20 +1780,23 @@ fn render_impl( let mut impl_items = Buffer::empty_from(w); let mut default_impl_items = Buffer::empty_from(w); + let impl_ = i.inner_impl(); - for trait_item in &i.inner_impl().items { - doc_impl_item( - &mut default_impl_items, - &mut impl_items, - cx, - trait_item, - if trait_.is_some() { &i.impl_item } else { parent }, - link, - render_mode, - false, - trait_, - rendering_params, - ); + if !impl_.is_negative_trait_impl() { + for trait_item in &impl_.items { + doc_impl_item( + &mut default_impl_items, + &mut impl_items, + cx, + trait_item, + if trait_.is_some() { &i.impl_item } else { parent }, + link, + render_mode, + false, + trait_, + rendering_params, + ); + } } fn render_default_items( @@ -1844,13 +1847,15 @@ fn render_impl( // We don't emit documentation for default items if they appear in the // Implementations on Foreign Types or Implementors sections. if rendering_params.show_default_items { - if let Some(t) = trait_ { + if let Some(t) = trait_ + && !impl_.is_negative_trait_impl() + { render_default_items( &mut default_impl_items, &mut impl_items, cx, t, - i.inner_impl(), + impl_, &i.impl_item, render_mode, rendering_params, @@ -1882,7 +1887,7 @@ fn render_impl( } if let Some(ref dox) = i.impl_item.opt_doc_value() { - if trait_.is_none() && i.inner_impl().items.is_empty() { + if trait_.is_none() && impl_.items.is_empty() { w.write_str( "
\
This impl block contains no items.
\ diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index d1e2b9978f7d..f807c3362c4c 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -95,7 +95,14 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> { } clean::ModuleItem(..) => { - if i.item_id.is_local() && i.visibility(self.tcx) != Some(Visibility::Public) { + if i.item_id.is_local() + && !is_item_reachable( + self.tcx, + self.is_json_output, + self.effective_visibilities, + i.item_id, + ) + { debug!("Stripper: stripping module {:?}", i.name); let old = mem::replace(&mut self.update_retained, false); let ret = strip_item(self.fold_item_recur(i)); diff --git a/src/llvm-project b/src/llvm-project index 57ae1a347405..ccf4c38bdd73 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 57ae1a3474057fead2c438928ed368b3740bf0ec +Subproject commit ccf4c38bdd73f1a37ec266c73bdaef80e39f8cf6 diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index a93ac6ccaf1d..999134a40909 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -634,7 +634,7 @@ pub struct Discriminant { /// hexadecimal, and underscores), making it unsuitable to be machine /// interpreted. /// - /// In some cases, when the value is to complex, this may be `"{ _ }"`. + /// In some cases, when the value is too complex, this may be `"{ _ }"`. /// When this occurs is unstable, and may change without notice. pub expr: String, /// The numerical value of the discriminant. Stored as a string due to diff --git a/src/tools/cargo b/src/tools/cargo index fa646583675d..0d8d22f83b06 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit fa646583675d7c140482bd906145c71b7fb4fc2b +Subproject commit 0d8d22f83b066503f6b2b755925197e959e58b4f diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml index 7afdd068a990..ce07290d1e1e 100644 --- a/src/tools/clippy/.cargo/config.toml +++ b/src/tools/clippy/.cargo/config.toml @@ -13,6 +13,13 @@ target-dir = "target" [unstable] binary-dep-depinfo = true +profile-rustflags = true [profile.dev] split-debuginfo = "unpacked" + +# Add back the containing directory of the packages we have to refer to using --manifest-path +[profile.dev.package.clippy_dev] +rustflags = ["--remap-path-prefix", "=clippy_dev"] +[profile.dev.package.lintcheck] +rustflags = ["--remap-path-prefix", "=lintcheck"] diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml index 06bf3b6fdbfa..0a0538490cce 100644 --- a/src/tools/clippy/.github/workflows/clippy.yml +++ b/src/tools/clippy/.github/workflows/clippy.yml @@ -25,6 +25,7 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 + RUSTFLAGS: -D warnings concurrency: # For a given workflow, if we push to the same PR, cancel all previous builds on that PR. @@ -47,25 +48,25 @@ jobs: # Run - name: Build - run: cargo build --tests --features deny-warnings,internal + run: cargo build --tests --features internal - name: Test - run: cargo test --features deny-warnings,internal + run: cargo test --features internal - name: Test clippy_lints - run: cargo test --features deny-warnings,internal + run: cargo test --features internal working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_utils - name: Test rustc_tools_util - run: cargo test --features deny-warnings + run: cargo test working-directory: rustc_tools_util - name: Test clippy_dev - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_dev - name: Test clippy-driver diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 1f4bec929182..10e18e84c89f 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -11,6 +11,7 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 CARGO_INCREMENTAL: 0 + RUSTFLAGS: -D warnings concurrency: # For a given workflow, if we push to the same branch, cancel all previous builds on that branch. @@ -85,34 +86,34 @@ jobs: # Run - name: Build - run: cargo build --tests --features deny-warnings,internal + run: cargo build --tests --features internal - name: Test if: matrix.host == 'x86_64-unknown-linux-gnu' - run: cargo test --features deny-warnings,internal + run: cargo test --features internal - name: Test if: matrix.host != 'x86_64-unknown-linux-gnu' - run: cargo test --features deny-warnings,internal -- --skip dogfood + run: cargo test --features internal -- --skip dogfood - name: Test clippy_lints - run: cargo test --features deny-warnings,internal + run: cargo test --features internal working-directory: clippy_lints - name: Test clippy_utils - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_utils - name: Test clippy_config - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_config - name: Test rustc_tools_util - run: cargo test --features deny-warnings + run: cargo test working-directory: rustc_tools_util - name: Test clippy_dev - run: cargo test --features deny-warnings + run: cargo test working-directory: clippy_dev - name: Test clippy-driver diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index 37f18a4c0874..cf0a8bde202a 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -16,6 +16,7 @@ on: env: RUST_BACKTRACE: 1 CARGO_INCREMENTAL: 0 + RUSTFLAGS: -D warnings jobs: clippy_dev: @@ -28,7 +29,7 @@ jobs: # Run - name: Build - run: cargo build --features deny-warnings + run: cargo build working-directory: clippy_dev - name: Test update_lints @@ -38,6 +39,8 @@ jobs: run: cargo dev fmt --check - name: Test cargo dev new lint + env: + RUSTFLAGS: -A unused-imports run: | cargo dev new_lint --name new_early_pass --pass early cargo dev new_lint --name new_late_pass --pass late diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 6a5139b6dc0b..3cbda0b38243 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -58,7 +58,7 @@ jobs: - name: Run lintcheck if: steps.cache-json.outputs.cache-hit != 'true' - run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml + run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload base JSON uses: actions/upload-artifact@v4 @@ -86,7 +86,7 @@ jobs: run: cargo build --manifest-path=lintcheck/Cargo.toml - name: Run lintcheck - run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml + run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml - name: Upload head JSON uses: actions/upload-artifact@v4 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 60c03b03d9be..fddc2fd994e8 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5830,6 +5830,7 @@ Released 2018-09-13 [`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use +[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push @@ -5998,6 +5999,7 @@ Released 2018-09-13 [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable +[`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok [`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index bb4dc97e748e..78409c7a09e2 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -30,7 +30,7 @@ color-print = "0.3.4" anstream = "0.6.0" [dev-dependencies] -ui_test = "0.24" +ui_test = "0.25" regex = "1.5.5" toml = "0.7.3" walkdir = "2.3" @@ -51,7 +51,6 @@ tokio = { version = "1", features = ["io-util"] } rustc_tools_util = "0.3.0" [features] -deny-warnings = ["clippy_lints/deny-warnings"] integration = ["tempfile"] internal = ["clippy_lints/internal", "tempfile"] diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index fb717a2c166d..e3d550b14662 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -364,7 +364,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat ## `await-holding-invalid-types` - +The list of types which may not be held across an await point. **Default Value:** `[]` @@ -668,6 +668,8 @@ crate. For example, `pub(crate)` items. ## `msrv` The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` +**Default Value:** `current version` + --- **Affected lints:** * [`allow_attributes`](https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes) @@ -862,6 +864,8 @@ The maximum number of lines a function or method can have The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. By default there is no limit +**Default Value:** `target_pointer_width * 2` + --- **Affected lints:** * [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref) diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index e1b2edc8a6ff..d5b28e253237 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +itertools = "0.12" rustc-semver = "1.1" serde = { version = "1.0", features = ["derive"] } toml = "0.7.3" @@ -13,9 +14,6 @@ toml = "0.7.3" [dev-dependencies] walkdir = "2.3" -[features] -deny-warnings = [] - [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 63140a36875d..4c2a8255d6b6 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -123,7 +123,8 @@ macro_rules! define_Conf { $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? - ($name:ident: $ty:ty = $default:expr), + $(#[lints($($for_lints:ident),* $(,)?)])? + $name:ident: $ty:ty = $default:expr, )*) => { /// Clippy lint configuration pub struct Conf { @@ -201,411 +202,91 @@ macro_rules! define_Conf { } pub fn get_configuration_metadata() -> Vec { - let mut sorted = vec![ - $( - { - let deprecation_reason = wrap_option!($($dep)?); - - ClippyConfiguration::new( - stringify!($name), - default_text!(defaults::$name() $(, $default_text)?), - concat!($($doc, '\n',)*), - deprecation_reason, - ) - }, - )+ - ]; - sorted.sort_by(|a, b| a.name.cmp(&b.name)); - sorted + vec![$( + ClippyConfiguration { + name: stringify!($name).replace('_', "-"), + default: default_text!(defaults::$name() $(, $default_text)?), + lints: &[$($(stringify!($for_lints)),*)?], + doc: concat!($($doc, '\n',)*), + deprecation_reason: wrap_option!($($dep)?) + }, + )*] } }; } define_Conf! { - /// Lint: ARITHMETIC_SIDE_EFFECTS. - /// - /// Suppress checking of the passed type names in all types of operations. - /// - /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. + /// Which crates to allow absolute paths from + #[lints(absolute_paths)] + absolute_paths_allowed_crates: FxHashSet = FxHashSet::default(), + /// The maximum number of segments a path can have before being linted, anything above this will + /// be linted. + #[lints(absolute_paths)] + absolute_paths_max_segments: u64 = 2, + /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block + #[lints(undocumented_unsafe_blocks)] + accept_comment_above_attributes: bool = true, + /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block + #[lints(undocumented_unsafe_blocks)] + accept_comment_above_statement: bool = true, + /// Don't lint when comparing the result of a modulo operation to zero. + #[lints(modulo_arithmetic)] + allow_comparison_to_zero: bool = true, + /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` + #[lints(dbg_macro)] + allow_dbg_in_tests: bool = false, + /// Whether `expect` should be allowed in test functions or `#[cfg(test)]` + #[lints(expect_used)] + allow_expect_in_tests: bool = false, + /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` + #[lints(uninlined_format_args)] + allow_mixed_uninlined_format_args: bool = true, + /// Whether to allow `r#""#` when `r""` can be used + #[lints(unnecessary_raw_string_hashes)] + allow_one_hash_in_raw_strings: bool = false, + /// Whether `panic` should be allowed in test functions or `#[cfg(test)]` + #[lints(panic)] + allow_panic_in_tests: bool = false, + /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` + #[lints(print_stderr, print_stdout)] + allow_print_in_tests: bool = false, + /// Whether to allow module inception if it's not public. + #[lints(module_inception)] + allow_private_module_inception: bool = false, + /// List of trait paths to ignore when checking renamed function parameters. /// /// #### Example /// /// ```toml - /// arithmetic-side-effects-allowed = ["SomeType", "AnotherType"] + /// allow-renamed-params-for = [ "std::convert::From" ] /// ``` /// /// #### Noteworthy /// - /// A type, say `SomeType`, listed in this configuration has the same behavior of - /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. - (arithmetic_side_effects_allowed: Vec = <_>::default()), - /// Lint: ARITHMETIC_SIDE_EFFECTS. - /// - /// Suppress checking of the passed type pair names in binary operations like addition or - /// multiplication. - /// - /// Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless - /// of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`. - /// - /// Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as - /// `["AnotherType", "SomeType"]`. - /// - /// #### Example - /// - /// ```toml - /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] - /// ``` - (arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default()), - /// Lint: ARITHMETIC_SIDE_EFFECTS. - /// - /// Suppress checking of the passed type names in unary operations like "negation" (`-`). - /// - /// #### Example - /// - /// ```toml - /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] - /// ``` - (arithmetic_side_effects_allowed_unary: Vec = <_>::default()), - /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN, NEEDLESS_PASS_BY_REF_MUT. - /// - /// 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, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, MANUAL_PATTERN_CHAR_COMPARISON, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, COLLAPSIBLE_MATCH. - /// - /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` - #[default_text = ""] - (msrv: Msrv = Msrv::empty()), - /// DEPRECATED LINT: BLACKLISTED_NAME. - /// - /// Use the Disallowed Names lint instead - #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)] - (blacklisted_names: Vec = Vec::new()), - /// Lint: COGNITIVE_COMPLEXITY. - /// - /// The maximum cognitive complexity a function can have - (cognitive_complexity_threshold: u64 = 25), - /// Lint: EXCESSIVE_NESTING. - /// - /// The maximum amount of nesting a block can reside in - (excessive_nesting_threshold: u64 = 0), - /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. - /// - /// Use the Cognitive Complexity lint instead. - #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] - (cyclomatic_complexity_threshold: u64 = 25), - /// Lint: DISALLOWED_NAMES. - /// - /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value - /// `".."` can be used as part of the list to indicate that the configured values should be appended to the + /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` + /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. - (disallowed_names: Vec = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()), - /// Lint: SEMICOLON_INSIDE_BLOCK. - /// - /// Whether to lint only if it's multiline. - (semicolon_inside_block_ignore_singleline: bool = false), - /// Lint: SEMICOLON_OUTSIDE_BLOCK. - /// - /// Whether to lint only if it's singleline. - (semicolon_outside_block_ignore_multiline: bool = false), - /// Lint: DOC_MARKDOWN. - /// - /// The list of words this lint should not consider as identifiers needing ticks. The value - /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the - /// default configuration of Clippy. By default, any configuration will replace the default value. For example: - /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. - /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. - (doc_valid_idents: FxHashSet = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()), - /// Lint: TOO_MANY_ARGUMENTS. - /// - /// The maximum number of argument a function or method can have - (too_many_arguments_threshold: u64 = 7), - /// Lint: TYPE_COMPLEXITY. - /// - /// The maximum complexity a type can have - (type_complexity_threshold: u64 = 250), - /// Lint: MANY_SINGLE_CHAR_NAMES. - /// - /// The maximum number of single char bindings a scope may have - (single_char_binding_names_threshold: u64 = 4), - /// Lint: BOXED_LOCAL, USELESS_VEC. - /// - /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap - (too_large_for_stack: u64 = 200), - /// Lint: ENUM_VARIANT_NAMES. - /// - /// The minimum number of enum variants for the lints about variant names to trigger - (enum_variant_name_threshold: u64 = 3), - /// Lint: STRUCT_FIELD_NAMES. - /// - /// The minimum number of struct fields for the lints about field names to trigger - (struct_field_name_threshold: u64 = 3), - /// Lint: LARGE_ENUM_VARIANT. - /// - /// The maximum size of an enum's variant to avoid box suggestion - (enum_variant_size_threshold: u64 = 200), - /// Lint: VERBOSE_BIT_MASK. - /// - /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' - (verbose_bit_mask_threshold: u64 = 1), - /// Lint: DECIMAL_LITERAL_REPRESENTATION. - /// - /// The lower bound for linting decimal literals - (literal_representation_threshold: u64 = 16384), - /// Lint: TRIVIALLY_COPY_PASS_BY_REF. - /// - /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by - /// reference. By default there is no limit - #[default_text = ""] - (trivial_copy_size_limit: Option = None), - /// Lint: LARGE_TYPES_PASSED_BY_VALUE. - /// - /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. - (pass_by_value_size_limit: u64 = 256), - /// Lint: TOO_MANY_LINES. - /// - /// The maximum number of lines a function or method can have - (too_many_lines_threshold: u64 = 100), - /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. - /// - /// The maximum allowed size for arrays on the stack - (array_size_threshold: u64 = 512_000), - /// Lint: LARGE_STACK_FRAMES. - /// - /// The maximum allowed stack size for functions in bytes - (stack_size_threshold: u64 = 512_000), - /// Lint: VEC_BOX. - /// - /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed - (vec_box_size_threshold: u64 = 4096), - /// Lint: TYPE_REPETITION_IN_BOUNDS. - /// - /// The maximum number of bounds a trait can have to be linted - (max_trait_bounds: u64 = 3), - /// Lint: STRUCT_EXCESSIVE_BOOLS. - /// - /// The maximum number of bool fields a struct can have - (max_struct_bools: u64 = 3), - /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. - /// - /// The maximum number of bool parameters a function can have - (max_fn_params_bools: u64 = 3), - /// Lint: WILDCARD_IMPORTS. - /// - /// Whether to allow certain wildcard imports (prelude, super in tests). - (warn_on_all_wildcard_imports: bool = false), - /// Lint: DISALLOWED_MACROS. - /// - /// The list of disallowed macros, written as fully qualified paths. - (disallowed_macros: Vec = Vec::new()), - /// Lint: DISALLOWED_METHODS. - /// - /// The list of disallowed methods, written as fully qualified paths. - (disallowed_methods: Vec = Vec::new()), - /// Lint: DISALLOWED_TYPES. - /// - /// The list of disallowed types, written as fully qualified paths. - (disallowed_types: Vec = Vec::new()), - /// Lint: UNREADABLE_LITERAL. - /// - /// Should the fraction of a decimal be linted to include separators. - (unreadable_literal_lint_fractions: bool = true), - /// Lint: UPPER_CASE_ACRONYMS. - /// - /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other - (upper_case_acronyms_aggressive: bool = false), - /// Lint: MANUAL_LET_ELSE. - /// - /// Whether the matches should be considered by the lint, and whether there should - /// be filtering for common types. - (matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes), - /// Lint: CARGO_COMMON_METADATA. - /// - /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. - (cargo_ignore_publish: bool = false), - /// Lint: NONSTANDARD_MACRO_BRACES. - /// - /// Enforce the named macros always use the braces specified. - /// - /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro - /// could be used with a full path two `MacroMatcher`s have to be added one with the full path - /// `crate_name::macro_name` and one with just the macro name. - (standard_macro_braces: Vec = Vec::new()), - /// Lint: MISSING_ENFORCED_IMPORT_RENAMES. - /// - /// The list of imports to always rename, a fully qualified path followed by the rename. - (enforced_import_renames: Vec = Vec::new()), - /// Lint: DISALLOWED_SCRIPT_IDENTS. - /// - /// The list of unicode scripts allowed to be used in the scope. - (allowed_scripts: Vec = vec!["Latin".to_string()]), - /// Lint: NON_SEND_FIELDS_IN_SEND_TY. - /// - /// Whether to apply the raw pointer heuristic to determine if a type is `Send`. - (enable_raw_pointer_heuristic_for_send: bool = true), - /// Lint: INDEX_REFUTABLE_SLICE. - /// - /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in - /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed. - /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. - (max_suggested_slice_pattern_length: u64 = 3), - /// Lint: AWAIT_HOLDING_INVALID_TYPE. - (await_holding_invalid_types: Vec = Vec::new()), - /// Lint: LARGE_INCLUDE_FILE. - /// - /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes - (max_include_file_size: u64 = 1_000_000), - /// Lint: EXPECT_USED. - /// - /// Whether `expect` should be allowed in test functions or `#[cfg(test)]` - (allow_expect_in_tests: bool = false), - /// Lint: UNWRAP_USED. - /// + #[lints(renamed_function_params)] + allow_renamed_params_for: Vec = + DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect(), /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` - (allow_unwrap_in_tests: bool = false), - /// Lint: PANIC. - /// - /// Whether `panic` should be allowed in test functions or `#[cfg(test)]` - (allow_panic_in_tests: bool = false), - /// Lint: DBG_MACRO. - /// - /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]` - (allow_dbg_in_tests: bool = false), - /// Lint: PRINT_STDOUT, PRINT_STDERR. - /// - /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]` - (allow_print_in_tests: bool = false), - /// Lint: USELESS_VEC. - /// + #[lints(unwrap_used)] + allow_unwrap_in_tests: bool = false, /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]` - (allow_useless_vec_in_tests: bool = false), - /// Lint: RESULT_LARGE_ERR. - /// - /// The maximum size of the `Err`-variant in a `Result` returned from a function - (large_error_threshold: u64 = 128), - /// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND, BORROW_INTERIOR_MUTABLE_CONST, DECLARE_INTERIOR_MUTABLE_CONST. - /// - /// A list of paths to types that should be treated as if they do not contain interior mutability - (ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()])), - /// Lint: UNINLINED_FORMAT_ARGS. - /// - /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` - (allow_mixed_uninlined_format_args: bool = true), - /// Lint: INDEXING_SLICING. - /// - /// Whether to suppress a restriction lint in constant code. In same - /// cases the restructured operation might not be unavoidable, as the - /// suggested counterparts are unavailable in constant code. This - /// configuration will cause restriction lints to trigger even - /// if no suggestion can be made. - (suppress_restriction_lint_in_const: bool = false), - /// Lint: MISSING_DOCS_IN_PRIVATE_ITEMS. - /// - /// Whether to **only** check for missing documentation in items visible within the current - /// crate. For example, `pub(crate)` items. - (missing_docs_in_crate_items: bool = false), - /// Lint: LARGE_FUTURES. - /// - /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint - (future_size_threshold: u64 = 16 * 1024), - /// Lint: UNNECESSARY_BOX_RETURNS. - /// - /// The byte size a `T` in `Box` can have, below which it triggers the `clippy::unnecessary_box` lint - (unnecessary_box_size: u64 = 128), - /// Lint: MODULE_INCEPTION. - /// - /// Whether to allow module inception if it's not public. - (allow_private_module_inception: bool = false), - /// Lint: MIN_IDENT_CHARS. - /// + #[lints(useless_vec)] + allow_useless_vec_in_tests: bool = false, + /// Additional dotfiles (files or directories starting with a dot) to allow + #[lints(path_ends_with_ext)] + allowed_dotfiles: Vec = Vec::default(), + /// A list of crate names to allow duplicates of + #[lints(multiple_crate_versions)] + allowed_duplicate_crates: FxHashSet = FxHashSet::default(), /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of /// the list to indicate, that the configured values should be appended to the default /// configuration of Clippy. By default, any configuration will replace the default value. - (allowed_idents_below_min_chars: FxHashSet = - DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect()), - /// Lint: MIN_IDENT_CHARS. - /// - /// Minimum chars an ident can have, anything below or equal to this will be linted. - (min_ident_chars_threshold: u64 = 1), - /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. - /// - /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block - (accept_comment_above_statement: bool = true), - /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS. - /// - /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block - (accept_comment_above_attributes: bool = true), - /// Lint: UNNECESSARY_RAW_STRING_HASHES. - /// - /// Whether to allow `r#""#` when `r""` can be used - (allow_one_hash_in_raw_strings: bool = false), - /// Lint: ABSOLUTE_PATHS. - /// - /// The maximum number of segments a path can have before being linted, anything above this will - /// be linted. - (absolute_paths_max_segments: u64 = 2), - /// Lint: ABSOLUTE_PATHS. - /// - /// Which crates to allow absolute paths from - (absolute_paths_allowed_crates: FxHashSet = FxHashSet::default()), - /// Lint: PATH_ENDS_WITH_EXT. - /// - /// Additional dotfiles (files or directories starting with a dot) to allow - (allowed_dotfiles: Vec = Vec::default()), - /// Lint: MULTIPLE_CRATE_VERSIONS. - /// - /// A list of crate names to allow duplicates of - (allowed_duplicate_crates: FxHashSet = FxHashSet::default()), - /// Lint: EXPLICIT_ITER_LOOP. - /// - /// Whether to recommend using implicit into iter for reborrowed values. - /// - /// #### Example - /// ```no_run - /// let mut vec = vec![1, 2, 3]; - /// let rmvec = &mut vec; - /// for _ in rmvec.iter() {} - /// for _ in rmvec.iter_mut() {} - /// ``` - /// - /// Use instead: - /// ```no_run - /// let mut vec = vec![1, 2, 3]; - /// let rmvec = &mut vec; - /// for _ in &*rmvec {} - /// for _ in &mut *rmvec {} - /// ``` - (enforce_iter_loop_reborrow: bool = false), - /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC. - /// - /// Whether to also run the listed lints on private items. - (check_private_items: bool = false), - /// Lint: PUB_UNDERSCORE_FIELDS. - /// - /// Lint "public" fields in a struct that are prefixed with an underscore based on their - /// exported visibility, or whether they are marked as "pub". - (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported), - /// Lint: MODULO_ARITHMETIC. - /// - /// Don't lint when comparing the result of a modulo operation to zero. - (allow_comparison_to_zero: bool = true), - /// Lint: WILDCARD_IMPORTS. - /// - /// List of path segments allowed to have wildcard imports. - /// - /// #### Example - /// - /// ```toml - /// allowed-wildcard-imports = [ "utils", "common" ] - /// ``` - /// - /// #### Noteworthy - /// - /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. - /// 2. Paths with any segment that containing the word 'prelude' - /// are already allowed by default. - (allowed_wildcard_imports: FxHashSet = FxHashSet::default()), - /// Lint: MODULE_NAME_REPETITIONS. - /// + #[lints(min_ident_chars)] + allowed_idents_below_min_chars: FxHashSet = + DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect(), /// List of prefixes to allow when determining whether an item's name ends with the module's name. /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`), /// then don't emit a warning. @@ -623,28 +304,343 @@ define_Conf! { /// `TryInto` will also be included) /// - Use `".."` as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value - (allowed_prefixes: Vec = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()), - /// Lint: RENAMED_FUNCTION_PARAMS. - /// - /// List of trait paths to ignore when checking renamed function parameters. + #[lints(module_name_repetitions)] + allowed_prefixes: Vec = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect(), + /// The list of unicode scripts allowed to be used in the scope. + #[lints(disallowed_script_idents)] + allowed_scripts: Vec = vec!["Latin".to_string()], + /// List of path segments allowed to have wildcard imports. /// /// #### Example /// /// ```toml - /// allow-renamed-params-for = [ "std::convert::From" ] + /// allowed-wildcard-imports = [ "utils", "common" ] /// ``` /// /// #### Noteworthy /// - /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr` - /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the - /// default configuration of Clippy. By default, any configuration will replace the default value. - (allow_renamed_params_for: Vec = - DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()), - /// Lint: MACRO_METAVARS_IN_UNSAFE. + /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`. + /// 2. Paths with any segment that containing the word 'prelude' + /// are already allowed by default. + #[lints(wildcard_imports)] + allowed_wildcard_imports: FxHashSet = FxHashSet::default(), + /// Suppress checking of the passed type names in all types of operations. /// + /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead. + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed = ["SomeType", "AnotherType"] + /// ``` + /// + /// #### Noteworthy + /// + /// A type, say `SomeType`, listed in this configuration has the same behavior of + /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`. + #[lints(arithmetic_side_effects)] + arithmetic_side_effects_allowed: Vec = <_>::default(), + /// Suppress checking of the passed type pair names in binary operations like addition or + /// multiplication. + /// + /// Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless + /// of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`. + /// + /// Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as + /// `["AnotherType", "SomeType"]`. + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]] + /// ``` + #[lints(arithmetic_side_effects)] + arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default(), + /// Suppress checking of the passed type names in unary operations like "negation" (`-`). + /// + /// #### Example + /// + /// ```toml + /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"] + /// ``` + #[lints(arithmetic_side_effects)] + arithmetic_side_effects_allowed_unary: Vec = <_>::default(), + /// The maximum allowed size for arrays on the stack + #[lints(large_const_arrays, large_stack_arrays)] + array_size_threshold: u64 = 512_000, + /// Suppress lints whenever the suggested change would cause breakage for other crates. + #[lints( + box_collection, + enum_variant_names, + large_types_passed_by_value, + linkedlist, + needless_pass_by_ref_mut, + option_option, + rc_buffer, + rc_mutex, + redundant_allocation, + single_call_fn, + trivially_copy_pass_by_ref, + unnecessary_box_returns, + unnecessary_wraps, + unused_self, + upper_case_acronyms, + vec_box, + wrong_self_convention, + )] + avoid_breaking_exported_api: bool = true, + /// The list of types which may not be held across an await point. + #[lints(await_holding_invalid_type)] + await_holding_invalid_types: Vec = Vec::new(), + /// DEPRECATED LINT: BLACKLISTED_NAME. + /// + /// Use the Disallowed Names lint instead + #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)] + blacklisted_names: Vec = Vec::new(), + /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. + #[lints(cargo_common_metadata)] + cargo_ignore_publish: bool = false, + /// Whether to also run the listed lints on private items. + #[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)] + check_private_items: bool = false, + /// The maximum cognitive complexity a function can have + #[lints(cognitive_complexity)] + cognitive_complexity_threshold: u64 = 25, + /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. + /// + /// Use the Cognitive Complexity lint instead. + #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] + cyclomatic_complexity_threshold: u64 = 25, + /// The list of disallowed macros, written as fully qualified paths. + #[lints(disallowed_macros)] + disallowed_macros: Vec = Vec::new(), + /// The list of disallowed methods, written as fully qualified paths. + #[lints(disallowed_methods)] + disallowed_methods: Vec = Vec::new(), + /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value + /// `".."` can be used as part of the list to indicate that the configured values should be appended to the + /// default configuration of Clippy. By default, any configuration will replace the default value. + #[lints(disallowed_names)] + disallowed_names: Vec = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(), + /// The list of disallowed types, written as fully qualified paths. + #[lints(disallowed_types)] + disallowed_types: Vec = Vec::new(), + /// The list of words this lint should not consider as identifiers needing ticks. The value + /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the + /// default configuration of Clippy. By default, any configuration will replace the default value. For example: + /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. + /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. + #[lints(doc_markdown)] + doc_valid_idents: FxHashSet = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(), + /// Whether to apply the raw pointer heuristic to determine if a type is `Send`. + #[lints(non_send_fields_in_send_ty)] + enable_raw_pointer_heuristic_for_send: bool = true, + /// Whether to recommend using implicit into iter for reborrowed values. + /// + /// #### Example + /// ```no_run + /// let mut vec = vec![1, 2, 3]; + /// let rmvec = &mut vec; + /// for _ in rmvec.iter() {} + /// for _ in rmvec.iter_mut() {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// let mut vec = vec![1, 2, 3]; + /// let rmvec = &mut vec; + /// for _ in &*rmvec {} + /// for _ in &mut *rmvec {} + /// ``` + #[lints(explicit_iter_loop)] + enforce_iter_loop_reborrow: bool = false, + /// The list of imports to always rename, a fully qualified path followed by the rename. + #[lints(missing_enforced_import_renames)] + enforced_import_renames: Vec = Vec::new(), + /// The minimum number of enum variants for the lints about variant names to trigger + #[lints(enum_variant_names)] + enum_variant_name_threshold: u64 = 3, + /// The maximum size of an enum's variant to avoid box suggestion + #[lints(large_enum_variant)] + enum_variant_size_threshold: u64 = 200, + /// The maximum amount of nesting a block can reside in + #[lints(excessive_nesting)] + excessive_nesting_threshold: u64 = 0, + /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint + #[lints(large_futures)] + future_size_threshold: u64 = 16 * 1024, + /// A list of paths to types that should be treated as if they do not contain interior mutability + #[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)] + ignore_interior_mutability: Vec = Vec::from(["bytes::Bytes".into()]), + /// The maximum size of the `Err`-variant in a `Result` returned from a function + #[lints(result_large_err)] + large_error_threshold: u64 = 128, + /// The lower bound for linting decimal literals + #[lints(decimal_literal_representation)] + literal_representation_threshold: u64 = 16384, + /// Whether the matches should be considered by the lint, and whether there should + /// be filtering for common types. + #[lints(manual_let_else)] + matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes, + /// The maximum number of bool parameters a function can have + #[lints(fn_params_excessive_bools)] + max_fn_params_bools: u64 = 3, + /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes + #[lints(large_include_file)] + max_include_file_size: u64 = 1_000_000, + /// The maximum number of bool fields a struct can have + #[lints(struct_excessive_bools)] + max_struct_bools: u64 = 3, + /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in + /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed. + /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. + #[lints(index_refutable_slice)] + max_suggested_slice_pattern_length: u64 = 3, + /// The maximum number of bounds a trait can have to be linted + #[lints(type_repetition_in_bounds)] + max_trait_bounds: u64 = 3, + /// Minimum chars an ident can have, anything below or equal to this will be linted. + #[lints(min_ident_chars)] + min_ident_chars_threshold: u64 = 1, + /// Whether to **only** check for missing documentation in items visible within the current + /// crate. For example, `pub(crate)` items. + #[lints(missing_docs_in_private_items)] + missing_docs_in_crate_items: bool = false, + /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml` + #[default_text = "current version"] + #[lints( + allow_attributes, + allow_attributes_without_reason, + almost_complete_range, + approx_constant, + assigning_clones, + borrow_as_ptr, + cast_abs_to_unsigned, + checked_conversions, + cloned_instead_of_copied, + collapsible_match, + collapsible_str_replace, + deprecated_cfg_attr, + derivable_impls, + err_expect, + filter_map_next, + from_over_into, + if_then_some_else_none, + index_refutable_slice, + iter_kv_map, + legacy_numeric_constants, + manual_bits, + manual_c_str_literals, + manual_clamp, + manual_hash_one, + manual_is_ascii_check, + manual_let_else, + manual_non_exhaustive, + manual_pattern_char_comparison, + manual_range_contains, + manual_rem_euclid, + manual_retain, + manual_split_once, + manual_str_repeat, + manual_strip, + manual_try_fold, + map_clone, + map_unwrap_or, + match_like_matches_macro, + mem_replace_with_default, + missing_const_for_fn, + needless_borrow, + option_as_ref_deref, + option_map_unwrap_or, + ptr_as_ptr, + redundant_field_names, + redundant_static_lifetimes, + seek_from_current, + seek_rewind, + transmute_ptr_to_ref, + tuple_array_conversions, + type_repetition_in_bounds, + unchecked_duration_subtraction, + uninlined_format_args, + unnecessary_lazy_evaluations, + unnested_or_patterns, + use_self, + )] + msrv: Msrv = Msrv::empty(), + /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. + #[lints(large_types_passed_by_value)] + pass_by_value_size_limit: u64 = 256, + /// Lint "public" fields in a struct that are prefixed with an underscore based on their + /// exported visibility, or whether they are marked as "pub". + #[lints(pub_underscore_fields)] + pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported, + /// Whether to lint only if it's multiline. + #[lints(semicolon_inside_block)] + semicolon_inside_block_ignore_singleline: bool = false, + /// Whether to lint only if it's singleline. + #[lints(semicolon_outside_block)] + semicolon_outside_block_ignore_multiline: bool = false, + /// The maximum number of single char bindings a scope may have + #[lints(many_single_char_names)] + single_char_binding_names_threshold: u64 = 4, + /// The maximum allowed stack size for functions in bytes + #[lints(large_stack_frames)] + stack_size_threshold: u64 = 512_000, + /// Enforce the named macros always use the braces specified. + /// + /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro + /// could be used with a full path two `MacroMatcher`s have to be added one with the full path + /// `crate_name::macro_name` and one with just the macro name. + #[lints(nonstandard_macro_braces)] + standard_macro_braces: Vec = Vec::new(), + /// The minimum number of struct fields for the lints about field names to trigger + #[lints(struct_field_names)] + struct_field_name_threshold: u64 = 3, + /// Whether to suppress a restriction lint in constant code. In same + /// cases the restructured operation might not be unavoidable, as the + /// suggested counterparts are unavailable in constant code. This + /// configuration will cause restriction lints to trigger even + /// if no suggestion can be made. + #[lints(indexing_slicing)] + suppress_restriction_lint_in_const: bool = false, + /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + #[lints(boxed_local, useless_vec)] + too_large_for_stack: u64 = 200, + /// The maximum number of argument a function or method can have + #[lints(too_many_arguments)] + too_many_arguments_threshold: u64 = 7, + /// The maximum number of lines a function or method can have + #[lints(too_many_lines)] + too_many_lines_threshold: u64 = 100, + /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by + /// reference. By default there is no limit + #[default_text = "target_pointer_width * 2"] + #[lints(trivially_copy_pass_by_ref)] + trivial_copy_size_limit: Option = None, + /// The maximum complexity a type can have + #[lints(type_complexity)] + type_complexity_threshold: u64 = 250, + /// The byte size a `T` in `Box` can have, below which it triggers the `clippy::unnecessary_box` lint + #[lints(unnecessary_box_returns)] + unnecessary_box_size: u64 = 128, + /// Should the fraction of a decimal be linted to include separators. + #[lints(unreadable_literal)] + unreadable_literal_lint_fractions: bool = true, + /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other + #[lints(upper_case_acronyms)] + upper_case_acronyms_aggressive: bool = false, + /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed + #[lints(vec_box)] + vec_box_size_threshold: u64 = 4096, + /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' + #[lints(verbose_bit_mask)] + verbose_bit_mask_threshold: u64 = 1, + /// Whether to allow certain wildcard imports (prelude, super in tests). + #[lints(wildcard_imports)] + warn_on_all_wildcard_imports: bool = false, /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. - (warn_unsafe_macro_metavars_in_private_macros: bool = false), + #[lints(macro_metavars_in_unsafe)] + warn_unsafe_macro_metavars_in_private_macros: bool = false, } /// Search for the configuration file. diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index ff7fa7241cb9..d2246f12029b 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -1,5 +1,4 @@ -#![feature(rustc_private, let_chains)] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![feature(rustc_private, array_windows, let_chains)] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/src/tools/clippy/clippy_config/src/metadata.rs b/src/tools/clippy/clippy_config/src/metadata.rs index 400887185e8c..7cbd92a9b710 100644 --- a/src/tools/clippy/clippy_config/src/metadata.rs +++ b/src/tools/clippy/clippy_config/src/metadata.rs @@ -1,11 +1,12 @@ -use std::fmt::{self, Write}; +use itertools::Itertools; +use std::fmt; #[derive(Debug, Clone, Default)] pub struct ClippyConfiguration { pub name: String, pub default: String, - pub lints: Vec, - pub doc: String, + pub lints: &'static [&'static str], + pub doc: &'static str, pub deprecation_reason: Option<&'static str>, } @@ -13,61 +14,23 @@ impl fmt::Display for ClippyConfiguration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "- `{}`: {}", self.name, self.doc)?; if !self.default.is_empty() { - write!(f, " (default: `{}`)", self.default)?; + write!(f, "\n\n (default: `{}`)", self.default)?; } Ok(()) } } impl ClippyConfiguration { - pub fn new( - name: &'static str, - default: String, - doc_comment: &'static str, - deprecation_reason: Option<&'static str>, - ) -> Self { - let (mut lints, doc) = parse_config_field_doc(doc_comment) - .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string())); - - lints.sort(); - - Self { - name: to_kebab(name), - lints, - doc, - default, - deprecation_reason, - } - } - pub fn to_markdown_paragraph(&self) -> String { - let mut out = format!( - "## `{}`\n{}\n\n", + format!( + "## `{}`\n{}\n\n**Default Value:** `{}`\n\n---\n**Affected lints:**\n{}\n\n", self.name, - self.doc - .lines() - .map(|line| line.strip_prefix(" ").unwrap_or(line)) - .collect::>() - .join("\n"), - ); - - if !self.default.is_empty() { - write!(out, "**Default Value:** `{}`\n\n", self.default).unwrap(); - } - - write!( - out, - "---\n**Affected lints:**\n{}\n\n", - self.lints - .iter() - .map(|name| name.to_string().split_whitespace().next().unwrap().to_string()) - .map(|name| format!("* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})")) - .collect::>() - .join("\n"), + self.doc.lines().map(|x| x.strip_prefix(' ').unwrap_or(x)).join("\n"), + self.default, + self.lints.iter().format_with("\n", |name, f| f(&format_args!( + "* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})" + ))), ) - .unwrap(); - - out } pub fn to_markdown_link(&self) -> String { @@ -75,47 +38,3 @@ impl ClippyConfiguration { format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name) } } - -/// This parses the field documentation of the config struct. -/// -/// ```rust, ignore -/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin") -/// ``` -/// -/// Would yield: -/// ```rust, ignore -/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin") -/// ``` -fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec, String)> { - const DOC_START: &str = " Lint: "; - if doc_comment.starts_with(DOC_START) - && let Some(split_pos) = doc_comment.find('.') - { - let mut doc_comment = doc_comment.to_string(); - let mut documentation = doc_comment.split_off(split_pos); - - // Extract lints - doc_comment.make_ascii_lowercase(); - let lints: Vec = doc_comment - .split_off(DOC_START.len()) - .lines() - .next() - .unwrap() - .split(", ") - .map(str::to_string) - .collect(); - - // Format documentation correctly - // split off leading `.` from lint name list and indent for correct formatting - documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n "); - - Some((lints, documentation)) - } else { - None - } -} - -/// Transforms a given `snake_case_string` to a tasty `kebab-case-string` -fn to_kebab(config_name: &str) -> String { - config_name.replace('_', "-") -} diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index 4104e7d94f14..a5d72c3a559d 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -13,9 +13,6 @@ opener = "0.6" shell-escape = "0.1" walkdir = "2.3" -[features] -deny-warnings = [] - [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] rustc_private = true diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 256231441817..5fc4365c6e78 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -1,30 +1,65 @@ use crate::clippy_project_root; use itertools::Itertools; +use rustc_lexer::{tokenize, TokenKind}; use shell_escape::escape; use std::ffi::{OsStr, OsString}; -use std::path::Path; +use std::ops::ControlFlow; +use std::path::{Path, PathBuf}; use std::process::{self, Command, Stdio}; use std::{fs, io}; use walkdir::WalkDir; -#[derive(Debug)] -pub enum CliError { +pub enum Error { CommandFailed(String, String), - IoError(io::Error), + Io(io::Error), RustfmtNotInstalled, - WalkDirError(walkdir::Error), + WalkDir(walkdir::Error), IntellijSetupActive, + Parse(PathBuf, usize, String), + CheckFailed, } -impl From for CliError { +impl From for Error { fn from(error: io::Error) -> Self { - Self::IoError(error) + Self::Io(error) } } -impl From for CliError { +impl From for Error { fn from(error: walkdir::Error) -> Self { - Self::WalkDirError(error) + Self::WalkDir(error) + } +} + +impl Error { + fn display(&self) { + match self { + Self::CheckFailed => { + eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); + }, + Self::CommandFailed(command, stderr) => { + eprintln!("error: command `{command}` failed!\nstderr: {stderr}"); + }, + Self::Io(err) => { + eprintln!("error: {err}"); + }, + Self::RustfmtNotInstalled => { + eprintln!("error: rustfmt nightly is not installed."); + }, + Self::WalkDir(err) => { + eprintln!("error: {err}"); + }, + Self::IntellijSetupActive => { + eprintln!( + "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\ + Not formatting because that would format the local repo as well!\n\ + Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`." + ); + }, + Self::Parse(path, line, msg) => { + eprintln!("error parsing `{}:{line}`: {msg}", path.display()); + }, + } } } @@ -34,75 +69,244 @@ struct FmtContext { rustfmt_path: String, } +struct ClippyConf<'a> { + name: &'a str, + attrs: &'a str, + lints: Vec<&'a str>, + field: &'a str, +} + +fn offset_to_line(text: &str, offset: usize) -> usize { + match text.split('\n').try_fold((1usize, 0usize), |(line, pos), s| { + let pos = pos + s.len() + 1; + if pos > offset { + ControlFlow::Break(line) + } else { + ControlFlow::Continue((line + 1, pos)) + } + }) { + ControlFlow::Break(x) | ControlFlow::Continue((x, _)) => x, + } +} + +/// Formats the configuration list in `clippy_config/src/conf.rs` +#[expect(clippy::too_many_lines)] +fn fmt_conf(check: bool) -> Result<(), Error> { + #[derive(Clone, Copy)] + enum State { + Start, + Docs, + Pound, + OpenBracket, + Attr(u32), + Lints, + EndLints, + Field, + } + + let path: PathBuf = [ + clippy_project_root().as_path(), + "clippy_config".as_ref(), + "src".as_ref(), + "conf.rs".as_ref(), + ] + .into_iter() + .collect(); + let text = fs::read_to_string(&path)?; + + let (pre, conf) = text + .split_once("define_Conf! {\n") + .expect("can't find config definition"); + let (conf, post) = conf.split_once("\n}\n").expect("can't find config definition"); + let conf_offset = pre.len() + 15; + + let mut pos = 0u32; + let mut attrs_start = 0; + let mut attrs_end = 0; + let mut field_start = 0; + let mut lints = Vec::new(); + let mut name = ""; + let mut fields = Vec::new(); + let mut state = State::Start; + + for (i, t) in tokenize(conf) + .map(|x| { + let start = pos; + pos += x.len; + (start as usize, x) + }) + .filter(|(_, t)| !matches!(t.kind, TokenKind::Whitespace)) + { + match (state, t.kind) { + (State::Start, TokenKind::LineComment { doc_style: Some(_) }) => { + attrs_start = i; + attrs_end = i + t.len as usize; + state = State::Docs; + }, + (State::Start, TokenKind::Pound) => { + attrs_start = i; + attrs_end = i; + state = State::Pound; + }, + (State::Docs, TokenKind::LineComment { doc_style: Some(_) }) => attrs_end = i + t.len as usize, + (State::Docs, TokenKind::Pound) => state = State::Pound, + (State::Pound, TokenKind::OpenBracket) => state = State::OpenBracket, + (State::OpenBracket, TokenKind::Ident) => { + state = if conf[i..i + t.len as usize] == *"lints" { + State::Lints + } else { + State::Attr(0) + }; + }, + (State::Attr(0), TokenKind::CloseBracket) => { + attrs_end = i + 1; + state = State::Docs; + }, + (State::Attr(x), TokenKind::OpenParen | TokenKind::OpenBracket | TokenKind::OpenBrace) => { + state = State::Attr(x + 1); + }, + (State::Attr(x), TokenKind::CloseParen | TokenKind::CloseBracket | TokenKind::CloseBrace) => { + state = State::Attr(x - 1); + }, + (State::Lints, TokenKind::Ident) => lints.push(&conf[i..i + t.len as usize]), + (State::Lints, TokenKind::CloseBracket) => state = State::EndLints, + (State::EndLints | State::Docs, TokenKind::Ident) => { + field_start = i; + name = &conf[i..i + t.len as usize]; + state = State::Field; + }, + (State::Field, TokenKind::LineComment { doc_style: Some(_) }) => { + #[expect(clippy::drain_collect)] + fields.push(ClippyConf { + name, + lints: lints.drain(..).collect(), + attrs: &conf[attrs_start..attrs_end], + field: conf[field_start..i].trim_end(), + }); + attrs_start = i; + attrs_end = i + t.len as usize; + state = State::Docs; + }, + (State::Field, TokenKind::Pound) => { + #[expect(clippy::drain_collect)] + fields.push(ClippyConf { + name, + lints: lints.drain(..).collect(), + attrs: &conf[attrs_start..attrs_end], + field: conf[field_start..i].trim_end(), + }); + attrs_start = i; + attrs_end = i; + state = State::Pound; + }, + (State::Field | State::Attr(_), _) + | (State::Lints, TokenKind::Comma | TokenKind::OpenParen | TokenKind::CloseParen) => {}, + _ => { + return Err(Error::Parse( + path, + offset_to_line(&text, conf_offset + i), + format!("unexpected token `{}`", &conf[i..i + t.len as usize]), + )); + }, + } + } + + if !matches!(state, State::Field) { + return Err(Error::Parse( + path, + offset_to_line(&text, conf_offset + conf.len()), + "incomplete field".into(), + )); + } + fields.push(ClippyConf { + name, + lints, + attrs: &conf[attrs_start..attrs_end], + field: conf[field_start..].trim_end(), + }); + + for field in &mut fields { + field.lints.sort_unstable(); + } + fields.sort_by_key(|x| x.name); + + let new_text = format!( + "{pre}define_Conf! {{\n{}}}\n{post}", + fields.iter().format_with("", |field, f| { + if field.lints.is_empty() { + f(&format_args!(" {}\n {}\n", field.attrs, field.field)) + } else if field.lints.iter().map(|x| x.len() + 2).sum::() < 120 - 14 { + f(&format_args!( + " {}\n #[lints({})]\n {}\n", + field.attrs, + field.lints.iter().join(", "), + field.field, + )) + } else { + f(&format_args!( + " {}\n #[lints({}\n )]\n {}\n", + field.attrs, + field + .lints + .iter() + .format_with("", |x, f| f(&format_args!("\n {x},"))), + field.field, + )) + } + }) + ); + + if text != new_text { + if check { + return Err(Error::CheckFailed); + } + fs::write(&path, new_text.as_bytes())?; + } + Ok(()) +} + +fn run_rustfmt(context: &FmtContext) -> Result<(), Error> { + let project_root = clippy_project_root(); + + // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to + // format because rustfmt would also format the entire rustc repo as it is a local + // dependency + if fs::read_to_string(project_root.join("Cargo.toml")) + .expect("Failed to read clippy Cargo.toml") + .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") + { + return Err(Error::IntellijSetupActive); + } + + check_for_rustfmt(context)?; + + cargo_fmt(context, project_root.as_path())?; + cargo_fmt(context, &project_root.join("clippy_dev"))?; + cargo_fmt(context, &project_root.join("rustc_tools_util"))?; + cargo_fmt(context, &project_root.join("lintcheck"))?; + + let chunks = WalkDir::new(project_root.join("tests")) + .into_iter() + .filter_map(|entry| { + let entry = entry.expect("failed to find tests"); + let path = entry.path(); + + if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" { + None + } else { + Some(entry.into_path().into_os_string()) + } + }) + .chunks(250); + + for chunk in &chunks { + rustfmt(context, chunk)?; + } + Ok(()) +} + // the "main" function of cargo dev fmt pub fn run(check: bool, verbose: bool) { - fn try_run(context: &FmtContext) -> Result { - let mut success = true; - - let project_root = clippy_project_root(); - - // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to - // format because rustfmt would also format the entire rustc repo as it is a local - // dependency - if fs::read_to_string(project_root.join("Cargo.toml")) - .expect("Failed to read clippy Cargo.toml") - .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") - { - return Err(CliError::IntellijSetupActive); - } - - rustfmt_test(context)?; - - success &= cargo_fmt(context, project_root.as_path())?; - success &= cargo_fmt(context, &project_root.join("clippy_dev"))?; - success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?; - success &= cargo_fmt(context, &project_root.join("lintcheck"))?; - - let chunks = WalkDir::new(project_root.join("tests")) - .into_iter() - .filter_map(|entry| { - let entry = entry.expect("failed to find tests"); - let path = entry.path(); - - if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" { - None - } else { - Some(entry.into_path().into_os_string()) - } - }) - .chunks(250); - - for chunk in &chunks { - success &= rustfmt(context, chunk)?; - } - - Ok(success) - } - - fn output_err(err: CliError) { - match err { - CliError::CommandFailed(command, stderr) => { - eprintln!("error: A command failed! `{command}`\nstderr: {stderr}"); - }, - CliError::IoError(err) => { - eprintln!("error: {err}"); - }, - CliError::RustfmtNotInstalled => { - eprintln!("error: rustfmt nightly is not installed."); - }, - CliError::WalkDirError(err) => { - eprintln!("error: {err}"); - }, - CliError::IntellijSetupActive => { - eprintln!( - "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`. -Not formatting because that would format the local repo as well! -Please revert the changes to Cargo.tomls with `cargo dev remove intellij`." - ); - }, - } - } - let output = Command::new("rustup") .args(["which", "rustfmt"]) .stderr(Stdio::inherit()) @@ -120,21 +324,10 @@ Please revert the changes to Cargo.tomls with `cargo dev remove intellij`." verbose, rustfmt_path, }; - let result = try_run(&context); - let code = match result { - Ok(true) => 0, - Ok(false) => { - eprintln!(); - eprintln!("Formatting check failed."); - eprintln!("Run `cargo dev fmt` to update formatting."); - 1 - }, - Err(err) => { - output_err(err); - 1 - }, - }; - process::exit(code); + if let Err(e) = run_rustfmt(&context).and_then(|()| fmt_conf(check)) { + e.display(); + process::exit(1); + } } fn format_command(program: impl AsRef, dir: impl AsRef, args: &[impl AsRef]) -> String { @@ -148,12 +341,12 @@ fn format_command(program: impl AsRef, dir: impl AsRef, args: &[imp ) } -fn exec( +fn exec_fmt_command( context: &FmtContext, program: impl AsRef, dir: impl AsRef, args: &[impl AsRef], -) -> Result { +) -> Result<(), Error> { if context.verbose { println!("{}", format_command(&program, &dir, args)); } @@ -166,28 +359,28 @@ fn exec( .unwrap(); let success = output.status.success(); - if !context.check && !success { - let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); - return Err(CliError::CommandFailed( - format_command(&program, &dir, args), - String::from(stderr), - )); + match (context.check, success) { + (_, true) => Ok(()), + (true, false) => Err(Error::CheckFailed), + (false, false) => { + let stderr = std::str::from_utf8(&output.stderr).unwrap_or(""); + Err(Error::CommandFailed( + format_command(&program, &dir, args), + String::from(stderr), + )) + }, } - - Ok(success) } -fn cargo_fmt(context: &FmtContext, path: &Path) -> Result { +fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<(), Error> { let mut args = vec!["fmt", "--all"]; if context.check { args.push("--check"); } - let success = exec(context, "cargo", path, &args)?; - - Ok(success) + exec_fmt_command(context, "cargo", path, &args) } -fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { +fn check_for_rustfmt(context: &FmtContext) -> Result<(), Error> { let program = "rustfmt"; let dir = std::env::current_dir()?; let args = &["--version"]; @@ -204,23 +397,20 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> { .unwrap_or("") .starts_with("error: 'rustfmt' is not installed") { - Err(CliError::RustfmtNotInstalled) + Err(Error::RustfmtNotInstalled) } else { - Err(CliError::CommandFailed( + Err(Error::CommandFailed( format_command(program, &dir, args), std::str::from_utf8(&output.stderr).unwrap_or("").to_string(), )) } } -fn rustfmt(context: &FmtContext, paths: impl Iterator) -> Result { +fn rustfmt(context: &FmtContext, paths: impl Iterator) -> Result<(), Error> { let mut args = Vec::new(); if context.check { args.push(OsString::from("--check")); } args.extend(paths); - - let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?; - - Ok(success) + exec_fmt_command(context, &context.rustfmt_path, std::env::current_dir()?, &args) } diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 3aa43dbe23ed..ad385d5fbd29 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -1,6 +1,5 @@ #![feature(let_chains)] #![feature(rustc_private)] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn( trivial_casts, trivial_numeric_casts, diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 366b52b25dfc..755b04b0b232 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -74,7 +73,7 @@ fn main() { new_name, uplift, } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift), - DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()), + DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason), } } @@ -223,7 +222,7 @@ enum DevCommand { name: String, #[arg(long, short)] /// The reason for deprecation - reason: Option, + reason: String, }, } diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs index 4a4261d1a1e6..19560b31fd3e 100644 --- a/src/tools/clippy/clippy_dev/src/serve.rs +++ b/src/tools/clippy/clippy_dev/src/serve.rs @@ -1,10 +1,14 @@ -use std::ffi::OsStr; -use std::num::ParseIntError; use std::path::Path; use std::process::Command; use std::time::{Duration, SystemTime}; use std::{env, thread}; +#[cfg(windows)] +const PYTHON: &str = "python"; + +#[cfg(not(windows))] +const PYTHON: &str = "python3"; + /// # Panics /// /// Panics if the python commands could not be spawned @@ -25,7 +29,7 @@ pub fn run(port: u16, lint: Option) -> ! { } if let Some(url) = url.take() { thread::spawn(move || { - Command::new("python3") + Command::new(PYTHON) .arg("-m") .arg("http.server") .arg(port.to_string()) @@ -58,8 +62,3 @@ fn mtime(path: impl AsRef) -> SystemTime { .unwrap_or(SystemTime::UNIX_EPOCH) } } - -#[allow(clippy::missing_errors_doc)] -pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> { - arg.to_string_lossy().parse::().map(|_| ()) -} diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 45353901c98f..15578d69c3a9 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,13 +1,12 @@ use crate::clippy_project_root; use aho_corasick::AhoCorasickBuilder; -use indoc::writedoc; use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fmt::{self, Write}; use std::fs::{self, OpenOptions}; -use std::io::{self, Read, Seek, SeekFrom, Write as _}; +use std::io::{self, Read, Seek, Write as _}; use std::ops::Range; use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; @@ -77,12 +76,8 @@ fn generate_lint_files( for lint in usable_lints .iter() .map(|l| &*l.name) - .chain(deprecated_lints.iter().map(|l| &*l.name)) - .chain( - renamed_lints - .iter() - .map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)), - ) + .chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) + .chain(renamed_lints.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) .sorted() { writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); @@ -108,11 +103,6 @@ fn generate_lint_files( update_mode, &gen_declared_lints(internal_lints.iter(), usable_lints.iter()), ); - process_file( - "clippy_lints/src/lib.deprecated.rs", - update_mode, - &gen_deprecated(deprecated_lints), - ); let content = gen_deprecated_lints_test(deprecated_lints); process_file("tests/ui/deprecated.rs", update_mode, &content); @@ -205,7 +195,7 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { let ext = f.path().extension(); (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) && name != Some(OsStr::new("rename.rs")) - && name != Some(OsStr::new("renamed_lints.rs")) + && name != Some(OsStr::new("deprecated_lints.rs")) }) { rewrite_file(file.path(), |s| { @@ -213,6 +203,19 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { }); } + let version = crate::new_lint::get_stabilization_version(); + rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { + insert_at_marker( + s, + "// end renamed lints. used by `cargo dev rename_lint`", + &format!( + "#[clippy::version = \"{version}\"]\n \ + (\"{}\", \"{}\"),\n ", + lint.old_name, lint.new_name, + ), + ) + }); + renamed_lints.push(lint); renamed_lints.sort_by(|lhs, rhs| { lhs.new_name @@ -222,11 +225,6 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { .then_with(|| lhs.old_name.cmp(&rhs.old_name)) }); - write_file( - Path::new("clippy_lints/src/renamed_lints.rs"), - &gen_renamed_lints_list(&renamed_lints), - ); - if uplift { write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); println!( @@ -293,7 +291,8 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being // renamed. - for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) { + for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("deprecated_lints.rs")) + { rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); } @@ -304,7 +303,6 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) { println!("note: `cargo uitest` still needs to be run to update the test results"); } -const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note"; /// Runs the `deprecate` command /// /// This does the following: @@ -314,33 +312,16 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note"; /// # Panics /// /// If a file path could not read from or written to -pub fn deprecate(name: &str, reason: Option<&str>) { - fn finish( - (lints, mut deprecated_lints, renamed_lints): (Vec, Vec, Vec), - name: &str, - reason: &str, - ) { - deprecated_lints.push(DeprecatedLint { - name: name.to_string(), - reason: reason.to_string(), - declaration_range: Range::default(), - }); +pub fn deprecate(name: &str, reason: &str) { + let prefixed_name = if name.starts_with("clippy::") { + name.to_owned() + } else { + format!("clippy::{name}") + }; + let stripped_name = &prefixed_name[8..]; - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{name}` has successfully been deprecated"); - - if reason == DEFAULT_DEPRECATION_REASON { - println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`"); - } - println!("note: you must run `cargo uitest` to update the test results"); - } - - let reason = reason.unwrap_or(DEFAULT_DEPRECATION_REASON); - let name_lower = name.to_lowercase(); - let name_upper = name.to_uppercase(); - - let (mut lints, deprecated_lints, renamed_lints) = gather_all(); - let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { + let (mut lints, mut deprecated_lints, renamed_lints) = gather_all(); + let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { eprintln!("error: failed to find lint `{name}`"); return; }; @@ -357,13 +338,27 @@ pub fn deprecate(name: &str, reason: Option<&str>) { let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs"); - if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) { - declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap(); - finish((lints, deprecated_lints, renamed_lints), name, reason); - return; - } + if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { + let version = crate::new_lint::get_stabilization_version(); + rewrite_file(deprecated_lints_path, |s| { + insert_at_marker( + s, + "// end deprecated lints. used by `cargo dev deprecate_lint`", + &format!("#[clippy::version = \"{version}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ",), + ) + }); - eprintln!("error: lint not found"); + deprecated_lints.push(DeprecatedLint { + name: prefixed_name, + reason: reason.into(), + }); + + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{name}` has successfully been deprecated"); + println!("note: you must run `cargo uitest` to update the test results"); + } else { + eprintln!("error: lint not found"); + } } fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io::Result { @@ -377,14 +372,14 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io // Some lints have their own directories, delete them if path.is_dir() { - fs::remove_dir_all(path).ok(); + let _ = fs::remove_dir_all(path); return; } // Remove all related test files - fs::remove_file(path.with_extension("rs")).ok(); - fs::remove_file(path.with_extension("stderr")).ok(); - fs::remove_file(path.with_extension("fixed")).ok(); + let _ = fs::remove_file(path.with_extension("rs")); + let _ = fs::remove_file(path.with_extension("stderr")); + let _ = fs::remove_file(path.with_extension("fixed")); } fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { @@ -427,7 +422,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io lint_mod_path.set_file_name(name); lint_mod_path.set_extension("rs"); - fs::remove_file(lint_mod_path).ok(); + let _ = fs::remove_file(lint_mod_path); } let mut content = @@ -465,37 +460,6 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec) -> io Ok(false) } -fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> { - let mut file = OpenOptions::new().write(true).open(path)?; - - file.seek(SeekFrom::End(0))?; - - let version = crate::new_lint::get_stabilization_version(); - let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON { - "TODO" - } else { - reason - }; - - writedoc!( - file, - " - - declare_deprecated_lint! {{ - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// {deprecation_reason} - #[clippy::version = \"{version}\"] - pub {name}, - \"{reason}\" - }} - - " - ) -} - /// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there /// were no replacements. fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option { @@ -604,14 +568,12 @@ impl Lint { struct DeprecatedLint { name: String, reason: String, - declaration_range: Range, } impl DeprecatedLint { - fn new(name: &str, reason: &str, declaration_range: Range) -> Self { + fn new(name: &str, reason: &str) -> Self { Self { - name: name.to_lowercase(), + name: remove_line_splices(name), reason: remove_line_splices(reason), - declaration_range, } } } @@ -629,28 +591,6 @@ impl RenamedLint { } } -/// Generates the `register_removed` code -#[must_use] -fn gen_deprecated(lints: &[DeprecatedLint]) -> String { - let mut output = GENERATED_FILE_COMMENT.to_string(); - output.push_str("{\n"); - for lint in lints { - let _: fmt::Result = write!( - output, - concat!( - " store.register_removed(\n", - " \"clippy::{}\",\n", - " \"{}\",\n", - " );\n" - ), - lint.name, lint.reason, - ); - } - output.push_str("}\n"); - - output -} - /// Generates the code for registering lints #[must_use] fn gen_declared_lints<'a>( @@ -680,7 +620,7 @@ fn gen_declared_lints<'a>( fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { let mut res: String = GENERATED_FILE_COMMENT.into(); for lint in lints { - writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap(); + writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); } res.push_str("\nfn main() {}\n"); res @@ -699,27 +639,13 @@ fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { seen_lints.clear(); for lint in lints { if seen_lints.insert(&lint.old_name) { - writeln!(res, "#![warn({})]", lint.old_name).unwrap(); + writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); } } res.push_str("\nfn main() {}\n"); res } -fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String { - const HEADER: &str = "\ - // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\ - #[rustfmt::skip]\n\ - pub static RENAMED_LINTS: &[(&str, &str)] = &[\n"; - - let mut res = String::from(HEADER); - for lint in lints { - writeln!(res, " (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap(); - } - res.push_str("];\n"); - res -} - /// Gathers all lints defined in `clippy_lints/src` fn gather_all() -> (Vec, Vec, Vec) { let mut lints = Vec::with_capacity(1000); @@ -744,10 +670,10 @@ fn gather_all() -> (Vec, Vec, Vec) { module.strip_suffix(".rs").unwrap_or(&module) }; - match module { - "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints), - "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints), - _ => parse_contents(&contents, module, &mut lints), + if module == "deprecated_lints" { + parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints); + } else { + parse_contents(&contents, module, &mut lints); } } (lints, deprecated_lints, renamed_lints) @@ -848,54 +774,37 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec) { } /// Parse a source file looking for `declare_deprecated_lint` macro invocations. -fn parse_deprecated_contents(contents: &str, lints: &mut Vec) { - let mut offset = 0usize; - let mut iter = tokenize(contents).map(|t| { - let range = offset..offset + t.len as usize; - offset = range.end; +fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec, renamed: &mut Vec) { + let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else { + return; + }; + let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else { + return; + }; - LintDeclSearchResult { - token_kind: t.kind, - content: &contents[range.clone()], - range, - } - }); + for line in deprecated_src.lines() { + let mut offset = 0usize; + let mut iter = tokenize(line).map(|t| { + let range = offset..offset + t.len as usize; + offset = range.end; - while let Some(LintDeclSearchResult { range, .. }) = iter.find( - |LintDeclSearchResult { - token_kind, content, .. - }| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint", - ) { - let start = range.start; - - let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| { - !matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }) + LintDeclSearchResult { + token_kind: t.kind, + content: &line[range.clone()], + range, + } }); + let (name, reason) = match_tokens!( iter, - // !{ - Bang OpenBrace - // #[clippy::version = "version"] - Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket - // pub LINT_NAME, - Ident Ident(name) Comma - // "description" - Literal{kind: LiteralKind::Str{..},..}(reason) + // ("old_name", + Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(name) Comma + // "new_name"), + Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma ); - - if let Some(LintDeclSearchResult { - token_kind: TokenKind::CloseBrace, - range, - .. - }) = iter.next() - { - lints.push(DeprecatedLint::new(name, reason, start..range.end)); - } + deprecated.push(DeprecatedLint::new(name, reason)); } -} - -fn parse_renamed_contents(contents: &str, lints: &mut Vec) { - for line in contents.lines() { + for line in renamed_src.lines() { let mut offset = 0usize; let mut iter = tokenize(line).map(|t| { let range = offset..offset + t.len as usize; @@ -915,7 +824,7 @@ fn parse_renamed_contents(contents: &str, lints: &mut Vec) { // "new_name"), Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma ); - lints.push(RenamedLint::new(old_name, new_name)); + renamed.push(RenamedLint::new(old_name, new_name)); } } @@ -1015,6 +924,12 @@ fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { panic!("failed to {action} file `{}`: {error}", name.display()) } +fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option { + let i = text.find(marker)?; + let (pre, post) = text.split_at(i); + Some([pre, new_text, post].into_iter().collect()) +} + fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { let mut file = OpenOptions::new() .write(true) @@ -1084,31 +999,6 @@ mod tests { assert_eq!(expected, result); } - #[test] - fn test_parse_deprecated_contents() { - static DEPRECATED_CONTENTS: &str = r#" - /// some doc comment - declare_deprecated_lint! { - #[clippy::version = "I'm a version"] - pub SHOULD_ASSERT_EQ, - "`assert!()` will be more flexible with RFC 2011" - } - "#; - - let mut result = Vec::new(); - parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result); - for r in &mut result { - r.declaration_range = Range::default(); - } - - let expected = vec![DeprecatedLint::new( - "should_assert_eq", - "\"`assert!()` will be more flexible with RFC 2011\"", - Range::default(), - )]; - assert_eq!(expected, result); - } - #[test] fn test_usable_lints() { let lints = vec![ @@ -1177,34 +1067,4 @@ mod tests { ); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } - - #[test] - fn test_gen_deprecated() { - let lints = vec![ - DeprecatedLint::new( - "should_assert_eq", - "\"has been superseded by should_assert_eq2\"", - Range::default(), - ), - DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()), - ]; - - let expected = GENERATED_FILE_COMMENT.to_string() - + &[ - "{", - " store.register_removed(", - " \"clippy::should_assert_eq\",", - " \"has been superseded by should_assert_eq2\",", - " );", - " store.register_removed(", - " \"clippy::another_deprecated\",", - " \"will be removed\",", - " );", - "}", - ] - .join("\n") - + "\n"; - - assert_eq!(expected, gen_deprecated(&lints)); - } } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index eb04c006f89f..99ed93468a01 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -32,7 +32,6 @@ url = "2.2" walkdir = "2.3" [features] -deny-warnings = ["clippy_config/deny-warnings", "clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default internal = ["serde_json", "tempfile", "regex"] diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index e6d52bcef717..3b4cc1134802 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -97,7 +97,7 @@ impl ApproxConstant { cx, APPROX_CONSTANT, e.span, - format!("approximate value of `{module}::consts::{}` found", &name), + format!("approximate value of `{module}::consts::{name}` found"), None, "consider using the constant directly", ); diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs index cfa25005a05e..fefd8195f8e7 100644 --- a/src/tools/clippy/clippy_lints/src/as_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -52,13 +52,15 @@ impl<'tcx> LateLintPass<'tcx> for AsConversions { && !in_external_macro(cx.sess(), expr.span) && !is_from_proc_macro(cx, expr) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, AS_CONVERSIONS, expr.span, "using a potentially dangerous silent `as` conversion", - None, - "consider using a safe wrapper for this conversion", + |diag| { + diag.help("consider using a safe wrapper for this conversion"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/asm_syntax.rs b/src/tools/clippy/clippy_lints/src/asm_syntax.rs index 0db1456d40bf..69a8eb7d94e7 100644 --- a/src/tools/clippy/clippy_lints/src/asm_syntax.rs +++ b/src/tools/clippy/clippy_lints/src/asm_syntax.rs @@ -1,6 +1,6 @@ use std::fmt; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; use rustc_ast::{InlineAsm, Item, ItemKind}; use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; @@ -49,14 +49,10 @@ fn check_asm_syntax( }; if style == check_for { - span_lint_and_help( - cx, - lint, - span, - format!("{style} x86 assembly syntax used"), - None, - format!("use {} x86 assembly syntax", !style), - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, lint, span, format!("{style} x86 assembly syntax used"), |diag| { + diag.help(format!("use {} x86 assembly syntax", !style)); + }); } } } @@ -98,13 +94,13 @@ declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); impl EarlyLintPass for InlineAsmX86IntelSyntax { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::InlineAsm(inline_asm) = &expr.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Intel); + check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel); } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if let ItemKind::GlobalAsm(inline_asm) = &item.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Intel); + check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel); } } } @@ -146,13 +142,13 @@ declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]); impl EarlyLintPass for InlineAsmX86AttSyntax { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::InlineAsm(inline_asm) = &expr.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Att); + check_asm_syntax(INLINE_ASM_X86_ATT_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Att); } } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if let ItemKind::GlobalAsm(inline_asm) = &item.kind { - check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Att); + check_asm_syntax(INLINE_ASM_X86_ATT_SYNTAX, cx, inline_asm, item.span, AsmStyle::Att); } } } diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index ed4cdce8cb88..7eaac80f969f 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_inside_always_const_context; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return; }; - let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else { + let Some(Constant::Bool(val)) = ConstEvalCtxt::new(cx).eval(condition) else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index 7217686dcca5..f1cb4a05af86 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; @@ -68,39 +68,28 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { return; } } - let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" }; - let mut app = Applicability::MachineApplicable; - match method_segment.ident.as_str() { + let (message, replacement) = match method_segment.ident.as_str() { "is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => { - span_lint_and_sugg( - cx, - ASSERTIONS_ON_RESULT_STATES, - macro_call.span, - "called `assert!` with `Result::is_ok`", - "replace with", - format!( - "{}.unwrap(){semicolon}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 - ), - app, - ); + ("called `assert!` with `Result::is_ok`", "unwrap") }, "is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => { - span_lint_and_sugg( - cx, - ASSERTIONS_ON_RESULT_STATES, - macro_call.span, - "called `assert!` with `Result::is_err`", - "replace with", - format!( - "{}.unwrap_err(){semicolon}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 - ), - app, - ); + ("called `assert!` with `Result::is_err`", "unwrap_err") }, - _ => (), + _ => return, }; + span_lint_and_then(cx, ASSERTIONS_ON_RESULT_STATES, macro_call.span, message, |diag| { + let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" }; + let mut app = Applicability::MachineApplicable; + diag.span_suggestion( + macro_call.span, + "replace with", + format!( + "{}.{replacement}(){semicolon}", + snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 + ), + app, + ); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index 03f777600f08..6e336efbb90a 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -227,9 +227,22 @@ fn build_sugg<'tcx>( match call_kind { CallKind::Method => { let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { - // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` - Sugg::hir_with_applicability(cx, ref_expr, "_", app) + // If `ref_expr` is a reference, we can remove the dereference operator (`*`) to make + // the generated code a bit simpler. In other cases, we don't do this special case, to avoid + // having to deal with Deref (https://github.com/rust-lang/rust-clippy/issues/12437). + + let ty = cx.typeck_results().expr_ty(ref_expr); + if ty.is_ref() { + // Apply special case, remove `*` + // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", app) + } else { + // Keep the original lhs + // `*lhs = self_expr.clone();` -> `(*lhs).clone_from(self_expr)` + Sugg::hir_with_applicability(cx, lhs, "_", app) + } } else { + // Keep the original lhs // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)` Sugg::hir_with_applicability(cx, lhs, "_", app) } @@ -249,8 +262,16 @@ fn build_sugg<'tcx>( }, CallKind::Ufcs => { let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind { - // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)` - Sugg::hir_with_applicability(cx, ref_expr, "_", app) + // See special case of removing `*` in method handling above + let ty = cx.typeck_results().expr_ty(ref_expr); + if ty.is_ref() { + // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)` + Sugg::hir_with_applicability(cx, ref_expr, "_", app) + } else { + // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut *lhs, self_expr)` + // mut_addr_deref is used to avoid unnecessary parentheses around `*lhs` + Sugg::hir_with_applicability(cx, ref_expr, "_", app).mut_addr_deref() + } } else { // `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)` Sugg::hir_with_applicability(cx, lhs, "_", app).mut_addr() diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs index df9994086cd4..a5a7b9f74a69 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs @@ -1,5 +1,5 @@ use super::ALLOW_ATTRIBUTES; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{AttrStyle, Attribute}; use rustc_errors::Applicability; @@ -13,14 +13,14 @@ pub fn check<'cx>(cx: &LateContext<'cx>, attr: &'cx Attribute) { && let Some(ident) = attr.ident() && !is_from_proc_macro(cx, attr) { - span_lint_and_sugg( - cx, - ALLOW_ATTRIBUTES, - ident.span, - "#[allow] attribute found", - "replace it with", - "expect".into(), - Applicability::MachineApplicable, - ); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, ALLOW_ATTRIBUTES, ident.span, "#[allow] attribute found", |diag| { + diag.span_suggestion( + ident.span, + "replace it with", + "expect", + Applicability::MachineApplicable, + ); + }); } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs index 4b42616a636b..4ab97118df1d 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs @@ -1,5 +1,5 @@ use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON}; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_lint::{LateContext, LintContext}; @@ -21,12 +21,14 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet return; } - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, attr.span, format!("`{}` attribute without specifying a reason", name.as_str()), - None, - "try adding a reason at the end with `, reason = \"..\"`", + |diag| { + diag.help("try adding a reason at the end with `, reason = \"..\"`"); + }, ); } diff --git a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs index 561ca9bd9866..612712d16843 100644 --- a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs +++ b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; -use clippy_utils::{in_constant, is_else_clause}; +use clippy_utils::{is_else_clause, is_in_const_context}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { && let Some(else_lit) = as_int_bool_lit(else_) && then_lit != else_lit && !expr.span.from_expansion() - && !in_constant(cx, expr.hir_id) + && !is_in_const_context(cx) { let ty = cx.typeck_results().expr_ty(then); let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index a1c6c0b608f7..a2f48c18170a 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -477,14 +477,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> { cx: self.cx, }; if let Ok(expr) = h2q.run(e) { - if h2q.terminals.len() > 8 { - // QMC has exponentially slow behavior as the number of terminals increases - // 8 is reasonable, it takes approximately 0.2 seconds. - // See #825 + let stats = terminal_stats(&expr); + if stats.ops > 7 { + // QMC has exponentially slow behavior as the number of ops increases. + // See #825, #13206 return; } - - let stats = terminal_stats(&expr); let mut simplified = expr.simplify(); for simple in Bool::Not(Box::new(expr)).simplify() { match simple { 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 ff460a3fd8e3..bd3acc06f4b2 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::in_constant; +use clippy_utils::is_in_const_context; use clippy_utils::source::snippet_opt; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_isize_or_usize; @@ -21,7 +21,7 @@ pub(super) fn check( cast_to_hir: &rustc_hir::Ty<'_>, msrv: &Msrv, ) { - if !should_lint(cx, expr, cast_from, cast_to, msrv) { + if !should_lint(cx, cast_from, cast_to, msrv) { return; } @@ -70,9 +70,9 @@ pub(super) fn check( ); } -fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { +fn should_lint(cx: &LateContext<'_>, 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 is_in_const_context(cx) { return false; } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs b/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs index 5bc8692c289f..464eabe5d9ab 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_nan_to_int.rs @@ -1,6 +1,6 @@ use super::CAST_NAN_TO_INT; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_note; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, } fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - match constant(cx, cx.typeck_results(), e) { + match ConstEvalCtxt::new(cx).eval(e) { // FIXME(f16_f128): add these types when nan checks are available on all platforms Some(Constant::F64(n)) => n.is_nan(), Some(Constant::F32(n)) => n.is_nan(), diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index 7c5acd1a678d..102fe25fc67b 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::expr_or_init; use clippy_utils::source::snippet; @@ -15,7 +15,7 @@ use rustc_target::abi::IntegerType; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - if let Some(Constant::Int(c)) = constant(cx, cx.typeck_results(), expr) { + if let Some(Constant::Int(c)) = ConstEvalCtxt::new(cx).eval(expr) { Some(c) } else { None diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index 8bbd41b0db1e..9daf237344a4 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -1,7 +1,7 @@ use std::convert::Infallible; use std::ops::ControlFlow; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; use clippy_utils::{method_chain_args, sext}; @@ -88,7 +88,7 @@ fn get_const_signed_int_eval<'cx>( ) -> Option { let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); - if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? + if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)? && let ty::Int(ity) = *ty.kind() { return Some(sext(cx.tcx, n, ity)); @@ -103,7 +103,7 @@ fn get_const_unsigned_int_eval<'cx>( ) -> Option { let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr)); - if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)? + if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)? && let ty::Uint(_ity) = *ty.kind() { return Some(n); diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs index 826589bf303b..75de53f73ee7 100644 --- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs @@ -1,6 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; @@ -14,21 +14,24 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, _ => { /* continue to checks */ }, } - match cast_from.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { - let mut applicability = Applicability::MaybeIncorrect; - let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); + if let ty::FnDef(..) | ty::FnPtr(_) = cast_from.kind() { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - FN_TO_NUMERIC_CAST_ANY, - expr.span, - format!("casting function pointer `{from_snippet}` to `{cast_to}`"), - "did you mean to invoke the function?", - format!("{from_snippet}() as {cast_to}"), - applicability, - ); - }, - _ => {}, + span_lint_and_then( + cx, + FN_TO_NUMERIC_CAST_ANY, + expr.span, + format!("casting function pointer `{from_snippet}` to `{cast_to}`"), + |diag| { + diag.span_suggestion_with_style( + expr.span, + "did you mean to invoke the function?", + format!("{from_snippet}() as {cast_to}"), + applicability, + SuggestionStyle::ShowAlways, + ); + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs index 5071af5ecb98..3c1c7d2dc3a5 100644 --- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use clippy_utils::{in_constant, is_integer_literal, std_or_core}; +use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; use rustc_lint::LateContext; @@ -10,7 +10,7 @@ use super::ZERO_PTR; pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { if let TyKind::Ptr(ref mut_ty) = to.kind && is_integer_literal(from, 0) - && !in_constant(cx, from.hir_id) + && !is_in_const_context(cx) && let Some(std_or_core) = std_or_core(cx) { let (msg, sugg_fn) = match mut_ty.mutbl { diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 0b1ab5411bf1..1711565fca89 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -4,7 +4,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; +use clippy_utils::{is_in_const_context, is_integer_literal, SpanlessEq}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { _ => return, } && !in_external_macro(cx.sess(), item.span) - && !in_constant(cx, item.hir_id) + && !is_in_const_context(cx) && self.msrv.meets(msrvs::TRY_FROM) && let Some(cv) = match op2 { // todo: check for case signed -> larger unsigned == only x >= 0 diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index 2c23c0b4f154..5d78744e9b5e 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq}; +use clippy_utils::{if_sequence, is_else_clause, is_in_const_context, SpanlessEq}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { return; } - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs index 27c00948a8f2..b49a977dbeaf 100644 --- a/src/tools/clippy/clippy_lints/src/create_dir.rs +++ b/src/tools/clippy/clippy_lints/src/create_dir.rs @@ -1,6 +1,6 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use rustc_errors::Applicability; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -39,14 +39,24 @@ impl LateLintPass<'_> for CreateDir { && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id) { - span_lint_and_sugg( + span_lint_and_then( cx, CREATE_DIR, expr.span, "calling `std::fs::create_dir` where there may be a better way", - "consider calling `std::fs::create_dir_all` instead", - format!("create_dir_all({})", snippet(cx, arg.span, "..")), - Applicability::MaybeIncorrect, + |diag| { + let mut app = Applicability::MaybeIncorrect; + diag.span_suggestion_with_style( + expr.span, + "consider calling `std::fs::create_dir_all` instead", + format!( + "create_dir_all({})", + snippet_with_applicability(cx, arg.span, "..", &mut app) + ), + app, + SuggestionStyle::ShowAlways, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 788c6f3ada29..93c8fff05e9e 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; use clippy_utils::macros::{macro_backtrace, MacroCall}; use clippy_utils::source::snippet_with_applicability; @@ -65,61 +65,67 @@ impl LateLintPass<'_> for DbgMacro { // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml !(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id)) { - let mut applicability = Applicability::MachineApplicable; - - let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { - // dbg!() - ExprKind::Block(..) => { - // If the `dbg!` macro is a "free" statement and not contained within other expressions, - // remove the whole statement. - if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) - && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) - { - (macro_call.span.to(semi_span), String::new()) - } else { - (macro_call.span, String::from("()")) - } - }, - // dbg!(1) - ExprKind::Match(val, ..) => ( - macro_call.span, - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(), - ), - // dbg!(2, 3) - ExprKind::Tup( - [ - Expr { - kind: ExprKind::Match(first, ..), - .. - }, - .., - Expr { - kind: ExprKind::Match(last, ..), - .. - }, - ], - ) => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - (macro_call.span, format!("({snippet})")) - }, - _ => return, - }; - self.prev_ctxt = cur_syntax_ctxt; - span_lint_and_sugg( + span_lint_and_then( cx, DBG_MACRO, - sugg_span, + macro_call.span, "the `dbg!` macro is intended as a debugging tool", - "remove the invocation before committing it to a version control system", - suggestion, - applicability, + |diag| { + let mut applicability = Applicability::MachineApplicable; + + let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { + // dbg!() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) + && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) + { + (macro_call.span.to(semi_span), String::new()) + } else { + (macro_call.span, String::from("()")) + } + }, + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) + .to_string(), + ), + // dbg!(2, 3) + ExprKind::Tup( + [ + Expr { + kind: ExprKind::Match(first, ..), + .. + }, + .., + Expr { + kind: ExprKind::Match(last, ..), + .. + }, + ], + ) => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + (macro_call.span, format!("({snippet})")) + }, + _ => unreachable!(), + }; + + diag.span_suggestion( + sugg_span, + "remove the invocation before committing it to a version control system", + suggestion, + applicability, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 69f9eb6842bc..3fb083dd8331 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -14,8 +14,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ #[cfg(feature = "internal")] crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO, #[cfg(feature = "internal")] - crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON_INFO, - #[cfg(feature = "internal")] crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO, #[cfg(feature = "internal")] crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO, @@ -741,6 +739,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::unused_async::UNUSED_ASYNC_INFO, crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO, crate::unused_peekable::UNUSED_PEEKABLE_INFO, + crate::unused_result_ok::UNUSED_RESULT_OK_INFO, crate::unused_rounding::UNUSED_ROUNDING_INFO, crate::unused_self::UNUSED_SELF_INFO, crate::unused_unit::UNUSED_UNIT_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index 72fa05be3cc6..0b7279f2b360 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -221,7 +221,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { .map(ToString::to_string) .collect::>() .join(", "); - format!("{adt_def_ty_name}::<{}>", &tys_str) + format!("{adt_def_ty_name}::<{tys_str}>") } else { binding_type.to_string() }; diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 9af73db6849f..a74b3a8c8362 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -92,20 +92,8 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { let (suffix, is_float) = match lit_ty.kind() { ty::Int(IntTy::I32) => ("i32", false), ty::Float(FloatTy::F64) => ("f64", true), - // Default numeric fallback never results in other types. _ => return, }; - - let src = if let Some(src) = snippet_opt(self.cx, lit.span) { - src - } else { - match lit.node { - LitKind::Int(src, _) => format!("{src}"), - LitKind::Float(src, _) => format!("{src}"), - _ => return, - } - }; - let sugg = numeric_literal::format(&src, Some(suffix), is_float); span_lint_hir_and_then( self.cx, DEFAULT_NUMERIC_FALLBACK, @@ -113,6 +101,17 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { lit.span, "default numeric fallback might occur", |diag| { + let src = if let Some(src) = snippet_opt(self.cx, lit.span) { + src + } else { + match lit.node { + LitKind::Int(src, _) => format!("{src}"), + LitKind::Float(src, _) => format!("{src}"), + _ => unreachable!("Default numeric fallback never results in other types"), + } + }; + + let sugg = numeric_literal::format(&src, Some(suffix), is_float); diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect); }, ); diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs index 3fa9bad0d03d..9f020d3081c4 100644 --- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs +++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::{HirId, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -56,16 +56,18 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { && is_union_with_two_non_zst_fields(cx, item) && !has_c_repr_attr(cx, item.hir_id()) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, DEFAULT_UNION_REPRESENTATION, item.span, "this union has the default representation", - None, - format!( - "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout", - cx.tcx.def_path_str(item.owner_id) - ), + |diag| { + diag.help(format!( + "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout", + cx.tcx.def_path_str(item.owner_id) + )); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs index a0900f46f6aa..0066ed643251 100644 --- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs +++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs @@ -1,243 +1,181 @@ -// NOTE: Entries should be created with `cargo dev deprecate` +// This file is managed by `cargo dev rename_lint` and `cargo dev deprecate_lint`. +// Prefer to use those when possible. -/// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This -/// enables the simple extraction of the metadata without changing the current deprecation -/// declaration. -pub struct ClippyDeprecatedLint { - #[allow(dead_code)] - pub desc: &'static str, +macro_rules! declare_with_version { + ($name:ident($name_version:ident): &[$ty:ty] = &[$( + #[clippy::version = $version:literal] + $e:expr, + )*]) => { + pub static $name: &[$ty] = &[$($e),*]; + #[allow(unused)] + pub static $name_version: &[&str] = &[$($version),*]; + }; } -#[macro_export] -macro_rules! declare_deprecated_lint { - { $(#[$attr:meta])* pub $name: ident, $reason: literal} => { - $(#[$attr])* - #[allow(dead_code)] - pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint { - desc: $reason - }; - } -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `assert!(a == b)` and recommend - /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011. +#[rustfmt::skip] +declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ #[clippy::version = "pre 1.29.0"] - pub SHOULD_ASSERT_EQ, - "`assert!()` will be more flexible with RFC 2011" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `Vec::extend`, which was slower than - /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true. + ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"), #[clippy::version = "pre 1.29.0"] - pub EXTEND_FROM_SLICE, - "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// `Range::step_by(0)` used to be linted since it's - /// an infinite iterator, which is better expressed by `iter::repeat`, - /// but the method has been removed for `Iterator::step_by` which panics - /// if given a zero + ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"), #[clippy::version = "pre 1.29.0"] - pub RANGE_STEP_BY_ZERO, - "`iterator.step_by(0)` panics nowadays" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `Vec::as_slice`, which was unstable with good - /// stable alternatives. `Vec::as_slice` has now been stabilized. + ("clippy::range_step_by_zero", "`Iterator::step_by(0)` now panics and is no longer an infinite iterator"), #[clippy::version = "pre 1.29.0"] - pub UNSTABLE_AS_SLICE, - "`Vec::as_slice` has been stabilized in 1.7" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This used to check for `Vec::as_mut_slice`, which was unstable with good - /// stable alternatives. `Vec::as_mut_slice` has now been stabilized. + ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"), #[clippy::version = "pre 1.29.0"] - pub UNSTABLE_AS_MUT_SLICE, - "`Vec::as_mut_slice` has been stabilized in 1.7" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint should never have applied to non-pointer types, as transmuting - /// between non-pointer types of differing alignment is well-defined behavior (it's semantically - /// equivalent to a memcpy). This lint has thus been refactored into two separate lints: - /// cast_ptr_alignment and transmute_ptr_to_ptr. + ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"), #[clippy::version = "pre 1.29.0"] - pub MISALIGNED_TRANSMUTE, - "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint is too subjective, not having a good reason for being in clippy. - /// Additionally, compound assignment operators may be overloaded separately from their non-assigning - /// counterparts, so this lint may suggest a change in behavior or the code may not compile. + ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"), #[clippy::version = "1.30.0"] - pub ASSIGN_OPS, - "using compound assignment operators (e.g., `+=`) is harmless" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The original rule will only lint for `if let`. After - /// making it support to lint `match`, naming as `if let` is not suitable for it. - /// So, this lint is deprecated. + ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"), #[clippy::version = "pre 1.29.0"] - pub IF_LET_REDUNDANT_PATTERN_MATCHING, - "this lint has been changed to redundant_pattern_matching" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint used to suggest replacing `let mut vec = - /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The - /// replacement has very different performance characteristics so the lint is - /// deprecated. - #[clippy::version = "pre 1.29.0"] - pub UNSAFE_VECTOR_INITIALIZATION, - "the replacement suggested by this lint had substantially different behavior" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been superseded by #[must_use] in rustc. + ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"), #[clippy::version = "1.39.0"] - pub UNUSED_COLLECT, - "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// Associated-constants are now preferred. + ("clippy::unused_collect", "`Iterator::collect` is now marked as `#[must_use]`"), #[clippy::version = "1.44.0"] - pub REPLACE_CONSTS, - "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The regex! macro does not exist anymore. + ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"), #[clippy::version = "1.47.0"] - pub REGEX_MACRO, - "the regex! macro has been removed from the regex crate in 2018" -} + ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"), + #[clippy::version = "1.54.0"] + ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"), + #[clippy::version = "1.54.0"] + ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"), + // end deprecated lints. used by `cargo dev deprecate_lint` +]} -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been replaced by `manual_find_map`, a - /// more specific lint. +#[rustfmt::skip] +declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ + #[clippy::version = ""] + ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"), + #[clippy::version = ""] + ("clippy::blacklisted_name", "clippy::disallowed_names"), + #[clippy::version = ""] + ("clippy::block_in_if_condition_expr", "clippy::blocks_in_conditions"), + #[clippy::version = ""] + ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_conditions"), + #[clippy::version = ""] + ("clippy::blocks_in_if_conditions", "clippy::blocks_in_conditions"), + #[clippy::version = ""] + ("clippy::box_vec", "clippy::box_collection"), + #[clippy::version = ""] + ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), + #[clippy::version = ""] + ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + #[clippy::version = ""] + ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"), + #[clippy::version = ""] + ("clippy::disallowed_method", "clippy::disallowed_methods"), + #[clippy::version = ""] + ("clippy::disallowed_type", "clippy::disallowed_types"), + #[clippy::version = ""] + ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), #[clippy::version = "1.51.0"] - pub FIND_MAP, - "this lint has been replaced by `manual_find_map`, a more specific lint" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been replaced by `manual_filter_map`, a - /// more specific lint. + ("clippy::find_map", "clippy::manual_find_map"), #[clippy::version = "1.53.0"] - pub FILTER_MAP, - "this lint has been replaced by `manual_filter_map`, a more specific lint" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The `avoid_breaking_exported_api` config option was added, which - /// enables the `enum_variant_names` lint for public items. - #[clippy::version = "1.54.0"] - pub PUB_ENUM_VARIANT_NAMES, - "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// The `avoid_breaking_exported_api` config option was added, which - /// enables the `wrong_self_conversion` lint for public items. - #[clippy::version = "1.54.0"] - pub WRONG_PUB_SELF_CONVENTION, - "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect the `#[cfg(features)]` and `#[cfg(tests)]` typos. - /// - /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs + ("clippy::filter_map", "clippy::manual_filter_map"), + #[clippy::version = ""] + ("clippy::identity_conversion", "clippy::useless_conversion"), + #[clippy::version = "pre 1.29.0"] + ("clippy::if_let_redundant_pattern_matching", "clippy::redundant_pattern_matching"), + #[clippy::version = ""] + ("clippy::if_let_some_result", "clippy::match_result_ok"), + #[clippy::version = ""] + ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"), + #[clippy::version = ""] + ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"), + #[clippy::version = ""] + ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"), + #[clippy::version = ""] + ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), + #[clippy::version = ""] + ("clippy::new_without_default_derive", "clippy::new_without_default"), + #[clippy::version = ""] + ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), + #[clippy::version = ""] + ("clippy::option_expect_used", "clippy::expect_used"), + #[clippy::version = ""] + ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), + #[clippy::version = ""] + ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), + #[clippy::version = ""] + ("clippy::option_unwrap_used", "clippy::unwrap_used"), + #[clippy::version = ""] + ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"), + #[clippy::version = ""] + ("clippy::ref_in_deref", "clippy::needless_borrow"), + #[clippy::version = ""] + ("clippy::result_expect_used", "clippy::expect_used"), + #[clippy::version = ""] + ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), + #[clippy::version = ""] + ("clippy::result_unwrap_used", "clippy::unwrap_used"), + #[clippy::version = ""] + ("clippy::single_char_push_str", "clippy::single_char_add_str"), + #[clippy::version = ""] + ("clippy::stutter", "clippy::module_name_repetitions"), + #[clippy::version = ""] + ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"), + #[clippy::version = ""] + ("clippy::to_string_in_display", "clippy::recursive_format_impl"), + #[clippy::version = ""] + ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"), + #[clippy::version = ""] + ("clippy::zero_width_space", "clippy::invisible_characters"), + #[clippy::version = ""] + ("clippy::cast_ref_to_mut", "invalid_reference_casting"), + #[clippy::version = ""] + ("clippy::clone_double_ref", "suspicious_double_ref_op"), + #[clippy::version = ""] + ("clippy::cmp_nan", "invalid_nan_comparisons"), + #[clippy::version = ""] + ("clippy::drop_bounds", "drop_bounds"), + #[clippy::version = ""] + ("clippy::drop_copy", "dropping_copy_types"), + #[clippy::version = ""] + ("clippy::drop_ref", "dropping_references"), + #[clippy::version = ""] + ("clippy::fn_null_check", "useless_ptr_null_checks"), + #[clippy::version = ""] + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), + #[clippy::version = ""] + ("clippy::forget_copy", "forgetting_copy_types"), + #[clippy::version = ""] + ("clippy::forget_ref", "forgetting_references"), + #[clippy::version = ""] + ("clippy::into_iter_on_array", "array_into_iter"), + #[clippy::version = ""] + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + #[clippy::version = ""] + ("clippy::invalid_ref", "invalid_value"), + #[clippy::version = ""] + ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"), + #[clippy::version = ""] + ("clippy::let_underscore_drop", "let_underscore_drop"), #[clippy::version = "1.80.0"] - pub MAYBE_MISUSED_CFG, - "this lint has been replaced by `unexpected_cfgs`" -} - -declare_deprecated_lint! { - /// ### What it does - /// Nothing. This lint has been deprecated. - /// - /// ### Deprecation reason - /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect invalid `#[cfg(linux)]` attributes. - /// - /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs + ("clippy::maybe_misused_cfg", "unexpected_cfgs"), + #[clippy::version = ""] + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), #[clippy::version = "1.80.0"] - pub MISMATCHED_TARGET_OS, - "this lint has been replaced by `unexpected_cfgs`" -} + ("clippy::mismatched_target_os", "unexpected_cfgs"), + #[clippy::version = ""] + ("clippy::panic_params", "non_fmt_panics"), + #[clippy::version = ""] + ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), + #[clippy::version = ""] + ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + #[clippy::version = ""] + ("clippy::undropped_manually_drops", "undropped_manually_drops"), + #[clippy::version = ""] + ("clippy::unknown_clippy_lints", "unknown_lints"), + #[clippy::version = ""] + ("clippy::unused_label", "unused_labels"), + #[clippy::version = ""] + ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"), + #[clippy::version = ""] + ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"), + // end renamed lints. used by `cargo dev rename_lint` +]} diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 253f9959e13e..d0cb24884686 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs}; +use clippy_utils::ty::{implements_trait, is_manually_drop}; use clippy_utils::{ expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy, ExprUseNode, @@ -947,7 +947,7 @@ fn report<'tcx>( let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); - let (_, ref_count) = peel_mid_ty_refs(ty); + let (_, ref_count) = peel_middle_ty_refs(ty); let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { // a deref call changing &T -> &U requires two deref operators the first time // this occurs. One to remove the reference, a second to call the deref impl. diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 5b6a5b08aa94..d7b3a7c74f3c 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; -use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args}; +use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; use pulldown_cmark::Event::{ Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, TaskListMarker, Text, @@ -768,7 +768,7 @@ fn check_doc<'a, Events: Iterator, Range { + Text(text) => { paragraph_range.end = range.end; let range_ = range.clone(); ticks_unbalanced |= text.contains('`') @@ -812,7 +812,8 @@ fn check_doc<'a, Events: Iterator, Range {} } } headers @@ -857,7 +858,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { "assert" | "assert_eq" | "assert_ne" ) { - self.is_const = in_constant(self.cx, expr.hir_id); + self.is_const = self.cx.tcx.hir().is_inside_const_context(expr.hir_id); self.panic_span = Some(macro_call.span); } } diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index 4a6ffcd9a788..c7dd7292a14b 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_must_use_func_call; use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node}; @@ -126,14 +126,14 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { }, _ => return, }; - span_lint_and_note( - cx, - lint, - expr.span, - msg, - note_span, - format!("argument has type `{arg_ty}`"), - ); + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let note = format!("argument has type `{arg_ty}`"); + if let Some(span) = note_span { + diag.span_note(span, note); + } else { + diag.note(note); + } + }); } } } 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 7a6dc4697276..02f9c2c36488 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 @@ -1,6 +1,6 @@ //! Lint on if expressions with an else if, but without a final else branch. -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -54,13 +54,15 @@ impl EarlyLintPass for ElseIfWithoutElse { && let ExprKind::If(_, _, None) = els.kind && !in_external_macro(cx.sess(), item.span) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, ELSE_IF_WITHOUT_ELSE, els.span, "`if` expression with an `else if`, but without a final `else`", - None, - "add an `else` block here", + |diag| { + diag.help("add an `else` block here"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs index c5fc72b5e2d8..b66dd2108fc9 100644 --- a/src/tools/clippy/clippy_lints/src/empty_drop.rs +++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::peel_blocks; use rustc_errors::Applicability; use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; @@ -50,15 +50,14 @@ impl LateLintPass<'_> for EmptyDrop { && block.stmts.is_empty() && block.expr.is_none() { - span_lint_and_sugg( - cx, - EMPTY_DROP, - item.span, - "empty drop implementation", - "try removing this impl", - String::new(), - Applicability::MaybeIncorrect, - ); + span_lint_and_then(cx, EMPTY_DROP, item.span, "empty drop implementation", |diag| { + diag.span_suggestion_hidden( + item.span, + "try removing this impl", + String::new(), + Applicability::MaybeIncorrect, + ); + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs index 5bba9c562b93..209104c5385c 100644 --- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs @@ -7,7 +7,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; use rustc_span::Symbol; -use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -141,52 +140,6 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix _ => return, }; - let mut help = None; - - 'build_help: { - // all lints disallowed, don't give help here - if [&[lint], other_lints.as_slice()] - .concat() - .iter() - .all(|lint| !lint.allowed(cx, expr)) - { - break 'build_help; - } - - // ne_bytes and all other lints allowed - if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) { - help = Some(Cow::Borrowed("specify the desired endianness explicitly")); - break 'build_help; - } - - // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but - // le_bytes is not - if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) { - help = Some(Cow::Borrowed("use the native endianness instead")); - break 'build_help; - } - - let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr)); - let len = allowed_lints.clone().count(); - - let mut help_str = "use ".to_owned(); - - for (i, lint) in allowed_lints.enumerate() { - let only_one = len == 1; - if !only_one { - help_str.push_str("either of "); - } - - help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix))); - - if i != len && !only_one { - help_str.push_str("or "); - } - } - - help = Some(Cow::Owned(help_str + "instead")); - } - span_lint_and_then( cx, lint.as_lint(), @@ -198,9 +151,47 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix if prefix == Prefix::To { " method" } else { "" }, ), move |diag| { - if let Some(help) = help { - diag.help(help); + // all lints disallowed, don't give help here + if [&[lint], other_lints.as_slice()] + .concat() + .iter() + .all(|lint| !lint.allowed(cx, expr)) + { + return; } + + // ne_bytes and all other lints allowed + if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) { + diag.help("specify the desired endianness explicitly"); + return; + } + + // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but + // le_bytes is not + if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) { + diag.help("use the native endianness instead"); + return; + } + + let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr)); + let len = allowed_lints.clone().count(); + + let mut help_str = "use ".to_owned(); + + for (i, lint) in allowed_lints.enumerate() { + let only_one = len == 1; + if !only_one { + help_str.push_str("either of "); + } + + help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix))); + + if i != len && !only_one { + help_str.push_str("or "); + } + } + help_str.push_str("instead"); + diag.help(help_str); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index 30eb643c42ec..e54cd248ead2 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { .const_eval_poly(def_id.to_def_id()) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty)); - if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) { + if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) { if let ty::Adt(adt, _) = ty.kind() { if adt.is_enum() { ty = adt.repr().discr_type().to_ty(cx.tcx); diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 0ed7859418bc..5a7226d590c4 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -9,8 +9,7 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TyCtxt, - TypeVisitableExt, TypeckResults, + self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -74,159 +73,184 @@ declare_clippy_lint! { declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]); impl<'tcx> LateLintPass<'tcx> for EtaReduction { - #[allow(clippy::too_many_lines)] - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let body = if let ExprKind::Closure(c) = expr.kind - && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) - && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_)) - && !expr.span.from_expansion() - { - cx.tcx.hir().body(c.body) - } else { - return; - }; - - if body.value.span.from_expansion() { - if body.params.is_empty() { - if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) { - // replace `|| vec![]` with `Vec::new` - span_lint_and_sugg( - cx, - REDUNDANT_CLOSURE, - expr.span, - "redundant closure", - "replace the closure with `Vec::new`", - "std::vec::Vec::new".into(), - Applicability::MachineApplicable, - ); - } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::MethodCall(_method, receiver, args, _) = expr.kind { + for arg in args { + check_clousure(cx, Some(receiver), arg); } - // skip `foo(|| macro!())` - return; } - - let typeck = cx.typeck_results(); - let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() { - closure_subs.as_closure() - } else { - return; - }; - - if is_adjusted(cx, body.value) { - return; + if let ExprKind::Call(func, args) = expr.kind { + check_clousure(cx, None, func); + for arg in args { + check_clousure(cx, None, arg); + } } + } +} - match body.value.kind { - ExprKind::Call(callee, args) - if matches!( - callee.kind, - ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..)) - ) => +#[allow(clippy::too_many_lines)] +fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) { + let body = if let ExprKind::Closure(c) = expr.kind + && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) + && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_)) + && !expr.span.from_expansion() + { + cx.tcx.hir().body(c.body) + } else { + return; + }; + + if body.value.span.from_expansion() { + if body.params.is_empty() { + if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) { + // replace `|| vec![]` with `Vec::new` + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE, + expr.span, + "redundant closure", + "replace the closure with `Vec::new`", + "std::vec::Vec::new".into(), + Applicability::MachineApplicable, + ); + } + } + // skip `foo(|| macro!())` + return; + } + + if is_adjusted(cx, body.value) { + return; + } + + let typeck = cx.typeck_results(); + let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() { + closure_subs.as_closure() + } else { + return; + }; + let closure_sig = cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder(); + match body.value.kind { + ExprKind::Call(callee, args) + if matches!( + callee.kind, + ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..)) + ) => + { + let callee_ty_raw = typeck.expr_ty(callee); + let callee_ty = callee_ty_raw.peel_refs(); + if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) + || !check_inputs(typeck, body.params, None, args) { - let callee_ty_raw = typeck.expr_ty(callee); - let callee_ty = callee_ty_raw.peel_refs(); - if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc)) - || !check_inputs(typeck, body.params, None, args) - { - return; - } - let callee_ty_adjusted = typeck - .expr_adjustments(callee) - .last() - .map_or(callee_ty, |a| a.target.peel_refs()); + return; + } + let callee_ty_adjusted = typeck + .expr_adjustments(callee) + .last() + .map_or(callee_ty, |a| a.target.peel_refs()); - let sig = match callee_ty_adjusted.kind() { - ty::FnDef(def, _) => { - // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location` - if cx.tcx.has_attr(*def, sym::track_caller) { - return; - } + let sig = match callee_ty_adjusted.kind() { + ty::FnDef(def, _) => { + // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location` + if cx.tcx.has_attr(*def, sym::track_caller) { + return; + } - cx.tcx.fn_sig(def).skip_binder().skip_binder() - }, - ty::FnPtr(sig) => sig.skip_binder(), - ty::Closure(_, subs) => cx - .tcx - .signature_unclosure(subs.as_closure().sig(), Safety::Safe) - .skip_binder(), - _ => { - if typeck.type_dependent_def_id(body.value.hir_id).is_some() - && let subs = typeck.node_args(body.value.hir_id) - && let output = typeck.expr_ty(body.value) - && let ty::Tuple(tys) = *subs.type_at(1).kind() - { - cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, Abi::Rust) - } else { - return; - } - }, - }; - if check_sig(cx, closure, sig) - && let generic_args = typeck.node_args(callee.hir_id) - // Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not - // `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result - // in a type which is `'static`. - // For now ignore all callee types which reference a type parameter. - && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) - { - span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { - if let Some(mut snippet) = snippet_opt(cx, callee.span) { - if path_to_local(callee).map_or(false, |l| { - // FIXME: Do we really need this `local_used_in` check? - // Isn't it checking something like... `callee(callee)`? - // If somehow this check is needed, add some test for it, - // 'cuz currently nothing changes after deleting this check. - local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) - }) { - match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) { - // Mutable closure is used after current expr; we cannot consume it. - Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), - Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { - snippet = format!("&{snippet}"); - }, - _ => (), - } + cx.tcx.fn_sig(def).skip_binder().skip_binder() + }, + ty::FnPtr(sig) => sig.skip_binder(), + ty::Closure(_, subs) => cx + .tcx + .signature_unclosure(subs.as_closure().sig(), Safety::Safe) + .skip_binder(), + _ => { + if typeck.type_dependent_def_id(body.value.hir_id).is_some() + && let subs = typeck.node_args(body.value.hir_id) + && let output = typeck.expr_ty(body.value) + && let ty::Tuple(tys) = *subs.type_at(1).kind() + { + cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, Abi::Rust) + } else { + return; + } + }, + }; + if let Some(outer) = outer_receiver + && ty_has_static(sig.output()) + && let generic_args = typeck.node_args(outer.hir_id) + // HACK: Given a closure in `T.method(|| f())`, where `fn f() -> U where U: 'static`, `T.method(f)` + // will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when + // T is a generic type. For example, return type of `Option::as_deref()` is a generic. + // So we have a hack like this. + && generic_args.len() > 0 + { + return; + } + if check_sig(closure_sig, sig) + && let generic_args = typeck.node_args(callee.hir_id) + // Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not + // `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result + // in a type which is `'static`. + // For now ignore all callee types which reference a type parameter. + && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) + { + span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { + if let Some(mut snippet) = snippet_opt(cx, callee.span) { + if path_to_local(callee).map_or(false, |l| { + // FIXME: Do we really need this `local_used_in` check? + // Isn't it checking something like... `callee(callee)`? + // If somehow this check is needed, add some test for it, + // 'cuz currently nothing changes after deleting this check. + local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) + }) { + match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { + // Mutable closure is used after current expr; we cannot consume it. + Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), + Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { + snippet = format!("&{snippet}"); + }, + _ => (), } - diag.span_suggestion( - expr.span, - "replace the closure with the function itself", - snippet, - Applicability::MachineApplicable, - ); } - }); - } - }, - ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => { - if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id) - && !cx.tcx.has_attr(method_def_id, sym::track_caller) - && check_sig(cx, closure, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) - { - span_lint_and_then( - cx, - REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - expr.span, - "redundant closure", - |diag| { - let args = typeck.node_args(body.value.hir_id); - let caller = self_.hir_id.owner.def_id; - let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args); - diag.span_suggestion( - expr.span, - "replace the closure with the method itself", - format!("{}::{}", type_name, path.ident.name), - Applicability::MachineApplicable, - ); - }, - ); - } - }, - _ => (), - } + diag.span_suggestion( + expr.span, + "replace the closure with the function itself", + snippet, + Applicability::MachineApplicable, + ); + } + }); + } + }, + ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => { + if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id) + && !cx.tcx.has_attr(method_def_id, sym::track_caller) + && check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder()) + { + span_lint_and_then( + cx, + REDUNDANT_CLOSURE_FOR_METHOD_CALLS, + expr.span, + "redundant closure", + |diag| { + let args = typeck.node_args(body.value.hir_id); + let caller = self_.hir_id.owner.def_id; + let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args); + diag.span_suggestion( + expr.span, + "replace the closure with the method itself", + format!("{}::{}", type_name, path.ident.name), + Applicability::MachineApplicable, + ); + }, + ); + } + }, + _ => (), } } @@ -251,12 +275,8 @@ fn check_inputs( }) } -fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs>, call_sig: FnSig<'_>) -> bool { - call_sig.safety == Safety::Safe - && !has_late_bound_to_non_late_bound_regions( - cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder(), - call_sig, - ) +fn check_sig<'tcx>(closure_sig: FnSig<'tcx>, call_sig: FnSig<'tcx>) -> bool { + call_sig.safety == Safety::Safe && !has_late_bound_to_non_late_bound_regions(closure_sig, call_sig) } /// This walks through both signatures and checks for any time a late-bound region is expected by an @@ -265,7 +285,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs>, c /// This is needed because rustc is unable to late bind early-bound regions in a function signature. fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'_>) -> bool { fn check_region(from_region: Region<'_>, to_region: Region<'_>) -> bool { - matches!(from_region.kind(), RegionKind::ReBound(..)) && !matches!(to_region.kind(), RegionKind::ReBound(..)) + from_region.is_bound() && !to_region.is_bound() } fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool { @@ -318,3 +338,8 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<' .zip(to_sig.inputs_and_output) .any(|(from_ty, to_ty)| check_ty(from_ty, to_ty)) } + +fn ty_has_static(ty: Ty<'_>) -> bool { + ty.walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if re.is_static())) +} diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index 0f4176ec73bb..9bf3baba4b59 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -88,11 +88,11 @@ impl LateLintPass<'_> for ExhaustiveItems { && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) && fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public()) { - let suggestion_span = item.span.shrink_to_lo(); - let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); span_lint_and_then(cx, lint, item.span, msg, |diag| { + let suggestion_span = item.span.shrink_to_lo(); + let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); let sugg = format!("#[non_exhaustive]\n{indent}"); - diag.span_suggestion( + diag.span_suggestion_verbose( suggestion_span, "try adding #[non_exhaustive]", sugg, diff --git a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs index bb74e345703f..95b8e882da79 100644 --- a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -62,13 +62,15 @@ impl EarlyLintPass for FieldScopedVisibilityModifiers { // pub(self) is equivalent to not using pub at all, so we ignore it continue; } - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, FIELD_SCOPED_VISIBILITY_MODIFIERS, field.vis.span, "scoped visibility modifier on a field", - None, - "consider making the field private and adding a scoped visibility method for it", + |diag| { + diag.help("consider making the field private and adding a scoped visibility method for it"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index 6adcd2235dc5..f095c1add91f 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -1,7 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal; use rustc_ast::ast::{self, LitFloatType, LitKind}; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, FloatTy}; @@ -105,32 +105,43 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { if is_whole && !sym_str.contains(['e', 'E']) { // Normalize the literal by stripping the fractional portion if sym_str.split('.').next().unwrap() != float_str { - // If the type suffix is missing the suggestion would be - // incorrectly interpreted as an integer so adding a `.0` - // suffix to prevent that. - if type_suffix.is_none() { - float_str.push_str(".0"); - } - - span_lint_and_sugg( + span_lint_and_then( cx, LOSSY_FLOAT_LITERAL, expr.span, "literal cannot be represented as the underlying type without loss of precision", - "consider changing the type or replacing it with", - numeric_literal::format(&float_str, type_suffix, true), - Applicability::MachineApplicable, + |diag| { + // If the type suffix is missing the suggestion would be + // incorrectly interpreted as an integer so adding a `.0` + // suffix to prevent that. + if type_suffix.is_none() { + float_str.push_str(".0"); + } + diag.span_suggestion_with_style( + expr.span, + "consider changing the type or replacing it with", + numeric_literal::format(&float_str, type_suffix, true), + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + }, ); } } else if digits > max as usize && float_str.len() < sym_str.len() { - span_lint_and_sugg( + span_lint_and_then( cx, EXCESSIVE_PRECISION, expr.span, "float has excessive precision", - "consider changing the type or truncating it to", - numeric_literal::format(&float_str, type_suffix, true), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion_with_style( + expr.span, + "consider changing the type or truncating it to", + numeric_literal::format(&float_str, type_suffix, true), + Applicability::MachineApplicable, + SuggestionStyle::ShowAlways, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index 68bdf88d0a7e..bf4bcabfe891 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -1,9 +1,9 @@ use clippy_utils::consts::Constant::{Int, F32, F64}; -use clippy_utils::consts::{constant, constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ - eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal, - peel_blocks, sugg, + eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate, + numeric_literal, peel_blocks, sugg, }; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; @@ -112,7 +112,7 @@ declare_lint_pass!(FloatingPointArithmetic => [ // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> { - if let Some(value) = constant(cx, cx.typeck_results(), base) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(base) { if F32(2.0) == value || F64(2.0) == value { return Some("log2"); } else if F32(10.0) == value || F64(10.0) == value { @@ -182,10 +182,8 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) { rhs, ) = receiver.kind { - let recv = match ( - constant(cx, cx.typeck_results(), lhs), - constant(cx, cx.typeck_results(), rhs), - ) { + let ecx = ConstEvalCtxt::new(cx); + let recv = match (ecx.eval(lhs), ecx.eval(rhs)) { (Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs, (_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs, _ => return, @@ -230,7 +228,7 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option { fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { // Check receiver - if let Some(value) = constant(cx, cx.typeck_results(), receiver) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) { if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value { Some("exp") } else if F32(2.0) == value || F64(2.0) == value { @@ -251,7 +249,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } // Check argument - if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value { ( SUBOPTIMAL_FLOPS, @@ -291,7 +289,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) { - if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) { + if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) { if value == Int(2) { if let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) { @@ -397,8 +395,9 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option { ) = &add_rhs.kind && lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi" - && let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1) - && let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(lvalue) = ecx.eval(largs_1) + && let Some(rvalue) = ecx.eval(rargs_1) && Int(2) == lvalue && Int(2) == rvalue { @@ -438,7 +437,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = expr.kind && cx.typeck_results().expr_ty(lhs).is_floating_point() - && let Some(value) = constant(cx, cx.typeck_results(), rhs) + && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs) && (F32(1.0) == value || F64(1.0) == value) && let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind && cx.typeck_results().expr_ty(self_arg).is_floating_point() @@ -552,7 +551,7 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) - /// Returns true iff expr is some zero literal fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match constant_simple(cx, cx.typeck_results(), expr) { + match ConstEvalCtxt::new(cx).eval_simple(expr) { Some(Int(i)) => i == 0, Some(F32(f)) => f == 0.0, Some(F64(f)) => f == 0.0, @@ -696,8 +695,9 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { mul_lhs, mul_rhs, ) = &div_lhs.kind - && let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs) - && let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs) + && let ecx = ConstEvalCtxt::new(cx) + && let Some(rvalue) = ecx.eval(div_rhs) + && let Some(lvalue) = ecx.eval(mul_rhs) { // TODO: also check for constant values near PI/180 or 180/PI if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) @@ -753,7 +753,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // All of these operations are currently not const and are in std. - if in_constant(cx, expr.hir_id) { + if is_in_const_context(cx) { return; } diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs index a75538dd329b..d05c5a01f41c 100644 --- a/src/tools/clippy/clippy_lints/src/format_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{higher, match_def_path, paths}; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource}; @@ -81,13 +81,15 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString { _ => return, }; if is_format(cx, arg) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, FORMAT_PUSH_STRING, expr.span, "`format!(..)` appended to existing `String`", - None, - "consider using `write!` to avoid the extra allocation", + |diag| { + diag.help("consider using `write!` to avoid the extra allocation"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs index 9acb72b2e372..6ab7bbc2dfc0 100644 --- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs +++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{in_constant, is_integer_literal}; +use clippy_utils::{is_in_const_context, is_integer_literal}; use rustc_errors::Applicability; use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -62,8 +62,8 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { && matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)) // do not lint in constant context, because the suggestion won't work. - // NB: keep this check until a new `const_trait_impl` is available and stablized. - && !in_constant(cx, exp.hir_id) + // NB: keep this check until a new `const_trait_impl` is available and stabilized. + && !is_in_const_context(cx) { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { let ty = cx.typeck_results().expr_ty(expr); diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs index b38cc7b36a12..1c52514a330d 100644 --- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs +++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{higher, SpanlessEq}; +use clippy_utils::{eq_expr_value, higher}; use core::ops::ControlFlow; use rustc_errors::Diag; use rustc_hir::{Expr, ExprKind}; @@ -51,53 +51,45 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { if_else: Some(if_else), .. }) = higher::IfLet::hir(cx, expr) + && let Some(op_mutex) = for_each_expr_without_closures(let_expr, |e| mutex_lock_call(cx, e, None)) + && let Some(arm_mutex) = + for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, e, Some(op_mutex))) { - let is_mutex_lock = |e: &'tcx Expr<'tcx>| { - if let Some(mutex) = is_mutex_lock_call(cx, e) { - ControlFlow::Break(mutex) - } else { - ControlFlow::Continue(()) - } + let diag = |diag: &mut Diag<'_, ()>| { + diag.span_label( + op_mutex.span, + "this Mutex will remain locked for the entire `if let`-block...", + ); + diag.span_label( + arm_mutex.span, + "... and is tried to lock again here, which will always deadlock.", + ); + diag.help("move the lock call outside of the `if let ...` expression"); }; - - let op_mutex = for_each_expr_without_closures(let_expr, is_mutex_lock); - if let Some(op_mutex) = op_mutex { - let arm_mutex = for_each_expr_without_closures((if_then, if_else), is_mutex_lock); - if let Some(arm_mutex) = arm_mutex - && SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) - { - let diag = |diag: &mut Diag<'_, ()>| { - diag.span_label( - op_mutex.span, - "this Mutex will remain locked for the entire `if let`-block...", - ); - diag.span_label( - arm_mutex.span, - "... and is tried to lock again here, which will always deadlock.", - ); - diag.help("move the lock call outside of the `if let ...` expression"); - }; - span_lint_and_then( - cx, - IF_LET_MUTEX, - expr.span, - "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", - diag, - ); - } - } + span_lint_and_then( + cx, + IF_LET_MUTEX, + expr.span, + "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", + diag, + ); } } } -fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +fn mutex_lock_call<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op_mutex: Option<&'tcx Expr<'_>>, +) -> ControlFlow<&'tcx Expr<'tcx>> { if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.as_str() == "lock" && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs() && is_type_diagnostic_item(cx, ty, sym::Mutex) + && op_mutex.map_or(true, |op| eq_expr_value(cx, self_arg, op)) { - Some(self_arg) + ControlFlow::Break(self_arg) } else { - None + ControlFlow::Continue(()) } } diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs index 2f6daeeb90d9..0ebd8d0c237b 100644 --- a/src/tools/clippy/clippy_lints/src/if_not_else.rs +++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs @@ -1,7 +1,7 @@ //! lint on if branches that could be swapped so no `!` operation is necessary //! on the condition -use clippy_utils::consts::{constant_simple, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_else_clause; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; @@ -49,7 +49,7 @@ declare_clippy_lint! { declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]); fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { - if let Some(value) = constant_simple(cx, cx.typeck_results(), expr) { + if let Some(value) = ConstEvalCtxt::new(cx).eval_simple(expr) { return Constant::Int(0) == value; } false diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index 39ea16b05d1a..0bca53c1536d 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -1,10 +1,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; -use clippy_utils::{contains_return, higher, in_constant, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; +use clippy_utils::{ + contains_return, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, path_res, peel_blocks, +}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; @@ -76,37 +78,44 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !is_else_clause(cx.tcx, expr) - && !in_constant(cx, expr.hir_id) + && !is_in_const_context(cx) && !in_external_macro(cx.sess(), expr.span) && self.msrv.meets(msrvs::BOOL_THEN) && !contains_return(then_block.stmts) { - let mut app = Applicability::Unspecified; - let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) - .maybe_par() - .to_string(); - let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; - let mut method_body = if then_block.stmts.is_empty() { - arg_snip.into_owned() - } else { - format!("{{ /* snippet */ {arg_snip} }}") - }; let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) { "then_some" } else { - method_body.insert_str(0, "|| "); "then" }; - let help = - format!("consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",); - span_lint_and_help( + span_lint_and_then( cx, IF_THEN_SOME_ELSE_NONE, expr.span, format!("this could be simplified with `bool::{method_name}`"), - None, - help, + |diag| { + let mut app = Applicability::Unspecified; + let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) + .maybe_par() + .to_string(); + let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; + let method_body = if let Some(first_stmt) = then_block.stmts.first() { + let (block_snippet, _) = + snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app); + let closure = if method_name == "then" { "|| " } else { "" }; + format!("{closure} {{ {block_snippet}; {arg_snip} }}") + } else { + arg_snip.into_owned() + }; + + diag.span_suggestion( + expr.span, + "try", + format!("{cond_snip}.{method_name}({method_body})"), + app, + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 344a04e6e7e8..e56f33f8dcfe 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::collections::BTreeMap; -use rustc_errors::Diag; +use rustc_errors::{Applicability, 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}; @@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; use rustc_span::Span; -use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; @@ -77,33 +77,32 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { &generics_snip[1..generics_snip.len() - 1] }; - multispan_sugg( - diag, - "consider adding a type parameter", - vec![ - ( - generics_suggestion_span, - format!( - "<{generics_snip}{}S: ::std::hash::BuildHasher{}>", - if generics_snip.is_empty() { "" } else { ", " }, - if vis.suggestions.is_empty() { - "" - } else { - // request users to add `Default` bound so that generic constructors can be used - " + Default" - }, - ), + let mut suggestions = vec![ + ( + generics_suggestion_span, + format!( + "<{generics_snip}{}S: ::std::hash::BuildHasher{}>", + if generics_snip.is_empty() { "" } else { ", " }, + if vis.suggestions.is_empty() { + "" + } else { + // request users to add `Default` bound so that generic constructors can be used + " + Default" + }, ), - ( - target.span(), - format!("{}<{}, S>", target.type_name(), target.type_arguments(),), - ), - ], - ); + ), + ( + target.span(), + format!("{}<{}, S>", target.type_name(), target.type_arguments(),), + ), + ]; + suggestions.extend(vis.suggestions); - if !vis.suggestions.is_empty() { - multispan_sugg(diag, "...and use generic constructor", vis.suggestions); - } + diag.multipart_suggestion( + "add a type parameter for `BuildHasher`", + suggestions, + Applicability::MaybeIncorrect, + ); } if !cx.effective_visibilities.is_exported(item.owner_id.def_id) { diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs index a102b434cfab..b926e1e62ba0 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_return.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs @@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context, wal use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro}; use core::ops::ControlFlow; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::intravisit::FnKind; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -45,8 +45,6 @@ declare_clippy_lint! { declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]); fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, span, "..", &mut app); span_lint_hir_and_then( cx, IMPLICIT_RETURN, @@ -54,14 +52,20 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) { span, "missing `return` statement", |diag| { - diag.span_suggestion(span, "add `return` as shown", format!("return {snip}"), app); + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, span, "..", &mut app); + diag.span_suggestion_with_style( + span, + "add `return` as shown", + format!("return {snip}"), + app, + SuggestionStyle::ShowAlways, + ); }, ); } fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, expr_span: Span) { - let mut app = Applicability::MachineApplicable; - let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; span_lint_hir_and_then( cx, IMPLICIT_RETURN, @@ -69,11 +73,14 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp break_span, "missing `return` statement", |diag| { - diag.span_suggestion( + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; + diag.span_suggestion_with_style( break_span, "change `break` to `return` as shown", format!("return {snip}"), app, + SuggestionStyle::ShowAlways, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs index f225c6e7f049..dd5908553e59 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; @@ -117,11 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option { fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> { if let ExprKind::Binary(op, l, r) = expr.kind { - let tr = cx.typeck_results(); - if let Some(Constant::Int(c)) = constant(cx, tr, r) { + let ecx = ConstEvalCtxt::new(cx); + if let Some(Constant::Int(c)) = ecx.eval(r) { return Some((c, op.node, l)); }; - if let Some(Constant::Int(c)) = constant(cx, tr, l) { + if let Some(Constant::Int(c)) = ecx.eval(l) { return Some((c, invert_op(op.node)?, r)); } } diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 12ca6d43b27b..0ef5b803a89d 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -55,7 +55,6 @@ impl IncompatibleMsrv { } } - #[allow(clippy::cast_lossless)] fn get_def_id_version(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> RustcVersion { if let Some(version) = self.is_above_msrv.get(&def_id) { return *version; @@ -67,9 +66,9 @@ impl IncompatibleMsrv { since: StableSince::Version(version), .. } => Some(RustcVersion::new( - version.major as _, - version.minor as _, - version.patch as _, + version.major.into(), + version.minor.into(), + version.patch.into(), )), _ => None, }) { diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 526b4e1fba0e..2f9661c9ea38 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -1,6 +1,6 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; use clippy_utils::ty::is_copy; @@ -246,7 +246,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { && let parent_id = cx.tcx.parent_hir_id(expr.hir_id) && let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) && let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind - && let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr) + && let Some(Constant::Int(index_value)) = ConstEvalCtxt::new(cx).eval(index_expr) && let Ok(index_value) = index_value.try_into() && index_value < max_suggested_slice diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 6729c7c8d101..3ac50b8f1fba 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -1,7 +1,7 @@ //! lint on indexing and slicing operations use clippy_config::Conf; -use clippy_utils::consts::{constant, Constant}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; use clippy_utils::{higher, is_from_proc_macro}; @@ -70,8 +70,6 @@ declare_clippy_lint! { /// /// Use instead: /// ```no_run - /// # #![allow(unused)] - /// /// # let x = vec![0; 5]; /// # let y = [0, 1, 2, 3]; /// x.get(2); @@ -179,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { return; } // Index is a constant uint. - if let Some(constant) = constant(cx, cx.typeck_results(), index) { + if let Some(constant) = ConstEvalCtxt::new(cx).eval(index) { // only `usize` index is legal in rust array index // leave other type to rustc if let Constant::Int(off) = constant @@ -217,14 +215,15 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { /// Returns a tuple of options with the start and end (exclusive) values of /// the range. If the start or end is not constant, None is returned. fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option, Option) { - let s = range.start.map(|expr| constant(cx, cx.typeck_results(), expr)); + let ecx = ConstEvalCtxt::new(cx); + let s = range.start.map(|expr| ecx.eval(expr)); let start = match s { Some(Some(Constant::Int(x))) => Some(x), Some(_) => None, None => Some(0), }; - let e = range.end.map(|expr| constant(cx, cx.typeck_results(), expr)); + let e = range.end.map(|expr| ecx.eval(expr)); let end = match e { Some(Some(Constant::Int(x))) => { if range.limits == RangeLimits::Closed { diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs index fa7e7f6b76d1..676d50c4951b 100644 --- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs +++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs @@ -41,7 +41,6 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// let infinite_iter = 0..; - /// # #[allow(unused)] /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5)); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index 0d3786dad4b1..9eed7aa92433 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -1,6 +1,6 @@ //! lint on inherent implementations -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; @@ -105,13 +105,14 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first. lint_spans.sort_by_key(|x| x.0.lo()); for (span, first_span) in lint_spans { - span_lint_and_note( + span_lint_and_then( cx, MULTIPLE_INHERENT_IMPL, span, "multiple implementations of this structure", - Some(first_span), - "first implementation here", + |diag| { + diag.span_note(first_span, "first implementation here"); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs index 30f2285bdd23..1929fbded3b5 100644 --- a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs @@ -7,7 +7,7 @@ use rustc_span::Span; use clippy_utils::comparisons; use clippy_utils::comparisons::Rel; -use clippy_utils::consts::{constant_full_int, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; @@ -95,7 +95,7 @@ fn upcast_comparison_bounds_err<'tcx>( invert: bool, ) { if let Some((lb, ub)) = lhs_bounds { - if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) { + if let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) { if rel == Rel::Eq || rel == Rel::Ne { if norm_rhs_val < lb || norm_rhs_val > ub { err_upcast_comparison(cx, span, lhs, rel == Rel::Ne); diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index c67da689aaee..f2f841dcec33 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -1,5 +1,5 @@ use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -66,16 +66,18 @@ impl LateLintPass<'_> for LargeIncludeFile { && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) { - span_lint_and_note( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LARGE_INCLUDE_FILE, expr.span.source_callsite(), "attempted to include a large file", - None, - format!( - "the configuration allows a maximum size of {} bytes", - self.max_file_size - ), + |diag| { + diag.note(format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + )); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 8fa63f3e8fde..b522c22a44d7 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::diagnostics::span_lint_and_then; 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::{LetStmt, LocalSource, PatKind}; @@ -149,43 +149,53 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }); if contains_sync_guard { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_LOCK, local.span, "non-binding `let` on a synchronization lock", - None, - "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`", + |diag| { + diag.help( + "consider using an underscore-prefixed named \ + binding or dropping explicitly with `std::mem::drop`", + ); + }, ); } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_FUTURE, local.span, "non-binding `let` on a future", - None, - "consider awaiting the future or dropping explicitly with `std::mem::drop`", + |diag| { + diag.help("consider awaiting the future or dropping explicitly with `std::mem::drop`"); + }, ); } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_MUST_USE, local.span, "non-binding `let` on an expression with `#[must_use]` type", - None, - "consider explicitly using expression value", + |diag| { + diag.help("consider explicitly using expression value"); + }, ); } else if is_must_use_func_call(cx, init) { - span_lint_and_help( + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( cx, LET_UNDERSCORE_MUST_USE, local.span, "non-binding `let` on a result of a `#[must_use]` function", - None, - "consider explicitly using function result", + |diag| { + diag.help("consider explicitly using function result"); + }, ); } @@ -204,18 +214,22 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { return; } - span_lint_and_help( + span_lint_and_then( cx, LET_UNDERSCORE_UNTYPED, local.span, "non-binding `let` without a type annotation", - Some(Span::new( - local.pat.span.hi(), - local.pat.span.hi() + BytePos(1), - local.pat.span.ctxt(), - local.pat.span.parent(), - )), - "consider adding a type annotation", + |diag| { + diag.span_help( + Span::new( + local.pat.span.hi(), + local.pat.span.hi() + BytePos(1), + local.pat.span.ctxt(), + local.pat.span.parent(), + ), + "consider adding a type annotation", + ); + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/lib.deprecated.rs b/src/tools/clippy/clippy_lints/src/lib.deprecated.rs deleted file mode 100644 index 0d21261822dd..000000000000 --- a/src/tools/clippy/clippy_lints/src/lib.deprecated.rs +++ /dev/null @@ -1,78 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -{ - store.register_removed( - "clippy::should_assert_eq", - "`assert!()` will be more flexible with RFC 2011", - ); - store.register_removed( - "clippy::extend_from_slice", - "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice", - ); - store.register_removed( - "clippy::range_step_by_zero", - "`iterator.step_by(0)` panics nowadays", - ); - store.register_removed( - "clippy::unstable_as_slice", - "`Vec::as_slice` has been stabilized in 1.7", - ); - store.register_removed( - "clippy::unstable_as_mut_slice", - "`Vec::as_mut_slice` has been stabilized in 1.7", - ); - store.register_removed( - "clippy::misaligned_transmute", - "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", - ); - store.register_removed( - "clippy::assign_ops", - "using compound assignment operators (e.g., `+=`) is harmless", - ); - store.register_removed( - "clippy::if_let_redundant_pattern_matching", - "this lint has been changed to redundant_pattern_matching", - ); - store.register_removed( - "clippy::unsafe_vector_initialization", - "the replacement suggested by this lint had substantially different behavior", - ); - store.register_removed( - "clippy::unused_collect", - "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint", - ); - store.register_removed( - "clippy::replace_consts", - "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", - ); - store.register_removed( - "clippy::regex_macro", - "the regex! macro has been removed from the regex crate in 2018", - ); - store.register_removed( - "clippy::find_map", - "this lint has been replaced by `manual_find_map`, a more specific lint", - ); - store.register_removed( - "clippy::filter_map", - "this lint has been replaced by `manual_filter_map`, a more specific lint", - ); - store.register_removed( - "clippy::pub_enum_variant_names", - "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items", - ); - store.register_removed( - "clippy::wrong_pub_self_convention", - "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items", - ); - store.register_removed( - "clippy::maybe_misused_cfg", - "this lint has been replaced by `unexpected_cfgs`", - ); - store.register_removed( - "clippy::mismatched_target_os", - "this lint has been replaced by `unexpected_cfgs`", - ); -} diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index a388b6b2eaab..ce13a9afef5b 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -13,7 +13,6 @@ #![feature(stmt_expr_attributes)] #![feature(unwrap_infallible)] #![recursion_limit = "512"] -#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow( clippy::missing_docs_in_private_items, clippy::must_use_candidate, @@ -64,13 +63,11 @@ extern crate clippy_utils; #[macro_use] extern crate declare_clippy_lint; -#[cfg(feature = "internal")] -pub mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; mod declared_lints; -mod renamed_lints; +mod deprecated_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absolute_paths; @@ -372,6 +369,7 @@ mod unsafe_removed_from_name; mod unused_async; mod unused_io_amount; mod unused_peekable; +mod unused_result_ok; mod unused_rounding; mod unused_self; mod unused_unit; @@ -495,7 +493,7 @@ pub fn explain(name: &str) -> i32 { // Check if the lint has configuration let mut mdconf = get_configuration_metadata(); let name = name.to_ascii_lowercase(); - mdconf.retain(|cconf| cconf.lints.contains(&name)); + mdconf.retain(|cconf| cconf.lints.contains(&&*name)); if !mdconf.is_empty() { println!("### Configuration for {}:\n", info.lint.name_lower()); for conf in mdconf { @@ -531,10 +529,14 @@ fn register_categories(store: &mut rustc_lint::LintStore) { /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { - register_removed_non_tool_lints(store); register_categories(store); - include!("lib.deprecated.rs"); + for (old_name, new_name) in deprecated_lints::RENAMED { + store.register_renamed(old_name, new_name); + } + for (name, reason) in deprecated_lints::DEPRECATED { + store.register_removed(name, reason); + } #[cfg(feature = "internal")] { @@ -669,6 +671,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf))); store.register_late_pass(|_| Box::new(missing_inline::MissingInline)); store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems)); + store.register_late_pass(|_| Box::new(unused_result_ok::UnusedResultOk)); store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk)); store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); @@ -818,7 +821,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf))); store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); - store.register_late_pass(|_| Box::::default()); + store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); @@ -907,68 +910,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)); - store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains)); + store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)); store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); // add lints here, do not remove this comment, it's used in `new_lint` } - -#[rustfmt::skip] -fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { - store.register_removed( - "should_assert_eq", - "`assert!()` will be more flexible with RFC 2011", - ); - store.register_removed( - "extend_from_slice", - "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice", - ); - store.register_removed( - "range_step_by_zero", - "`iterator.step_by(0)` panics nowadays", - ); - store.register_removed( - "unstable_as_slice", - "`Vec::as_slice` has been stabilized in 1.7", - ); - store.register_removed( - "unstable_as_mut_slice", - "`Vec::as_mut_slice` has been stabilized in 1.7", - ); - store.register_removed( - "misaligned_transmute", - "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", - ); - store.register_removed( - "assign_ops", - "using compound assignment operators (e.g., `+=`) is harmless", - ); - store.register_removed( - "if_let_redundant_pattern_matching", - "this lint has been changed to redundant_pattern_matching", - ); - store.register_removed( - "unsafe_vector_initialization", - "the replacement suggested by this lint had substantially different behavior", - ); - store.register_removed( - "reverse_range_loop", - "this lint is now included in reversed_empty_ranges", - ); -} - -/// Register renamed lints. -/// -/// Used in `./src/driver.rs`. -pub fn register_renamed(ls: &mut rustc_lint::LintStore) { - for (old_name, new_name) in renamed_lints::RENAMED_LINTS { - ls.register_renamed(old_name, new_name); - } -} - -// only exists to let the dogfood integration test works. -// Don't run clippy as an executable directly -#[allow(dead_code)] -fn main() { - panic!("Please use the cargo-clippy executable"); -} diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index b685d1dad1a7..259e4d6c08fb 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -2,13 +2,13 @@ //! floating-point literal expressions. use clippy_config::Conf; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -159,63 +159,39 @@ enum WarningType { } impl WarningType { - fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: Span) { + fn lint_and_text(&self) -> (&'static Lint, &'static str, &'static str) { match self { - Self::MistypedLiteralSuffix => span_lint_and_sugg( - cx, + Self::MistypedLiteralSuffix => ( MISTYPED_LITERAL_SUFFIXES, - span, "mistyped literal suffix", "did you mean to write", - suggested_format, - Applicability::MaybeIncorrect, ), - Self::UnreadableLiteral => span_lint_and_sugg( - cx, - UNREADABLE_LITERAL, - span, - "long literal lacking separators", - "consider", - suggested_format, - Applicability::MachineApplicable, - ), - Self::LargeDigitGroups => span_lint_and_sugg( - cx, - LARGE_DIGIT_GROUPS, - span, - "digit groups should be smaller", - "consider", - suggested_format, - Applicability::MachineApplicable, - ), - Self::InconsistentDigitGrouping => span_lint_and_sugg( - cx, + Self::UnreadableLiteral => (UNREADABLE_LITERAL, "long literal lacking separators", "consider"), + Self::LargeDigitGroups => (LARGE_DIGIT_GROUPS, "digit groups should be smaller", "consider"), + Self::InconsistentDigitGrouping => ( INCONSISTENT_DIGIT_GROUPING, - span, "digits grouped inconsistently by underscores", "consider", - suggested_format, - Applicability::MachineApplicable, ), - Self::DecimalRepresentation => span_lint_and_sugg( - cx, + Self::DecimalRepresentation => ( DECIMAL_LITERAL_REPRESENTATION, - span, "integer literal has a better hexadecimal representation", "consider", - suggested_format, - Applicability::MachineApplicable, ), - Self::UnusualByteGroupings => span_lint_and_sugg( - cx, + Self::UnusualByteGroupings => ( UNUSUAL_BYTE_GROUPINGS, - span, "digits of hex, binary or octal literal not in groups of equal size", "consider", - suggested_format, - Applicability::MachineApplicable, ), - }; + } + } + + fn display(&self, num_lit: &NumericLiteral<'_>, cx: &EarlyContext<'_>, span: Span) { + let (lint, message, try_msg) = self.lint_and_text(); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then(cx, lint, span, message, |diag| { + diag.span_suggestion(span, try_msg, num_lit.format(), Applicability::MaybeIncorrect); + }); } } @@ -293,7 +269,7 @@ impl LiteralDigitGrouping { WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => true, }; if should_warn { - warning_type.display(num_lit.format(), cx, span); + warning_type.display(&num_lit, cx, span); } } } @@ -346,11 +322,14 @@ impl LiteralDigitGrouping { } } *part = main_part; - let mut sugg = num_lit.format(); - sugg.push('_'); - sugg.push(missing_char); - sugg.push_str(last_group); - WarningType::MistypedLiteralSuffix.display(sugg, cx, span); + let (lint, message, try_msg) = WarningType::MistypedLiteralSuffix.lint_and_text(); + span_lint_and_then(cx, lint, span, message, |diag| { + let mut sugg = num_lit.format(); + sugg.push('_'); + sugg.push(missing_char); + sugg.push_str(last_group); + diag.span_suggestion(span, try_msg, sugg, Applicability::MaybeIncorrect); + }); false } else { true @@ -471,7 +450,7 @@ impl DecimalLiteralRepresentation { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| { - warning_type.display(num_lit.format(), cx, span); + warning_type.display(&num_lit, cx, span); }); } } diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs index f0ee64d714e0..73a23615c2dc 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs @@ -2,6 +2,7 @@ use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{get_enclosing_block, is_integer_const}; +use rustc_ast::Label; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr}; use rustc_hir::{Expr, Pat}; @@ -17,6 +18,7 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, + label: Option
- diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index 661f80a6d346..ed1e090e1b54 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -1,5 +1,5 @@ (function () { - var md = window.markdownit({ + const md = window.markdownit({ html: true, linkify: true, typographer: true, @@ -17,7 +17,7 @@ }); function scrollToLint(lintId) { - var target = document.getElementById(lintId); + const target = document.getElementById(lintId); if (!target) { return; } @@ -25,21 +25,17 @@ } function scrollToLintByURL($scope, $location) { - var removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) { + const removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) { scrollToLint($location.path().substring(1)); removeListener(); }); } function selectGroup($scope, selectedGroup) { - var groups = $scope.groups; - for (var group in groups) { + const groups = $scope.groups; + for (const group in groups) { if (groups.hasOwnProperty(group)) { - if (group === selectedGroup) { - groups[group] = true; - } else { - groups[group] = false; - } + groups[group] = group === selectedGroup; } } } @@ -108,7 +104,7 @@ }) .controller("lintList", function ($scope, $http, $location, $timeout) { // Level filter - var LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true}; + const LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true}; $scope.levels = { ...LEVEL_FILTERS_DEFAULT }; $scope.byLevels = function (lint) { return $scope.levels[lint.level]; @@ -367,7 +363,7 @@ } $scope.clearVersionFilters = function () { - for (let filter in $scope.versionFilters) { + for (const filter in $scope.versionFilters) { $scope.versionFilters[filter] = { enabled: false, minorVersion: null }; } } @@ -378,7 +374,7 @@ $scope.updateVersionFilters = function() { for (const filter in $scope.versionFilters) { - let minorVersion = $scope.versionFilters[filter].minorVersion; + const minorVersion = $scope.versionFilters[filter].minorVersion; // 1.29.0 and greater if (minorVersion && minorVersion > 28) { @@ -391,14 +387,14 @@ } $scope.byVersion = function(lint) { - let filters = $scope.versionFilters; + const filters = $scope.versionFilters; for (const filter in filters) { if (filters[filter].enabled) { - let minorVersion = filters[filter].minorVersion; + const minorVersion = filters[filter].minorVersion; // Strip the "pre " prefix for pre 1.29.0 lints - let lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; - let lintMinorVersion = lintVersion.substring(2, 4); + const lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; + const lintMinorVersion = lintVersion.substring(2, 4); switch (filter) { // "=" gets the highest priority, since all filters are inclusive @@ -441,8 +437,8 @@ // Search the description // The use of `for`-loops instead of `foreach` enables us to return early - let terms = searchStr.split(" "); - let docsLowerCase = lint.docs.toLowerCase(); + const terms = searchStr.split(" "); + const docsLowerCase = lint.docs.toLowerCase(); for (index = 0; index < terms.length; index++) { // This is more likely and will therefore be checked first if (docsLowerCase.indexOf(terms[index]) !== -1) { @@ -479,7 +475,7 @@ const clipboard = document.getElementById("clipboard-" + lint.id); if (clipboard) { let resetClipboardTimeout = null; - let resetClipboardIcon = clipboard.innerHTML; + const resetClipboardIcon = clipboard.innerHTML; function resetClipboard() { resetClipboardTimeout = null; @@ -511,7 +507,7 @@ $scope.data = data; $scope.loading = false; - var selectedGroup = getQueryVariable("sel"); + const selectedGroup = getQueryVariable("sel"); if (selectedGroup) { selectGroup($scope, selectedGroup.toLowerCase()); } @@ -519,7 +515,7 @@ scrollToLintByURL($scope, $location); setTimeout(function () { - var el = document.getElementById('filter-input'); + const el = document.getElementById('filter-input'); if (el) { el.focus() } }, 0); }) @@ -531,10 +527,10 @@ })(); function getQueryVariable(variable) { - var query = window.location.search.substring(1); - var vars = query.split('&'); - for (var i = 0; i < vars.length; i++) { - var pair = vars[i].split('='); + const query = window.location.search.substring(1); + const vars = query.split('&'); + for (const entry of vars) { + const pair = entry.split('='); if (decodeURIComponent(pair[0]) == variable) { return decodeURIComponent(pair[1]); } @@ -579,6 +575,32 @@ function setTheme(theme, store) { } } +function handleShortcut(ev) { + if (ev.ctrlKey || ev.altKey || ev.metaKey) { + return; + } + + if (document.activeElement.tagName === "INPUT") { + if (ev.key === "Escape") { + document.activeElement.blur(); + } + } else { + switch (ev.key) { + case "s": + case "S": + case "/": + ev.preventDefault(); // To prevent the key to be put into the input. + document.getElementById("search-input").focus(); + break; + default: + break; + } + } +} + +document.addEventListener("keypress", handleShortcut); +document.addEventListener("keydown", handleShortcut); + // loading the theme after the initial load const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); const theme = localStorage.getItem('clippy-lint-list-theme'); diff --git a/src/tools/collect-license-metadata/Cargo.toml b/src/tools/collect-license-metadata/Cargo.toml index d0820cfc2a0e..edf9e5c5393e 100644 --- a/src/tools/collect-license-metadata/Cargo.toml +++ b/src/tools/collect-license-metadata/Cargo.toml @@ -2,6 +2,8 @@ name = "collect-license-metadata" version = "0.1.0" edition = "2021" +description = "Runs the reuse tool and caches the output, so rust toolchain devs don't need to have reuse installed" +license = "MIT OR Apache-2.0" [dependencies] anyhow = "1.0.65" diff --git a/src/tools/collect-license-metadata/src/main.rs b/src/tools/collect-license-metadata/src/main.rs index ca6aa01d78c0..dce36bb17b60 100644 --- a/src/tools/collect-license-metadata/src/main.rs +++ b/src/tools/collect-license-metadata/src/main.rs @@ -8,6 +8,11 @@ use anyhow::Error; use crate::licenses::LicensesInterner; +/// The entry point to the binary. +/// +/// You should probably let `bootstrap` execute this program instead of running it directly. +/// +/// Run `x.py run collect-license-metadata` fn main() -> Result<(), Error> { let reuse_exe: PathBuf = std::env::var_os("REUSE_EXE").expect("Missing REUSE_EXE").into(); let dest: PathBuf = std::env::var_os("DEST").expect("Missing DEST").into(); diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs index 0706f3bee05c..50c909793f5e 100644 --- a/src/tools/compiletest/src/command-list.rs +++ b/src/tools/compiletest/src/command-list.rs @@ -170,6 +170,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-32bit", "only-64bit", "only-aarch64", + "only-aarch64-unknown-linux-gnu", "only-apple", "only-arm", "only-avr", @@ -204,6 +205,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-watchos", "only-windows", "only-windows-gnu", + "only-windows-msvc", "only-x86", "only-x86_64", "only-x86_64-fortanix-unknown-sgx", diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index 899ef0f8a6c2..404101abd41b 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -2,10 +2,14 @@ name = "generate-copyright" version = "0.1.0" edition = "2021" +description = "Produces a manifest of all the copyrighted materials in the Rust Toolchain" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1.0.65" +cargo_metadata = "0.18.1" +rinja = "0.3.0" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" +thiserror = "1" diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs new file mode 100644 index 000000000000..c85e4aa371a2 --- /dev/null +++ b/src/tools/generate-copyright/src/cargo_metadata.rs @@ -0,0 +1,191 @@ +//! Gets metadata about a workspace from Cargo + +use std::collections::BTreeMap; +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; + +/// Describes how this module can fail +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("I/O Error: {0:?}")] + Io(#[from] std::io::Error), + #[error("Failed get output from cargo-metadata: {0:?}")] + GettingMetadata(#[from] cargo_metadata::Error), + #[error("Failed to run cargo vendor: {0:?}")] + LaunchingVendor(std::io::Error), + #[error("Failed to complete cargo vendor")] + RunningVendor, + #[error("Bad path {0:?} whilst scraping files")] + Scraping(PathBuf), +} + +/// Uniquely describes a package on crates.io +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Package { + /// The name of the package + pub name: String, + /// The version number + pub version: String, +} + +/// Extra data about a package +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct PackageMetadata { + /// The license it is under + pub license: String, + /// The list of authors from the package metadata + pub authors: Vec, + /// A list of important files from the package, with their contents. + /// + /// This includes *COPYRIGHT*, *NOTICE*, *AUTHOR*, *LICENSE*, and *LICENCE* files, case-insensitive. + pub notices: BTreeMap, + /// If this is true, this dep is in the Rust Standard Library + pub is_in_libstd: Option, +} + +/// Use `cargo metadata` and `cargo vendor` to get a list of dependencies and their license data. +/// +/// This will involve running `cargo vendor` into `${BUILD}/vendor` so we can +/// grab the license files. +/// +/// Any dependency with a path beginning with `root_path` is ignored, as we +/// assume `reuse` has covered it already. +pub fn get_metadata_and_notices( + cargo: &Path, + dest: &Path, + root_path: &Path, + manifest_paths: &[&Path], +) -> Result, Error> { + let mut output = get_metadata(cargo, root_path, manifest_paths)?; + + // Now do a cargo-vendor and grab everything + let vendor_path = dest.join("vendor"); + println!("Vendoring deps into {}...", vendor_path.display()); + run_cargo_vendor(cargo, &vendor_path, manifest_paths)?; + + // Now for each dependency we found, go and grab any important looking files + for (package, metadata) in output.iter_mut() { + load_important_files(package, metadata, &vendor_path)?; + } + + Ok(output) +} + +/// Use `cargo metadata` to get a list of dependencies and their license data. +/// +/// Any dependency with a path beginning with `root_path` is ignored, as we +/// assume `reuse` has covered it already. +pub fn get_metadata( + cargo: &Path, + root_path: &Path, + manifest_paths: &[&Path], +) -> Result, Error> { + let mut output = BTreeMap::new(); + // Look at the metadata for each manifest + for manifest_path in manifest_paths { + if manifest_path.file_name() != Some(OsStr::new("Cargo.toml")) { + panic!("cargo_manifest::get requires a path to a Cargo.toml file"); + } + let metadata = cargo_metadata::MetadataCommand::new() + .cargo_path(cargo) + .env("RUSTC_BOOTSTRAP", "1") + .manifest_path(manifest_path) + .exec()?; + for package in metadata.packages { + let manifest_path = package.manifest_path.as_path(); + if manifest_path.starts_with(root_path) { + // it's an in-tree dependency and reuse covers it + continue; + } + // otherwise it's an out-of-tree dependency + let package_id = Package { name: package.name, version: package.version.to_string() }; + output.insert( + package_id, + PackageMetadata { + license: package.license.unwrap_or_else(|| String::from("Unspecified")), + authors: package.authors, + notices: BTreeMap::new(), + is_in_libstd: None, + }, + ); + } + } + + Ok(output) +} + +/// Run cargo-vendor, fetching into the given dir +fn run_cargo_vendor(cargo: &Path, dest: &Path, manifest_paths: &[&Path]) -> Result<(), Error> { + let mut vendor_command = std::process::Command::new(cargo); + vendor_command.env("RUSTC_BOOTSTRAP", "1"); + vendor_command.arg("vendor"); + vendor_command.arg("--quiet"); + vendor_command.arg("--versioned-dirs"); + for manifest_path in manifest_paths { + vendor_command.arg("-s"); + vendor_command.arg(manifest_path); + } + vendor_command.arg(dest); + + let vendor_status = vendor_command.status().map_err(Error::LaunchingVendor)?; + + if !vendor_status.success() { + return Err(Error::RunningVendor); + } + + Ok(()) +} + +/// Add important files off disk into this dependency. +/// +/// Maybe one-day Cargo.toml will contain enough information that we don't need +/// to do this manual scraping. +fn load_important_files( + package: &Package, + dep: &mut PackageMetadata, + vendor_root: &Path, +) -> Result<(), Error> { + let name_version = format!("{}-{}", package.name, package.version); + println!("Scraping notices for {}...", name_version); + let dep_vendor_path = vendor_root.join(name_version); + for entry in std::fs::read_dir(dep_vendor_path)? { + let entry = entry?; + let metadata = entry.metadata()?; + let path = entry.path(); + let Some(filename) = path.file_name() else { + return Err(Error::Scraping(path)); + }; + let lc_filename = filename.to_ascii_lowercase(); + let lc_filename_str = lc_filename.to_string_lossy(); + let mut keep = false; + for m in ["copyright", "licence", "license", "author", "notice"] { + if lc_filename_str.contains(m) { + keep = true; + break; + } + } + if keep { + if metadata.is_dir() { + for inner_entry in std::fs::read_dir(entry.path())? { + let inner_entry = inner_entry?; + if inner_entry.metadata()?.is_file() { + let inner_filename = inner_entry.file_name(); + let inner_filename_str = inner_filename.to_string_lossy(); + let qualified_filename = + format!("{}/{}", lc_filename_str, inner_filename_str); + println!("Scraping {}", qualified_filename); + dep.notices.insert( + qualified_filename.to_string(), + std::fs::read_to_string(inner_entry.path())?, + ); + } + } + } else if metadata.is_file() { + let filename = filename.to_string_lossy(); + println!("Scraping {}", filename); + dep.notices.insert(filename.to_string(), std::fs::read_to_string(path)?); + } + } + } + Ok(()) +} diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs index dce1a558697e..afa75d0d6714 100644 --- a/src/tools/generate-copyright/src/main.rs +++ b/src/tools/generate-copyright/src/main.rs @@ -1,79 +1,70 @@ -use std::io::Write; -use std::path::PathBuf; +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; use anyhow::Error; +use rinja::Template; +mod cargo_metadata; + +#[derive(Template)] +#[template(path = "COPYRIGHT.html")] +struct CopyrightTemplate { + in_tree: Node, + dependencies: BTreeMap, +} + +/// The entry point to the binary. +/// +/// You should probably let `bootstrap` execute this program instead of running it directly. +/// +/// Run `x.py run generate-copyright` fn main() -> Result<(), Error> { - let dest = env_path("DEST")?; + let dest_file = env_path("DEST")?; + let out_dir = env_path("OUT_DIR")?; + let cargo = env_path("CARGO")?; let license_metadata = env_path("LICENSE_METADATA")?; - let metadata: Metadata = serde_json::from_slice(&std::fs::read(&license_metadata)?)?; + let collected_tree_metadata: Metadata = + serde_json::from_slice(&std::fs::read(&license_metadata)?)?; - let mut buffer = Vec::new(); - render_recursive(&metadata.files, &mut buffer, 0)?; + let root_path = std::path::absolute(".")?; + let workspace_paths = [ + Path::new("./Cargo.toml"), + Path::new("./src/tools/cargo/Cargo.toml"), + Path::new("./library/Cargo.toml"), + ]; + let mut collected_cargo_metadata = + cargo_metadata::get_metadata_and_notices(&cargo, &out_dir, &root_path, &workspace_paths)?; - std::fs::write(&dest, &buffer)?; - - Ok(()) -} - -fn render_recursive(node: &Node, buffer: &mut Vec, depth: usize) -> Result<(), Error> { - let prefix = std::iter::repeat("> ").take(depth + 1).collect::(); - - match node { - Node::Root { children } => { - for child in children { - render_recursive(child, buffer, depth)?; - } - } - Node::Directory { name, children, license } => { - render_license(&prefix, std::iter::once(name), license.as_ref(), buffer)?; - if !children.is_empty() { - writeln!(buffer, "{prefix}")?; - writeln!(buffer, "{prefix}*Exceptions:*")?; - for child in children { - writeln!(buffer, "{prefix}")?; - render_recursive(child, buffer, depth + 1)?; - } - } - } - Node::Group { files, directories, license } => { - render_license(&prefix, directories.iter().chain(files.iter()), Some(license), buffer)?; - } - Node::File { name, license } => { - render_license(&prefix, std::iter::once(name), Some(license), buffer)?; - } - } - - Ok(()) -} - -fn render_license<'a>( - prefix: &str, - names: impl Iterator, - license: Option<&License>, - buffer: &mut Vec, -) -> Result<(), Error> { - for name in names { - writeln!(buffer, "{prefix}**`{name}`** ")?; - } - if let Some(license) = license { - writeln!(buffer, "{prefix}License: `{}`", license.spdx)?; - for copyright in license.copyright.iter() { - writeln!(buffer, "{prefix}Copyright: {copyright}")?; - } - } + let stdlib_set = + cargo_metadata::get_metadata(&cargo, &root_path, &[Path::new("./library/std/Cargo.toml")])?; + + for (key, value) in collected_cargo_metadata.iter_mut() { + value.is_in_libstd = Some(stdlib_set.contains_key(key)); + } + + let template = CopyrightTemplate { + in_tree: collected_tree_metadata.files, + dependencies: collected_cargo_metadata, + }; + + let output = template.render()?; + + std::fs::write(&dest_file, output)?; Ok(()) } +/// Describes a tree of metadata for our filesystem tree #[derive(serde::Deserialize)] struct Metadata { files: Node, } -#[derive(serde::Deserialize)] +/// Describes one node in our metadata tree +#[derive(serde::Deserialize, rinja::Template)] #[serde(rename_all = "kebab-case", tag = "type")] +#[template(path = "Node.html")] pub(crate) enum Node { Root { children: Vec }, Directory { name: String, children: Vec, license: Option }, @@ -81,12 +72,14 @@ pub(crate) enum Node { Group { files: Vec, directories: Vec, license: License }, } +/// A License has an SPDX license name and a list of copyright holders. #[derive(serde::Deserialize)] struct License { spdx: String, copyright: Vec, } +/// Grab an environment variable as a PathBuf, or fail nicely. fn env_path(var: &str) -> Result { if let Some(var) = std::env::var_os(var) { Ok(var.into()) diff --git a/src/tools/generate-copyright/templates/COPYRIGHT.html b/src/tools/generate-copyright/templates/COPYRIGHT.html new file mode 100644 index 000000000000..ccb177a54d41 --- /dev/null +++ b/src/tools/generate-copyright/templates/COPYRIGHT.html @@ -0,0 +1,54 @@ + + + + + Copyright notices for The Rust Toolchain + + + +

Copyright notices for The Rust Toolchain

+ +

This file describes the copyright and licensing information for the source +code within The Rust Project git tree, and the third-party dependencies used +when building the Rust toolchain (including the Rust Standard Library).

+ +

Table of Contents

+ + +

In-tree files

+ +

The following licenses cover the in-tree source files that were used in this +release:

+ +{{ in_tree|safe }} + +

Out-of-tree dependencies

+ +

The following licenses cover the out-of-tree crates that were used in this +release:

+ +{% for (key, value) in dependencies %} +

📦 {{key.name}}-{{key.version}}

+

URL: https://crates.io/crates/{{ key.name }}/{{ key.version }}

+

In libstd: {% if value.is_in_libstd.unwrap() %} Yes {% else %} No {% endif %}

+

Authors: {{ value.authors|join(", ") }}

+

License: {{ value.license }}

+ {% let len = value.notices.len() %} + {% if len > 0 %} +

Notices: + {% for (notice_name, notice_text) in value.notices %} +

+ {{ notice_name }} +
+{{ notice_text }}
+                
+
+ {% endfor %} +

+ {% endif %} +{% endfor %} + + \ No newline at end of file diff --git a/src/tools/generate-copyright/templates/Node.html b/src/tools/generate-copyright/templates/Node.html new file mode 100644 index 000000000000..a71a1bf3b73d --- /dev/null +++ b/src/tools/generate-copyright/templates/Node.html @@ -0,0 +1,71 @@ +{% match self %} + +{% when Node::Root { children } %} + +{% for child in children %} +{{ child|safe }} +{% endfor %} + +{% when Node::Directory { name, children, license } %} + +
+ +

+ File/Directory: {{ name }} +

+ + {% if let Some(license) = license %} + +

License: {{ license.spdx }}

+ {% for copyright in license.copyright.iter() %} +

Copyright: {{ copyright }}

+ {% endfor %} + + {% endif %} + + {% if !children.is_empty() %} + +

Exceptions:

+ {% for child in children %} + {{ child|safe }} + {% endfor %} + + {% endif %} + +
+ +{% when Node::File { name, license } %} + +
+

+ File/Directory: {{ name }} +

+ +

License: {{ license.spdx }}

+ {% for copyright in license.copyright.iter() %} +

Copyright: {{ copyright }}

+ {% endfor %} +
+ +{% when Node::Group { files, directories, license } %} + +
+ +

+ File/Directory: + {% for name in files %} + {{ name }} + {% endfor %} + {% for name in directories %} + {{ name }} + {% endfor %} +

+ +

License: {{ license.spdx }}

+ {% for copyright in license.copyright.iter() %} +

Copyright: {{ copyright }}

+ {% endfor %} + +
+ +{% endmatch %} diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 499d0fe2f1c9..a0bff386a71d 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -212,7 +212,6 @@ degree documented below): - All Rust [Tier 1 targets](https://doc.rust-lang.org/rustc/platform-support.html) are supported by Miri. They are all checked on Miri's CI, and some (at least one per OS) are even checked on every Rust PR, so the shipped Miri should always work on these targets. -- `aarch64-apple-darwin` is supported. - `s390x-unknown-linux-gnu` is supported as our "big-endian target of choice". - For every other target with OS `linux`, `macos`, or `windows`, Miri should generally work, but we make no promises and we don't run tests for such targets. diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 5e75638f4670..3e90ecc5c039 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -137,7 +137,7 @@ case $HOST_TARGET in MANY_SEEDS=16 TEST_TARGET=x86_64-pc-windows-gnu run_tests ;; aarch64-apple-darwin) - # Host (tier 2) + # Host GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 MANY_SEEDS=64 TEST_TARGET=i686-pc-windows-gnu run_tests diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 8767d6970f00..a05b3517f235 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -137,15 +137,20 @@ impl Command { } } - // Wait until the port is open. - let josh_ready = net::TcpStream::connect_timeout( - &net::SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)), - Duration::from_secs(1), - ) - .context("failed to connect to josh-proxy")?; - drop(josh_ready); - - Ok(Josh(josh)) + // Wait until the port is open. We try every 10ms until 1s passed. + for _ in 0..100 { + // This will generally fail immediately when the port is still closed. + let josh_ready = net::TcpStream::connect_timeout( + &net::SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)), + Duration::from_millis(1), + ); + if josh_ready.is_ok() { + return Ok(Josh(josh)); + } + // Not ready yet. + std::thread::sleep(Duration::from_millis(10)); + } + bail!("Even after waiting for 1s, josh-proxy is still not available.") } pub fn exec(self) -> Result<()> { diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 699a17a4943a..9dad0df254cb 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -6696447f784a888446d13bb400a8d507a68331c9 +1d8f135b20fac63c493d5963ce02963b46ca0986 diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 1173da469756..d75202b0a301 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -460,11 +460,8 @@ pub fn eval_entry<'tcx>( ecx.handle_ice(); panic::resume_unwind(panic_payload) }); - let res = match res { - Err(res) => res, - // `Ok` can never happen - Ok(never) => match never {}, - }; + // `Ok` can never happen. + let Err(res) = res; // Machine cleanup. Only do this if all threads have terminated; threads that are still running // might cause Stacked Borrows errors (https://github.com/rust-lang/miri/issues/2396). diff --git a/src/tools/miri/tests/pass/async-fn.rs b/src/tools/miri/tests/pass/async-fn.rs index 13400c88c711..67ec2e26b306 100644 --- a/src/tools/miri/tests/pass/async-fn.rs +++ b/src/tools/miri/tests/pass/async-fn.rs @@ -59,6 +59,7 @@ async fn hello_world() { } // This example comes from https://github.com/rust-lang/rust/issues/115145 +#[allow(unreachable_patterns)] async fn uninhabited_variant() { async fn unreachable(_: Never) {} diff --git a/src/tools/miri/tests/pass/enums.rs b/src/tools/miri/tests/pass/enums.rs index ac7aafc1bb2e..1dafef025e95 100644 --- a/src/tools/miri/tests/pass/enums.rs +++ b/src/tools/miri/tests/pass/enums.rs @@ -43,6 +43,7 @@ fn discriminant_overflow() { } } +#[allow(unreachable_patterns)] fn more_discriminant_overflow() { pub enum Infallible {} diff --git a/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs b/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs index 05090d685ab2..af4c3b069319 100644 --- a/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs +++ b/src/tools/miri/tests/pass/tree_borrows/vec_unique.rs @@ -30,20 +30,20 @@ fn main() { // whether we got the distance correct: // If the output shows // - // |- - // '- + // ├─ + // └─ // // then `nth_parent` is not big enough. // The correct value for `nth_parent` should be the minimum // integer for which the output shows // - // '- + // └─ // ) // // Ultimately we want pointers obtained through independent // calls of `as_ptr` to be able to alias, which will probably involve // a new permission that allows aliasing when there is no protector. - let nth_parent = if cfg!(uniq) { 2 } else { 0 }; + let nth_parent = if cfg!(uniq) { 9 } else { 0 }; name!(base.as_ptr()=>nth_parent); name!(base.as_ptr()=>nth_parent); diff --git a/src/tools/run-make-support/src/external_deps/c_build.rs b/src/tools/run-make-support/src/external_deps/c_build.rs index fb22780eaa0c..f8d1666adda4 100644 --- a/src/tools/run-make-support/src/external_deps/c_build.rs +++ b/src/tools/run-make-support/src/external_deps/c_build.rs @@ -12,14 +12,31 @@ use crate::targets::{is_darwin, is_msvc, is_windows}; /// Built from a C file. #[track_caller] pub fn build_native_static_lib(lib_name: &str) -> PathBuf { + build_native_static_lib_internal(lib_name, false) +} + +/// Builds an optimized static lib (`.lib` on Windows MSVC and `.a` for the rest) with the given name. +/// Built from a C file. +#[track_caller] +pub fn build_native_static_lib_optimized(lib_name: &str) -> PathBuf { + build_native_static_lib_internal(lib_name, true) +} + +#[track_caller] +fn build_native_static_lib_internal(lib_name: &str, optimzed: bool) -> PathBuf { let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; let src = format!("{lib_name}.c"); let lib_path = static_lib_name(lib_name); - if is_msvc() { - cc().arg("-c").out_exe(&obj_file).input(src).run(); - } else { - cc().arg("-v").arg("-c").out_exe(&obj_file).input(src).run(); - }; + + let mut cc = cc(); + if !is_msvc() { + cc.arg("-v"); + } + if optimzed { + cc.optimize(); + } + cc.arg("-c").out_exe(&obj_file).input(src).optimize().run(); + let obj_file = if is_msvc() { PathBuf::from(format!("{lib_name}.obj")) } else { diff --git a/src/tools/run-make-support/src/external_deps/cc.rs b/src/tools/run-make-support/src/external_deps/cc.rs index 36cef15781f1..011ad89e170a 100644 --- a/src/tools/run-make-support/src/external_deps/cc.rs +++ b/src/tools/run-make-support/src/external_deps/cc.rs @@ -115,6 +115,17 @@ impl Cc { self.cmd.arg(path.as_ref()); self } + + /// Optimize the output. + /// Equivalent to `-O3` for GNU-compatible linkers or `-O2` for MSVC linkers. + pub fn optimize(&mut self) -> &mut Self { + if is_msvc() { + self.cmd.arg("-O2"); + } else { + self.cmd.arg("-O3"); + } + self + } } /// `EXTRACFLAGS` diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index fc7e5ceae400..a44dd00ad798 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -45,7 +45,7 @@ pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rust // These rely on external dependencies. pub use cc::{cc, cxx, extra_c_flags, extra_cxx_flags, Cc}; -pub use c_build::{build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_cxx}; +pub use c_build::{build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_optimized, build_native_static_lib_cxx}; pub use clang::{clang, Clang}; pub use htmldocck::htmldocck; pub use llvm::{ diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index bd43a62341d1..d76f53818716 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -27,6 +27,7 @@ macro_rules! from_bytes { ($ty:tt, $value:expr) => { ($ty::from_le_bytes(match ($value).try_into() { Ok(it) => it, + #[allow(unreachable_patterns)] Err(_) => return Err(MirEvalError::InternalError("mismatched size".into())), })) }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 057f55338056..9aa2eeebc175 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -1161,6 +1161,7 @@ impl<'ctx> MirLowerCtx<'ctx> { ProjectionElem::OpaqueCast(it) => { ProjectionElem::OpaqueCast(it) } + #[allow(unreachable_patterns)] ProjectionElem::Index(it) => match it {}, }) .collect(), diff --git a/src/tools/rustdoc/main.rs b/src/tools/rustdoc/main.rs index 5b499a1fa1fa..d4099cafe5df 100644 --- a/src/tools/rustdoc/main.rs +++ b/src/tools/rustdoc/main.rs @@ -1,3 +1,6 @@ +// We need this feature as it changes `dylib` linking behavior and allows us to link to `rustc_driver`. +#![feature(rustc_private)] + fn main() { rustdoc::main() } diff --git a/src/tools/rustfmt/src/git-rustfmt/main.rs b/src/tools/rustfmt/src/git-rustfmt/main.rs index 3059d917c6b9..5674f40bef91 100644 --- a/src/tools/rustfmt/src/git-rustfmt/main.rs +++ b/src/tools/rustfmt/src/git-rustfmt/main.rs @@ -1,3 +1,7 @@ +// We need this feature as it changes `dylib` linking behavior and allows us to link to +// `rustc_driver`. +#![feature(rustc_private)] + #[macro_use] extern crate tracing; diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index a2cfdea712e7..14f0a9cd23d2 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1,6 +1,5 @@ run-make/branch-protection-check-IBT/Makefile run-make/cat-and-grep-sanity-check/Makefile -run-make/cross-lang-lto-upstream-rlibs/Makefile run-make/dep-info-doesnt-run-much/Makefile run-make/dep-info-spaces/Makefile run-make/dep-info/Makefile @@ -13,27 +12,15 @@ run-make/libs-through-symlinks/Makefile run-make/libtest-json/Makefile run-make/libtest-junit/Makefile run-make/libtest-thread-limit/Makefile -run-make/long-linker-command-lines-cmd-exe/Makefile -run-make/long-linker-command-lines/Makefile run-make/macos-deployment-target/Makefile run-make/min-global-align/Makefile run-make/native-link-modifier-bundle/Makefile run-make/no-alloc-shim/Makefile -run-make/pdb-buildinfo-cl-cmd/Makefile -run-make/pgo-gen-lto/Makefile -run-make/pgo-indirect-call-promotion/Makefile -run-make/raw-dylib-alt-calling-convention/Makefile -run-make/raw-dylib-c/Makefile -run-make/redundant-libs/Makefile run-make/remap-path-prefix-dwarf/Makefile run-make/reproducible-build/Makefile run-make/rlib-format-packed-bundled-libs/Makefile -run-make/simd-ffi/Makefile run-make/split-debuginfo/Makefile -run-make/staticlib-dylib-linkage/Makefile run-make/symbol-mangling-hashed/Makefile run-make/sysroot-crates-are-unstable/Makefile -run-make/thumb-none-cortex-m/Makefile -run-make/thumb-none-qemu/Makefile run-make/translation/Makefile run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index e23e931b0eb0..89011bbb48f5 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -192,6 +192,7 @@ const EXCEPTIONS_RUSTBOOK: ExceptionList = &[ const EXCEPTIONS_CRANELIFT: ExceptionList = &[ // tidy-alphabetical-start ("cranelift-bforest", "Apache-2.0 WITH LLVM-exception"), + ("cranelift-bitset", "Apache-2.0 WITH LLVM-exception"), ("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"), ("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"), ("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"), @@ -511,6 +512,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ "bumpalo", "cfg-if", "cranelift-bforest", + "cranelift-bitset", "cranelift-codegen", "cranelift-codegen-meta", "cranelift-codegen-shared", diff --git a/tests/assembly/x86-return-float.rs b/tests/assembly/x86-return-float.rs index c4a2c1ad44ec..f9ebf53dddca 100644 --- a/tests/assembly/x86-return-float.rs +++ b/tests/assembly/x86-return-float.rs @@ -8,9 +8,9 @@ // Force frame pointers to make ASM more consistent between targets //@ compile-flags: -O -C force-frame-pointers //@ filecheck-flags: --implicit-check-not fld --implicit-check-not fst -//@ revisions: unix windows -//@[unix] ignore-windows -//@[windows] only-windows +//@ revisions: normal win +//@[normal] ignore-windows +//@[win] only-windows #![crate_type = "lib"] #![feature(f16, f128)] @@ -190,10 +190,10 @@ pub unsafe fn call_f64_f64(x: &mut (f64, f64)) { } // CHECK: movl {{.*}}(%ebp), %[[PTR:.*]] // CHECK: calll {{()|_}}get_f64_f64 - // unix: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] - // unix-NEXT: movsd [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] - // windows: movsd (%esp), %[[VAL1:.*]] - // windows-NEXT: movsd 8(%esp), %[[VAL2:.*]] + // normal: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] + // normal-NEXT: movsd [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] + // win: movsd (%esp), %[[VAL1:.*]] + // win-NEXT: movsd 8(%esp), %[[VAL2:.*]] // CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]]) // CHECK-NEXT: movsd %[[VAL2]], 8(%[[PTR]]) *x = get_f64_f64(); @@ -207,13 +207,13 @@ pub unsafe fn call_f32_f64(x: &mut (f32, f64)) { } // CHECK: movl {{.*}}(%ebp), %[[PTR:.*]] // CHECK: calll {{()|_}}get_f32_f64 - // unix: movss [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] - // unix-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]] - // windows: movss (%esp), %[[VAL1:.*]] - // windows-NEXT: movsd 8(%esp), %[[VAL2:.*]] + // normal: movss [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] + // normal-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]] + // win: movss (%esp), %[[VAL1:.*]] + // win-NEXT: movsd 8(%esp), %[[VAL2:.*]] // CHECK-NEXT: movss %[[VAL1]], (%[[PTR]]) - // unix-NEXT: movsd %[[VAL2]], 4(%[[PTR]]) - // windows-NEXT: movsd %[[VAL2]], 8(%[[PTR]]) + // normal-NEXT: movsd %[[VAL2]], 4(%[[PTR]]) + // win-NEXT: movsd %[[VAL2]], 8(%[[PTR]]) *x = get_f32_f64(); } @@ -225,10 +225,10 @@ pub unsafe fn call_f64_f32(x: &mut (f64, f32)) { } // CHECK: movl {{.*}}(%ebp), %[[PTR:.*]] // CHECK: calll {{()|_}}get_f64_f32 - // unix: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] - // unix-NEXT: movss [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] - // windows: movsd (%esp), %[[VAL1:.*]] - // windows-NEXT: movss 8(%esp), %[[VAL2:.*]] + // normal: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] + // normal-NEXT: movss [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] + // win: movsd (%esp), %[[VAL1:.*]] + // win-NEXT: movss 8(%esp), %[[VAL2:.*]] // CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]]) // CHECK-NEXT: movss %[[VAL2]], 8(%[[PTR]]) *x = get_f64_f32(); @@ -257,10 +257,10 @@ pub unsafe fn call_f64_other(x: &mut (f64, usize)) { } // CHECK: movl {{.*}}(%ebp), %[[PTR:.*]] // CHECK: calll {{()|_}}get_f64_other - // unix: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] - // unix-NEXT: movl [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] - // windows: movsd (%esp), %[[VAL1:.*]] - // windows-NEXT: movl 8(%esp), %[[VAL2:.*]] + // normal: movsd [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] + // normal-NEXT: movl [[#%d,OFFSET+8]](%ebp), %[[VAL2:.*]] + // win: movsd (%esp), %[[VAL1:.*]] + // win-NEXT: movl 8(%esp), %[[VAL2:.*]] // CHECK-NEXT: movsd %[[VAL1]], (%[[PTR]]) // CHECK-NEXT: movl %[[VAL2]], 8(%[[PTR]]) *x = get_f64_other(); @@ -289,13 +289,13 @@ pub unsafe fn call_other_f64(x: &mut (usize, f64)) { } // CHECK: movl {{.*}}(%ebp), %[[PTR:.*]] // CHECK: calll {{()|_}}get_other_f64 - // unix: movl [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] - // unix-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]] - // windows: movl (%esp), %[[VAL1:.*]] - // windows-NEXT: movsd 8(%esp), %[[VAL2:.*]] + // normal: movl [[#%d,OFFSET:]](%ebp), %[[VAL1:.*]] + // normal-NEXT: movsd [[#%d,OFFSET+4]](%ebp), %[[VAL2:.*]] + // win: movl (%esp), %[[VAL1:.*]] + // win-NEXT: movsd 8(%esp), %[[VAL2:.*]] // CHECK-NEXT: movl %[[VAL1]], (%[[PTR]]) - // unix-NEXT: movsd %[[VAL2]], 4(%[[PTR]]) - // windows-NEXT: movsd %[[VAL2]], 8(%[[PTR]]) + // normal-NEXT: movsd %[[VAL2]], 4(%[[PTR]]) + // win-NEXT: movsd %[[VAL2]], 8(%[[PTR]]) *x = get_other_f64(); } diff --git a/tests/codegen/aarch64-struct-align-128.rs b/tests/codegen/aarch64-struct-align-128.rs index d1b4132d501f..3fed19d96b12 100644 --- a/tests/codegen/aarch64-struct-align-128.rs +++ b/tests/codegen/aarch64-struct-align-128.rs @@ -1,12 +1,12 @@ // Test that structs aligned to 128 bits are passed with the correct ABI on aarch64. -//@ revisions:linux darwin windows +//@ revisions: linux darwin win //@[linux] compile-flags: --target aarch64-unknown-linux-gnu //@[darwin] compile-flags: --target aarch64-apple-darwin -//@[windows] compile-flags: --target aarch64-pc-windows-msvc +//@[win] compile-flags: --target aarch64-pc-windows-msvc //@[linux] needs-llvm-components: aarch64 //@[darwin] needs-llvm-components: aarch64 -//@[windows] needs-llvm-components: aarch64 +//@[win] needs-llvm-components: aarch64 #![feature(no_core, lang_items)] #![crate_type = "lib"] @@ -39,9 +39,9 @@ pub struct Wrapped8 { } extern "C" { - // linux: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) - // darwin: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) - // windows: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) + // linux: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) + // darwin: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) + // win: declare void @test_8([2 x i64], [2 x i64], [2 x i64]) fn test_8(a: Align8, b: Transparent8, c: Wrapped8); } @@ -69,9 +69,9 @@ pub struct Wrapped16 { } extern "C" { - // linux: declare void @test_16([2 x i64], [2 x i64], i128) - // darwin: declare void @test_16(i128, i128, i128) - // windows: declare void @test_16(i128, i128, i128) + // linux: declare void @test_16([2 x i64], [2 x i64], i128) + // darwin: declare void @test_16(i128, i128, i128) + // win: declare void @test_16(i128, i128, i128) fn test_16(a: Align16, b: Transparent16, c: Wrapped16); } @@ -94,9 +94,9 @@ pub struct WrappedI128 { } extern "C" { - // linux: declare void @test_i128(i128, i128, i128) - // darwin: declare void @test_i128(i128, i128, i128) - // windows: declare void @test_i128(i128, i128, i128) + // linux: declare void @test_i128(i128, i128, i128) + // darwin: declare void @test_i128(i128, i128, i128) + // win: declare void @test_i128(i128, i128, i128) fn test_i128(a: I128, b: TransparentI128, c: WrappedI128); } @@ -121,9 +121,9 @@ pub struct WrappedPacked { } extern "C" { - // linux: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) - // darwin: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) - // windows: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) + // linux: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) + // darwin: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) + // win: declare void @test_packed([2 x i64], [2 x i64], [2 x i64]) fn test_packed(a: Packed, b: TransparentPacked, c: WrappedPacked); } diff --git a/tests/codegen/array-from_fn.rs b/tests/codegen/array-from_fn.rs new file mode 100644 index 000000000000..7202d0c67e69 --- /dev/null +++ b/tests/codegen/array-from_fn.rs @@ -0,0 +1,13 @@ +//@ revisions: NORMAL OPT +//@ [NORMAL] compile-flags: -C opt-level=0 -C debuginfo=2 +//@ [OPT] compile-flags: -C opt-level=s -C debuginfo=0 + +#![crate_type = "lib"] +#![feature(array_from_fn)] + +#[no_mangle] +pub fn iota() -> [u8; 16] { + // OPT-NOT: core..array..Guard + // NORMAL: core..array..Guard + std::array::from_fn(|i| i as _) +} diff --git a/tests/codegen/cold-call-declare-and-call.rs b/tests/codegen/cold-call-declare-and-call.rs index cd41c0a6dfb6..b18565ee6c3b 100644 --- a/tests/codegen/cold-call-declare-and-call.rs +++ b/tests/codegen/cold-call-declare-and-call.rs @@ -1,8 +1,8 @@ -//@ revisions: NORMAL WINDOWS +//@ revisions: NORMAL WIN //@ compile-flags: -C no-prepopulate-passes //@[NORMAL] ignore-windows -//@[WINDOWS] only-windows -//@[WINDOWS] only-x86_64 +//@[WIN] only-windows +//@[WIN] only-x86_64 #![crate_type = "lib"] #![feature(rust_cold_cc)] @@ -14,8 +14,8 @@ // See the comment in `Target::adjust_abi` for why this differs -// WINDOWS: define void @this_should_never_happen(i16 -// WINDOWS: call void @this_should_never_happen(i16 +// WIN: define void @this_should_never_happen(i16 +// WIN: call void @this_should_never_happen(i16 #[no_mangle] pub extern "rust-cold" fn this_should_never_happen(x: u16) {} diff --git a/tests/codegen/default-requires-uwtable.rs b/tests/codegen/default-requires-uwtable.rs index 567bd55ecc3f..3cb35cea022a 100644 --- a/tests/codegen/default-requires-uwtable.rs +++ b/tests/codegen/default-requires-uwtable.rs @@ -1,9 +1,9 @@ -//@ revisions: WINDOWS ANDROID +//@ revisions: WINDOWS_ ANDROID_ //@ compile-flags: -C panic=abort -Copt-level=0 -//@ [WINDOWS] compile-flags: --target=x86_64-pc-windows-msvc -//@ [WINDOWS] needs-llvm-components: x86 -//@ [ANDROID] compile-flags: --target=armv7-linux-androideabi -//@ [ANDROID] needs-llvm-components: arm +//@ [WINDOWS_] compile-flags: --target=x86_64-pc-windows-msvc +//@ [WINDOWS_] needs-llvm-components: x86 +//@ [ANDROID_] compile-flags: --target=armv7-linux-androideabi +//@ [ANDROID_] needs-llvm-components: arm #![feature(no_core, lang_items)] #![crate_type = "lib"] diff --git a/tests/codegen/instrument-coverage/testprog.rs b/tests/codegen/instrument-coverage/testprog.rs index eea4d9cb3cf0..655fe779fca6 100644 --- a/tests/codegen/instrument-coverage/testprog.rs +++ b/tests/codegen/instrument-coverage/testprog.rs @@ -1,7 +1,7 @@ //@ edition: 2021 //@ compile-flags: -Zno-profiler-runtime //@ compile-flags: -Cinstrument-coverage -Copt-level=0 -//@ revisions: LINUX DARWIN WINDOWS +//@ revisions: LINUX DARWIN WIN //@ [LINUX] only-linux //@ [LINUX] filecheck-flags: -DINSTR_PROF_DATA=__llvm_prf_data @@ -19,13 +19,13 @@ //@ [DARWIN] filecheck-flags: -DINSTR_PROF_COVFUN=__LLVM_COV,__llvm_covfun //@ [DARWIN] filecheck-flags: -DCOMDAT_IF_SUPPORTED= -//@ [WINDOWS] only-windows -//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_DATA=.lprfd$M -//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_NAME=.lprfn$M -//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_CNTS=.lprfc$M -//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_COVMAP=.lcovmap$M -//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_COVFUN=.lcovfun$M -//@ [WINDOWS] filecheck-flags: '-DCOMDAT_IF_SUPPORTED=, comdat' +//@ [WIN] only-windows +//@ [WIN] filecheck-flags: -DINSTR_PROF_DATA=.lprfd$M +//@ [WIN] filecheck-flags: -DINSTR_PROF_NAME=.lprfn$M +//@ [WIN] filecheck-flags: -DINSTR_PROF_CNTS=.lprfc$M +//@ [WIN] filecheck-flags: -DINSTR_PROF_COVMAP=.lcovmap$M +//@ [WIN] filecheck-flags: -DINSTR_PROF_COVFUN=.lcovfun$M +//@ [WIN] filecheck-flags: '-DCOMDAT_IF_SUPPORTED=, comdat' // ignore-tidy-linelength @@ -71,7 +71,7 @@ fn main() { // Check for metadata, variables, declarations, and function definitions injected // into LLVM IR when compiling with -Cinstrument-coverage. -// WINDOWS: $__llvm_profile_runtime_user = comdat any +// WIN: $__llvm_profile_runtime_user = comdat any // CHECK: @__llvm_coverage_mapping = private constant // CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8 @@ -79,7 +79,7 @@ fn main() { // CHECK: @__covrec_{{[A-F0-9]+}}u = linkonce_odr hidden constant // CHECK-SAME: section "[[INSTR_PROF_COVFUN]]"[[COMDAT_IF_SUPPORTED]], align 8 -// WINDOWS: @__llvm_profile_runtime = external{{.*}}global i32 +// WIN: @__llvm_profile_runtime = external{{.*}}global i32 // CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global // CHECK-SAME: section "[[INSTR_PROF_CNTS]]"{{.*}}, align 8 @@ -111,10 +111,10 @@ fn main() { // CHECK: declare void @llvm.instrprof.increment(ptr, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]] -// WINDOWS: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat { -// WINDOWS-NEXT: %1 = load i32, ptr @__llvm_profile_runtime -// WINDOWS-NEXT: ret i32 %1 -// WINDOWS-NEXT: } +// WIN: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat { +// WIN-NEXT: %1 = load i32, ptr @__llvm_profile_runtime +// WIN-NEXT: ret i32 %1 +// WIN-NEXT: } // CHECK: attributes #[[LLVM_INSTRPROF_INCREMENT_ATTR]] = { nounwind } -// WINDOWS: attributes #[[LLVM_PROFILE_RUNTIME_USER_ATTR]] = { noinline } +// WIN: attributes #[[LLVM_PROFILE_RUNTIME_USER_ATTR]] = { noinline } diff --git a/tests/codegen/issues/issue-107681-unwrap_unchecked.rs b/tests/codegen/issues/issue-107681-unwrap_unchecked.rs new file mode 100644 index 000000000000..7d9679d2322b --- /dev/null +++ b/tests/codegen/issues/issue-107681-unwrap_unchecked.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -O +//@ min-llvm-version: 19 + +// Test for #107681. +// Make sure we don't create `br` or `select` instructions. + +#![crate_type = "lib"] + +use std::iter::Copied; +use std::slice::Iter; + +#[no_mangle] +pub unsafe fn foo(x: &mut Copied>) -> u32 { + // CHECK-LABEL: @foo( + // CHECK-NOT: br + // CHECK-NOT: select + // CHECK: [[RET:%.*]] = load i32, ptr + // CHECK-NEXT: ret i32 [[RET]] + x.next().unwrap_unchecked() +} diff --git a/tests/codegen/issues/issue-118306.rs b/tests/codegen/issues/issue-118306.rs new file mode 100644 index 000000000000..8af1c6a971c7 --- /dev/null +++ b/tests/codegen/issues/issue-118306.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -O +//@ min-llvm-version: 19 +//@ only-x86_64 + +// Test for #118306. +// Make sure we don't create `br` or `select` instructions. + +#![crate_type = "lib"] + +#[no_mangle] +pub fn branchy(input: u64) -> u64 { + // CHECK-LABEL: @branchy( + // CHECK-NEXT: start: + // CHECK-NEXT: [[_2:%.*]] = and i64 [[INPUT:%.*]], 3 + // CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [4 x i64], ptr @switch.table.branchy, i64 0, i64 [[_2]] + // CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i64, ptr [[SWITCH_GEP]] + // CHECK-NEXT: ret i64 [[SWITCH_LOAD]] + match input % 4 { + 1 | 2 => 1, + 3 => 2, + _ => 0, + } +} diff --git a/tests/codegen/issues/issue-126585.rs b/tests/codegen/issues/issue-126585.rs new file mode 100644 index 000000000000..a468efd728d1 --- /dev/null +++ b/tests/codegen/issues/issue-126585.rs @@ -0,0 +1,24 @@ +//@ compile-flags: -Copt-level=s +//@ min-llvm-version: 19 +//@ only-x86_64 + +// Test for #126585. +// Ensure that this IR doesn't have extra undef phi input, which also guarantees that this asm +// doesn't have subsequent labels and unnecessary `jmp` instructions. + +#![crate_type = "lib"] + +#[no_mangle] +fn checked_div_round(a: u64, b: u64) -> Option { + // CHECK-LABEL: @checked_div_round + // CHECK: phi + // CHECK-NOT: undef + // CHECK: phi + // CHECK-NOT: undef + match b { + 0 => None, + 1 => Some(a), + // `a / b` is computable and `(a % b) * 2` can not overflow since `b >= 2`. + b => Some(a / b + if (a % b) * 2 >= b { 1 } else { 0 }), + } +} diff --git a/tests/codegen/repr/transparent-sysv64.rs b/tests/codegen/repr/transparent-sysv64.rs index afb06dcc1bdf..068414976c52 100644 --- a/tests/codegen/repr/transparent-sysv64.rs +++ b/tests/codegen/repr/transparent-sysv64.rs @@ -1,12 +1,12 @@ -//@ revisions: linux apple windows +//@ revisions: linux apple win //@ compile-flags: -O -C no-prepopulate-passes //@[linux] compile-flags: --target x86_64-unknown-linux-gnu //@[linux] needs-llvm-components: x86 //@[apple] compile-flags: --target x86_64-apple-darwin //@[apple] needs-llvm-components: x86 -//@[windows] compile-flags: --target x86_64-pc-windows-msvc -//@[windows] needs-llvm-components: x86 +//@[win] compile-flags: --target x86_64-pc-windows-msvc +//@[win] needs-llvm-components: x86 #![feature(no_core, lang_items)] #![crate_type = "lib"] diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index 87783706d953..f38a1ae72de5 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -1,7 +1,7 @@ //@ revisions: COMPAT INCOMPAT //@ needs-llvm-components: x86 //@ compile-flags: --target=x86_64-unknown-linux-gnu -Copt-level=3 -//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2,+avx,+sse4.2,+sse4.1,+ssse3,+sse3 +//@ [COMPAT] compile-flags: -Ctarget-feature=+avx2 //@ [INCOMPAT] compile-flags: -Ctarget-feature=-avx2,-avx // See also tests/assembly/target-feature-multiple.rs @@ -39,8 +39,8 @@ pub unsafe fn banana() -> u32 { } // CHECK: attributes [[APPLEATTRS]] -// COMPAT-SAME: "target-features"="+avx2,+avx,{{.*}}" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" // INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}" // CHECK: attributes [[BANANAATTRS]] -// COMPAT-SAME: "target-features"="+avx2,+avx,{{.*}}" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" // INCOMPAT-SAME: "target-features"="-avx2,-avx" diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs index 7f0805bc1b43..1b4596ae2cb5 100644 --- a/tests/codegen/tied-features-strength.rs +++ b/tests/codegen/tied-features-strength.rs @@ -8,7 +8,7 @@ // is LLVM-14 we can remove the optional regex matching for this feature. //@ [ENABLE_SVE] compile-flags: -C target-feature=+sve -Copt-level=0 -// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?))*}}" } +// ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } //@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0 // DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)?|(-sve,?)|(\+neon,?))*}}" } diff --git a/tests/crashes/121127.rs b/tests/crashes/121127.rs index 2e64bf68b82f..e50dc7763fc6 100644 --- a/tests/crashes/121127.rs +++ b/tests/crashes/121127.rs @@ -1,5 +1,5 @@ //@ known-bug: #121127 -//@ compile-flags: -Zpolymorphize=on -Zinline-mir=yes -C debuginfo=2 +//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes -C debuginfo=2 // Note that as of PR#123949 this only crashes with debuginfo enabled #![feature(specialization)] diff --git a/tests/crashes/122909.rs b/tests/crashes/122909.rs deleted file mode 100644 index 90bba772b915..000000000000 --- a/tests/crashes/122909.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ compile-flags: -Zpolymorphize=on -Zinline-mir=yes -//@ known-bug: #122909 - - -use std::sync::{Arc, Context, Weak}; - -pub struct WeakOnce(); -impl WeakOnce { - extern "rust-call" fn try_get(&self) -> Option> {} - - pub fn get(&self) -> Arc { - self.try_get() - .unwrap_or_else(|| panic!("Singleton {} not available", std::any::type_name::())) - } -} diff --git a/tests/crashes/126272.rs b/tests/crashes/126272.rs deleted file mode 100644 index 3412c7d8f0f4..000000000000 --- a/tests/crashes/126272.rs +++ /dev/null @@ -1,28 +0,0 @@ -//@ known-bug: rust-lang/rust#126272 - -#![feature(adt_const_params)] -#![allow(incomplete_features)] - -use std::marker::ConstParamTy; - -#[derive(Debug, PartialEq, Eq, ConstParamTy)] -struct Foo { - value: i32, - nested: &'static Bar, -} - -#[derive(Debug, PartialEq, Eq, ConstParamTy)] -struct Bar(T); - -struct Test; - -fn main() { - let x: Test< - { - Foo { - value: 3, - nested: &Bar(4), - } - }, - > = Test; -} diff --git a/tests/crashes/126896.rs b/tests/crashes/126896.rs index 35bf9d5207ac..49c539d7acc8 100644 --- a/tests/crashes/126896.rs +++ b/tests/crashes/126896.rs @@ -1,5 +1,5 @@ //@ known-bug: rust-lang/rust#126896 -//@ compile-flags: -Zpolymorphize=on -Zinline-mir=yes +//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes #![feature(type_alias_impl_trait)] type Two<'a, 'b> = impl std::fmt::Debug; diff --git a/tests/crashes/127299.rs b/tests/crashes/127299.rs deleted file mode 100644 index 7eb78387997a..000000000000 --- a/tests/crashes/127299.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ known-bug: rust-lang/rust#127299 -trait Qux { - fn bar() -> i32; -} - -pub struct Lint { - pub desc: &'static Qux, -} - -static FOO: &Lint = &Lint { desc: "desc" }; - -fn main() {} diff --git a/tests/debuginfo/pretty-std.rs b/tests/debuginfo/pretty-std.rs index 70827d551ca3..45c6dbf34395 100644 --- a/tests/debuginfo/pretty-std.rs +++ b/tests/debuginfo/pretty-std.rs @@ -81,10 +81,10 @@ // cdb-check:vec,d [...] : { len=4 } [Type: [...]::Vec] // cdb-check: [len] : 4 [Type: [...]] // cdb-check: [capacity] : [...] [Type: [...]] -// cdb-check: [0] : 4 [Type: unsigned __int64] -// cdb-check: [1] : 5 [Type: unsigned __int64] -// cdb-check: [2] : 6 [Type: unsigned __int64] -// cdb-check: [3] : 7 [Type: unsigned __int64] +// cdb-check: [0] : 4 [Type: u64] +// cdb-check: [1] : 5 [Type: u64] +// cdb-check: [2] : 6 [Type: u64] +// cdb-check: [3] : 7 [Type: u64] // cdb-command: dx str_slice // cdb-check:str_slice : "IAMA string slice!" [Type: ref$] @@ -141,8 +141,8 @@ // cdb-check: [] [Type: alloc::collections::vec_deque::VecDeque] // cdb-check: [len] : 0x2 [Type: unsigned [...]] // cdb-check: [capacity] : 0x8 [Type: unsigned [...]] -// cdb-check: [0x0] : 90 [Type: int] -// cdb-check: [0x1] : 20 [Type: int] +// cdb-check: [0x0] : 90 [Type: i32] +// cdb-check: [0x1] : 20 [Type: i32] #![allow(unused_variables)] use std::collections::{LinkedList, VecDeque}; diff --git a/tests/debuginfo/strings-and-strs.rs b/tests/debuginfo/strings-and-strs.rs index 4a6adc2fc533..2d29ac12bd8a 100644 --- a/tests/debuginfo/strings-and-strs.rs +++ b/tests/debuginfo/strings-and-strs.rs @@ -7,7 +7,7 @@ // gdb-command:run // gdb-command:print plain_string -// gdbr-check:$1 = alloc::string::String {vec: alloc::vec::Vec {buf: alloc::raw_vec::RawVec {ptr: core::ptr::unique::Unique {pointer: core::ptr::non_null::NonNull {pointer: 0x[...]}, _marker: core::marker::PhantomData}, cap: alloc::raw_vec::Cap (5), alloc: alloc::alloc::Global}, len: 5}} +// gdbr-check:$1 = alloc::string::String {vec: alloc::vec::Vec {buf: alloc::raw_vec::RawVec {inner: alloc::raw_vec::RawVecInner {ptr: core::ptr::unique::Unique {pointer: core::ptr::non_null::NonNull {pointer: 0x[...]}, _marker: core::marker::PhantomData}, cap: alloc::raw_vec::Cap (5), alloc: alloc::alloc::Global}, _marker: core::marker::PhantomData}, len: 5}} // gdb-command:print plain_str // gdbr-check:$2 = "Hello" diff --git a/tests/debuginfo/thread-names.rs b/tests/debuginfo/thread-names.rs index 1b9ada6fc52d..6b3b0c7f4b23 100644 --- a/tests/debuginfo/thread-names.rs +++ b/tests/debuginfo/thread-names.rs @@ -1,8 +1,8 @@ //@ compile-flags:-g -//@ revisions: macos windows +//@ revisions: macos win // We can't set the main thread name on Linux because it renames the process (#97191) //@[macos] only-macos -//@[windows] only-windows +//@[win] only-windows //@ ignore-sgx //@ ignore-windows-gnu diff --git a/tests/mir-opt/building/match/never_patterns.opt1.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/never_patterns.opt1.SimplifyCfg-initial.after.mir index 78356a90743a..bba4d9c0149a 100644 --- a/tests/mir-opt/building/match/never_patterns.opt1.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/never_patterns.opt1.SimplifyCfg-initial.after.mir @@ -13,17 +13,17 @@ fn opt1(_1: &Result) -> &u32 { bb0: { PlaceMention(_1); - _2 = discriminant((*_1)); - switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1]; + falseEdge -> [real: bb4, imaginary: bb1]; } bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; + _2 = discriminant((*_1)); + switchInt(move _2) -> [1: bb3, otherwise: bb2]; } bb2: { - falseEdge -> [real: bb4, imaginary: bb3]; + FakeRead(ForMatchedPlace(None), _1); + unreachable; } bb3: { diff --git a/tests/mir-opt/building/match/never_patterns.opt2.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/never_patterns.opt2.SimplifyCfg-initial.after.mir index 979fbb2860dc..fc0769d6f7dc 100644 --- a/tests/mir-opt/building/match/never_patterns.opt2.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/never_patterns.opt2.SimplifyCfg-initial.after.mir @@ -11,25 +11,10 @@ fn opt2(_1: &Result) -> &u32 { bb0: { PlaceMention(_1); - _2 = discriminant((*_1)); - switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1]; - } - - bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } - - bb2: { StorageLive(_3); _3 = &(((*_1) as Ok).0: u32); _0 = &(*_3); StorageDead(_3); return; } - - bb3: { - FakeRead(ForMatchedPlace(None), (((*_1) as Err).0: Void)); - unreachable; - } } diff --git a/tests/mir-opt/building/match/never_patterns.opt3.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/never_patterns.opt3.SimplifyCfg-initial.after.mir index 93ebe600b3ff..86347db4d92e 100644 --- a/tests/mir-opt/building/match/never_patterns.opt3.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/never_patterns.opt3.SimplifyCfg-initial.after.mir @@ -12,24 +12,19 @@ fn opt3(_1: &Result) -> &u32 { bb0: { PlaceMention(_1); _2 = discriminant((*_1)); - switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; + switchInt(move _2) -> [1: bb2, otherwise: bb1]; } bb1: { - FakeRead(ForMatchedPlace(None), _1); - unreachable; - } - - bb2: { - FakeRead(ForMatchedPlace(None), (((*_1) as Err).0: Void)); - unreachable; - } - - bb3: { StorageLive(_3); _3 = &(((*_1) as Ok).0: u32); _0 = &(*_3); StorageDead(_3); return; } + + bb2: { + FakeRead(ForMatchedPlace(None), (((*_1) as Err).0: Void)); + unreachable; + } } diff --git a/tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff b/tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff new file mode 100644 index 000000000000..279c1a1990dc --- /dev/null +++ b/tests/mir-opt/elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff @@ -0,0 +1,13 @@ +- // MIR for `pointee` before ElaborateBoxDerefs ++ // MIR for `pointee` after ElaborateBoxDerefs + + fn pointee(_1: Box) -> () { +- debug foo => (*_1); ++ debug foo => (*(((_1.0: std::ptr::Unique).0: std::ptr::NonNull).0: *const i32)); + let mut _0: (); + + bb0: { + return; + } + } + diff --git a/tests/mir-opt/elaborate_box_deref_in_debuginfo.rs b/tests/mir-opt/elaborate_box_deref_in_debuginfo.rs new file mode 100644 index 000000000000..0046e7104f1c --- /dev/null +++ b/tests/mir-opt/elaborate_box_deref_in_debuginfo.rs @@ -0,0 +1,20 @@ +// skip-filecheck +//@ test-mir-pass: ElaborateBoxDerefs + +#![feature(custom_mir, core_intrinsics)] + +extern crate core; +use core::intrinsics::mir::*; + +// EMIT_MIR elaborate_box_deref_in_debuginfo.pointee.ElaborateBoxDerefs.diff +#[custom_mir(dialect = "built")] +fn pointee(opt: Box) { + mir!( + debug foo => *opt; + { + Return() + } + ) +} + +fn main() {} diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir index 14ad951a4766..0fe4fd370722 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -5,63 +5,93 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _0: &[u8]; scope 1 (inlined as Deref>::deref) { debug self => _1; - let mut _4: *const u8; - let mut _5: usize; + let mut _7: usize; scope 2 (inlined Vec::::as_ptr) { debug self => _1; let mut _2: &alloc::raw_vec::RawVec; scope 3 (inlined alloc::raw_vec::RawVec::::ptr) { debug self => _2; - let mut _3: std::ptr::NonNull; - scope 4 (inlined Unique::::as_ptr) { - debug ((self: Unique).0: std::ptr::NonNull) => _3; - debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; - scope 5 (inlined NonNull::::as_ptr) { + let mut _3: &alloc::raw_vec::RawVecInner; + scope 4 (inlined alloc::raw_vec::RawVecInner::ptr::) { + debug self => _3; + let mut _6: std::ptr::NonNull; + scope 5 (inlined alloc::raw_vec::RawVecInner::non_null::) { debug self => _3; + let mut _4: std::ptr::NonNull; + scope 6 (inlined Unique::::cast::) { + debug ((self: Unique).0: std::ptr::NonNull) => _4; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 7 (inlined NonNull::::cast::) { + debug self => _4; + scope 8 (inlined NonNull::::as_ptr) { + debug self => _4; + let mut _5: *const u8; + } + } + } + scope 9 (inlined #[track_caller] as Into>>::into) { + debug ((self: Unique).0: std::ptr::NonNull) => _6; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 10 (inlined as From>>::from) { + debug ((unique: Unique).0: std::ptr::NonNull) => _6; + debug ((unique: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 11 (inlined Unique::::as_non_null_ptr) { + debug ((self: Unique).0: std::ptr::NonNull) => _6; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + } + } + } + } + scope 12 (inlined NonNull::::as_ptr) { + debug self => _6; } } } } - scope 6 (inlined std::slice::from_raw_parts::<'_, u8>) { - debug data => _4; - debug len => _5; - let _6: *const [u8]; - scope 7 (inlined core::ub_checks::check_language_ub) { - scope 8 (inlined core::ub_checks::check_language_ub::runtime) { + scope 13 (inlined std::slice::from_raw_parts::<'_, u8>) { + debug data => _5; + debug len => _7; + let _8: *const [u8]; + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { } } - scope 9 (inlined std::mem::size_of::) { + scope 16 (inlined std::mem::size_of::) { } - scope 10 (inlined align_of::) { + scope 17 (inlined align_of::) { } - scope 11 (inlined slice_from_raw_parts::) { - debug data => _4; - debug len => _5; - scope 12 (inlined std::ptr::from_raw_parts::<[u8], u8>) { - debug data_pointer => _4; - debug metadata => _5; + scope 18 (inlined slice_from_raw_parts::) { + debug data => _5; + debug len => _7; + scope 19 (inlined std::ptr::from_raw_parts::<[u8], u8>) { + debug data_pointer => _5; + debug metadata => _7; } } } } bb0: { - StorageLive(_4); StorageLive(_2); _2 = &((*_1).0: alloc::raw_vec::RawVec); StorageLive(_3); - _3 = ((((*_1).0: alloc::raw_vec::RawVec).0: std::ptr::Unique).0: std::ptr::NonNull); - _4 = (_3.0: *const u8); + _3 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + StorageLive(_6); + StorageLive(_4); + _4 = (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + _5 = (_4.0: *const u8); + _6 = NonNull:: { pointer: _5 }; + StorageDead(_4); + StorageDead(_6); StorageDead(_3); StorageDead(_2); - StorageLive(_5); - _5 = ((*_1).1: usize); - StorageLive(_6); - _6 = *const [u8] from (_4, _5); - _0 = &(*_6); - StorageDead(_6); - StorageDead(_5); - StorageDead(_4); + StorageLive(_7); + _7 = ((*_1).1: usize); + StorageLive(_8); + _8 = *const [u8] from (_5, _7); + _0 = &(*_8); + StorageDead(_8); + StorageDead(_7); return; } } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir index 14ad951a4766..0fe4fd370722 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -5,63 +5,93 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { let mut _0: &[u8]; scope 1 (inlined as Deref>::deref) { debug self => _1; - let mut _4: *const u8; - let mut _5: usize; + let mut _7: usize; scope 2 (inlined Vec::::as_ptr) { debug self => _1; let mut _2: &alloc::raw_vec::RawVec; scope 3 (inlined alloc::raw_vec::RawVec::::ptr) { debug self => _2; - let mut _3: std::ptr::NonNull; - scope 4 (inlined Unique::::as_ptr) { - debug ((self: Unique).0: std::ptr::NonNull) => _3; - debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; - scope 5 (inlined NonNull::::as_ptr) { + let mut _3: &alloc::raw_vec::RawVecInner; + scope 4 (inlined alloc::raw_vec::RawVecInner::ptr::) { + debug self => _3; + let mut _6: std::ptr::NonNull; + scope 5 (inlined alloc::raw_vec::RawVecInner::non_null::) { debug self => _3; + let mut _4: std::ptr::NonNull; + scope 6 (inlined Unique::::cast::) { + debug ((self: Unique).0: std::ptr::NonNull) => _4; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 7 (inlined NonNull::::cast::) { + debug self => _4; + scope 8 (inlined NonNull::::as_ptr) { + debug self => _4; + let mut _5: *const u8; + } + } + } + scope 9 (inlined #[track_caller] as Into>>::into) { + debug ((self: Unique).0: std::ptr::NonNull) => _6; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 10 (inlined as From>>::from) { + debug ((unique: Unique).0: std::ptr::NonNull) => _6; + debug ((unique: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 11 (inlined Unique::::as_non_null_ptr) { + debug ((self: Unique).0: std::ptr::NonNull) => _6; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + } + } + } + } + scope 12 (inlined NonNull::::as_ptr) { + debug self => _6; } } } } - scope 6 (inlined std::slice::from_raw_parts::<'_, u8>) { - debug data => _4; - debug len => _5; - let _6: *const [u8]; - scope 7 (inlined core::ub_checks::check_language_ub) { - scope 8 (inlined core::ub_checks::check_language_ub::runtime) { + scope 13 (inlined std::slice::from_raw_parts::<'_, u8>) { + debug data => _5; + debug len => _7; + let _8: *const [u8]; + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { } } - scope 9 (inlined std::mem::size_of::) { + scope 16 (inlined std::mem::size_of::) { } - scope 10 (inlined align_of::) { + scope 17 (inlined align_of::) { } - scope 11 (inlined slice_from_raw_parts::) { - debug data => _4; - debug len => _5; - scope 12 (inlined std::ptr::from_raw_parts::<[u8], u8>) { - debug data_pointer => _4; - debug metadata => _5; + scope 18 (inlined slice_from_raw_parts::) { + debug data => _5; + debug len => _7; + scope 19 (inlined std::ptr::from_raw_parts::<[u8], u8>) { + debug data_pointer => _5; + debug metadata => _7; } } } } bb0: { - StorageLive(_4); StorageLive(_2); _2 = &((*_1).0: alloc::raw_vec::RawVec); StorageLive(_3); - _3 = ((((*_1).0: alloc::raw_vec::RawVec).0: std::ptr::Unique).0: std::ptr::NonNull); - _4 = (_3.0: *const u8); + _3 = &(((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner); + StorageLive(_6); + StorageLive(_4); + _4 = (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + _5 = (_4.0: *const u8); + _6 = NonNull:: { pointer: _5 }; + StorageDead(_4); + StorageDead(_6); StorageDead(_3); StorageDead(_2); - StorageLive(_5); - _5 = ((*_1).1: usize); - StorageLive(_6); - _6 = *const [u8] from (_4, _5); - _0 = &(*_6); - StorageDead(_6); - StorageDead(_5); - StorageDead(_4); + StorageLive(_7); + _7 = ((*_1).1: usize); + StorageLive(_8); + _8 = *const [u8] from (_5, _7); + _0 = &(*_8); + StorageDead(_8); + StorageDead(_7); return; } } diff --git a/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff index da7a2bd10e01..1e1ddfae0eb9 100644 --- a/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff +++ b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-abort.diff @@ -19,14 +19,16 @@ bb1: { _2 = discriminant(_1); -- switchInt(move _2) -> [0: bb4, 1: bb3, otherwise: bb2]; -+ _5 = Eq(_2, const 0_isize); +- switchInt(move _2) -> [1: bb3, otherwise: bb2]; ++ _5 = Ne(_2, const 1_isize); + assume(move _5); -+ goto -> bb4; ++ goto -> bb2; } bb2: { - unreachable; + _0 = const (); + StorageDead(_1); + return; } bb3: { @@ -35,11 +37,5 @@ - StorageLive(_4); unreachable; } - - bb4: { - _0 = const (); - StorageDead(_1); - return; - } } diff --git a/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff index a2121fc684f5..809d24aa15a1 100644 --- a/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff +++ b/tests/mir-opt/unreachable.as_match.UnreachablePropagation.panic-unwind.diff @@ -19,14 +19,16 @@ bb1: { _2 = discriminant(_1); -- switchInt(move _2) -> [0: bb4, 1: bb3, otherwise: bb2]; -+ _5 = Eq(_2, const 0_isize); +- switchInt(move _2) -> [1: bb3, otherwise: bb2]; ++ _5 = Ne(_2, const 1_isize); + assume(move _5); -+ goto -> bb4; ++ goto -> bb2; } bb2: { - unreachable; + _0 = const (); + StorageDead(_1); + return; } bb3: { @@ -35,11 +37,5 @@ - StorageLive(_4); unreachable; } - - bb4: { - _0 = const (); - StorageDead(_1); - return; - } } diff --git a/tests/mir-opt/unreachable.rs b/tests/mir-opt/unreachable.rs index 881e3542f0ac..f7f4815ae7ce 100644 --- a/tests/mir-opt/unreachable.rs +++ b/tests/mir-opt/unreachable.rs @@ -45,18 +45,16 @@ fn as_match() { // CHECK: bb0: { // CHECK: {{_.*}} = empty() // CHECK: bb1: { - // CHECK: [[eq:_.*]] = Eq({{.*}}, const 0_isize); + // CHECK: [[eq:_.*]] = Ne({{.*}}, const 1_isize); // CHECK-NEXT: assume(move [[eq]]); - // CHECK-NEXT: goto -> bb4; + // CHECK-NEXT: goto -> bb2; // CHECK: bb2: { - // CHECK-NEXT: unreachable; + // CHECK: return; // CHECK: bb3: { // CHECK-NEXT: unreachable; - // CHECK: bb4: { - // CHECK: return; match empty() { - None => {} Some(_x) => match _x {}, + None => {} } } diff --git a/tests/mir-opt/unreachable_enum_branching.rs b/tests/mir-opt/unreachable_enum_branching.rs index fac14042b107..7647f9bf0779 100644 --- a/tests/mir-opt/unreachable_enum_branching.rs +++ b/tests/mir-opt/unreachable_enum_branching.rs @@ -49,7 +49,7 @@ struct Plop { fn simple() { // CHECK-LABEL: fn simple( // CHECK: [[discr:_.*]] = discriminant( - // CHECK: switchInt(move [[discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb2, otherwise: [[unreachable]]]; + // CHECK: switchInt(move [[discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb1, otherwise: [[unreachable]]]; // CHECK: [[unreachable]]: { // CHECK-NEXT: unreachable; match Test1::C { diff --git a/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-abort.diff b/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-abort.diff index 8aef99149367..5c08648fac3b 100644 --- a/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-abort.diff +++ b/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-abort.diff @@ -14,40 +14,40 @@ StorageLive(_2); _2 = Test1::C; _3 = discriminant(_2); -- switchInt(move _3) -> [0: bb4, 1: bb3, 2: bb2, otherwise: bb1]; -+ switchInt(move _3) -> [0: bb1, 1: bb1, 2: bb2, otherwise: bb1]; +- switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1]; ++ switchInt(move _3) -> [0: bb5, 1: bb5, 2: bb1, otherwise: bb5]; } bb1: { - unreachable; - } - - bb2: { StorageLive(_5); _5 = const "C"; _1 = &(*_5); StorageDead(_5); - goto -> bb5; + goto -> bb4; } - bb3: { + bb2: { StorageLive(_4); _4 = const "B(Empty)"; _1 = &(*_4); StorageDead(_4); - goto -> bb5; + goto -> bb4; + } + + bb3: { + _1 = const "A(Empty)"; + goto -> bb4; } bb4: { - _1 = const "A(Empty)"; - goto -> bb5; - } - - bb5: { StorageDead(_2); StorageDead(_1); _0 = const (); return; ++ } ++ ++ bb5: { ++ unreachable; } } diff --git a/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-unwind.diff b/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-unwind.diff index 8aef99149367..5c08648fac3b 100644 --- a/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-unwind.diff +++ b/tests/mir-opt/unreachable_enum_branching.simple.UnreachableEnumBranching.panic-unwind.diff @@ -14,40 +14,40 @@ StorageLive(_2); _2 = Test1::C; _3 = discriminant(_2); -- switchInt(move _3) -> [0: bb4, 1: bb3, 2: bb2, otherwise: bb1]; -+ switchInt(move _3) -> [0: bb1, 1: bb1, 2: bb2, otherwise: bb1]; +- switchInt(move _3) -> [0: bb3, 1: bb2, otherwise: bb1]; ++ switchInt(move _3) -> [0: bb5, 1: bb5, 2: bb1, otherwise: bb5]; } bb1: { - unreachable; - } - - bb2: { StorageLive(_5); _5 = const "C"; _1 = &(*_5); StorageDead(_5); - goto -> bb5; + goto -> bb4; } - bb3: { + bb2: { StorageLive(_4); _4 = const "B(Empty)"; _1 = &(*_4); StorageDead(_4); - goto -> bb5; + goto -> bb4; + } + + bb3: { + _1 = const "A(Empty)"; + goto -> bb4; } bb4: { - _1 = const "A(Empty)"; - goto -> bb5; - } - - bb5: { StorageDead(_2); StorageDead(_1); _0 = const (); return; ++ } ++ ++ bb5: { ++ unreachable; } } diff --git a/tests/run-make/cross-lang-lto-upstream-rlibs/Makefile b/tests/run-make/cross-lang-lto-upstream-rlibs/Makefile deleted file mode 100644 index 6f1caa31a806..000000000000 --- a/tests/run-make/cross-lang-lto-upstream-rlibs/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -include ../tools.mk - -# ignore windows due to libLLVM being present in PATH and the PATH and library path being the same -# (so fixing it is harder). See #57765 for context -ifndef IS_WINDOWS - -# This test makes sure that we don't loose upstream object files when compiling -# staticlibs with -C linker-plugin-lto - -all: staticlib.rs upstream.rs - $(RUSTC) upstream.rs -C linker-plugin-lto -Ccodegen-units=1 - - # Check No LTO - $(RUSTC) staticlib.rs -C linker-plugin-lto -Ccodegen-units=1 -L. -o $(TMPDIR)/staticlib.a - (cd $(TMPDIR); "$(LLVM_BIN_DIR)"/llvm-ar x ./staticlib.a) - # Make sure the upstream object file was included - ls $(TMPDIR)/upstream.*.rcgu.o - - # Cleanup - rm $(TMPDIR)/* - - # Check ThinLTO - $(RUSTC) upstream.rs -C linker-plugin-lto -Ccodegen-units=1 -Clto=thin - $(RUSTC) staticlib.rs -C linker-plugin-lto -Ccodegen-units=1 -Clto=thin -L. -o $(TMPDIR)/staticlib.a - (cd $(TMPDIR); "$(LLVM_BIN_DIR)"/llvm-ar x ./staticlib.a) - ls $(TMPDIR)/upstream.*.rcgu.o - -else - -all: - -endif diff --git a/tests/run-make/cross-lang-lto-upstream-rlibs/rmake.rs b/tests/run-make/cross-lang-lto-upstream-rlibs/rmake.rs new file mode 100644 index 000000000000..f0b8fa75bee3 --- /dev/null +++ b/tests/run-make/cross-lang-lto-upstream-rlibs/rmake.rs @@ -0,0 +1,57 @@ +// When using the flag -C linker-plugin-lto, static libraries could lose their upstream object +// files during compilation. This bug was fixed in #53031, and this test compiles a staticlib +// dependent on upstream, checking that the upstream object file still exists after no LTO and +// thin LTO. +// See https://github.com/rust-lang/rust/pull/53031 + +use run_make_support::{ + cwd, has_extension, has_prefix, has_suffix, llvm_ar, rfs, rustc, shallow_find_files, + static_lib_name, +}; + +fn main() { + // The test starts with no LTO enabled. + rustc().input("upstream.rs").arg("-Clinker-plugin-lto").codegen_units(1).run(); + rustc() + .input("staticlib.rs") + .arg("-Clinker-plugin-lto") + .codegen_units(1) + .output(static_lib_name("staticlib")) + .run(); + llvm_ar().extract().arg(static_lib_name("staticlib")).run(); + // Ensure the upstream object file was included. + assert_eq!( + shallow_find_files(cwd(), |path| { + has_prefix(path, "upstream.") && has_suffix(path, ".rcgu.o") + }) + .len(), + 1 + ); + // Remove all output files that are not source Rust code for cleanup. + for file in shallow_find_files(cwd(), |path| !has_extension(path, "rs")) { + rfs::remove_file(file) + } + + // Check it again, with Thin LTO. + rustc() + .input("upstream.rs") + .arg("-Clinker-plugin-lto") + .codegen_units(1) + .arg("-Clto=thin") + .run(); + rustc() + .input("staticlib.rs") + .arg("-Clinker-plugin-lto") + .codegen_units(1) + .arg("-Clto=thin") + .output(static_lib_name("staticlib")) + .run(); + llvm_ar().extract().arg(static_lib_name("staticlib")).run(); + assert_eq!( + shallow_find_files(cwd(), |path| { + has_prefix(path, "upstream.") && has_suffix(path, ".rcgu.o") + }) + .len(), + 1 + ); +} diff --git a/tests/run-make/dump-ice-to-disk/rmake.rs b/tests/run-make/dump-ice-to-disk/rmake.rs index 2fb5c825064b..48b4071e0654 100644 --- a/tests/run-make/dump-ice-to-disk/rmake.rs +++ b/tests/run-make/dump-ice-to-disk/rmake.rs @@ -1,81 +1,197 @@ -// This test checks if internal compilation error (ICE) log files work as expected. -// - Get the number of lines from the log files without any configuration options, -// then check that the line count doesn't change if the backtrace gets configured to be short -// or full. -// - Check that disabling ICE logging results in zero files created. -// - Check that the ICE files contain some of the expected strings. -// See https://github.com/rust-lang/rust/pull/108714 +//! This test checks if Internal Compilation Error (ICE) dump files `rustc-ice*.txt` work as +//! expected. +//! +//! - Basic sanity checks on a default ICE dump. +//! - Get the number of lines from the dump files without any `RUST_BACKTRACE` options, then check +//! ICE dump file (line count) is not affected by `RUSTC_BACKTRACE` settings. +//! - Check that disabling ICE dumping results in zero dump files created. +//! - Check that the ICE dump contain some of the expected strings. +//! - Check that `RUST_BACKTRACE=0` prevents ICE dump from created. +//! - Exercise the `-Zmetrics-dir` nightly flag (#128914): +//! - When `-Zmetrics=dir=PATH` is present but `RUSTC_ICE` is not set, check that the ICE dump +//! is placed under `PATH`. +//! - When `RUSTC_ICE=RUSTC_ICE_PATH` and `-Zmetrics-dir=METRICS_PATH` are both provided, check +//! that `RUSTC_ICE_PATH` takes precedence and no ICE dump is emitted under `METRICS_PATH`. +//! +//! See . -use run_make_support::{cwd, has_extension, has_prefix, rfs, rustc, shallow_find_files}; +//@ ignore-windows +// FIXME(#128911): @jieyouxu: This test is sometimes for whatever forsaken reason flakey in +// `i686-mingw`, and I cannot reproduce it locally. The error messages upon assertion failure in +// this test is intentionally extremely verbose to aid debugging that issue. -fn main() { - rustc().input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); - let default = get_text_from_ice(".").lines().count(); - clear_ice_files(); +use std::cell::OnceCell; +use std::path::{Path, PathBuf}; - rustc().env("RUSTC_ICE", cwd()).input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); - let ice_text = get_text_from_ice(cwd()); - let default_set = ice_text.lines().count(); - let content = ice_text; - let ice_files = shallow_find_files(cwd(), |path| { - has_prefix(path, "rustc-ice") && has_extension(path, "txt") - }); - assert_eq!(ice_files.len(), 1); // There should only be 1 ICE file. - let ice_file_name = - ice_files.first().and_then(|f| f.file_name()).and_then(|n| n.to_str()).unwrap(); - // Ensure that the ICE dump path doesn't contain `:`, because they cause problems on Windows. - assert!(!ice_file_name.contains(":"), "{ice_file_name}"); +use run_make_support::{ + cwd, filename_contains, has_extension, has_prefix, rfs, run_in_tmpdir, rustc, + shallow_find_files, +}; - clear_ice_files(); - rustc() - .env("RUSTC_ICE", cwd()) - .input("lib.rs") - .env("RUST_BACKTRACE", "short") - .arg("-Ztreat-err-as-bug=1") - .run_fail(); - let short = get_text_from_ice(cwd()).lines().count(); - clear_ice_files(); - rustc() - .env("RUSTC_ICE", cwd()) - .input("lib.rs") - .env("RUST_BACKTRACE", "full") - .arg("-Ztreat-err-as-bug=1") - .run_fail(); - let full = get_text_from_ice(cwd()).lines().count(); - clear_ice_files(); - - // The ICE dump is explicitly disabled. Therefore, this should produce no files. - rustc().env("RUSTC_ICE", "0").input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); - let ice_files = shallow_find_files(cwd(), |path| { - has_prefix(path, "rustc-ice") && has_extension(path, "txt") - }); - assert!(ice_files.is_empty()); // There should be 0 ICE files. - - // The line count should not change. - assert_eq!(short, default_set); - assert_eq!(short, default); - assert_eq!(full, default_set); - assert!(default > 0); - // Some of the expected strings in an ICE file should appear. - assert!(content.contains("thread 'rustc' panicked at")); - assert!(content.contains("stack backtrace:")); +#[derive(Debug)] +struct IceDump { + name: &'static str, + path: PathBuf, + message: String, } -fn clear_ice_files() { - let ice_files = shallow_find_files(cwd(), |path| { - has_prefix(path, "rustc-ice") && has_extension(path, "txt") - }); - for file in ice_files { - rfs::remove_file(file); +impl IceDump { + fn lines_count(&self) -> usize { + self.message.lines().count() } } #[track_caller] -fn get_text_from_ice(dir: impl AsRef) -> String { - let ice_files = - shallow_find_files(dir, |path| has_prefix(path, "rustc-ice") && has_extension(path, "txt")); - assert_eq!(ice_files.len(), 1); // There should only be 1 ICE file. - let ice_file = ice_files.get(0).unwrap(); - let output = rfs::read_to_string(ice_file); - output +fn assert_ice_len_equals(left: &IceDump, right: &IceDump) { + let left_len = left.lines_count(); + let right_len = right.lines_count(); + + if left_len != right_len { + eprintln!("=== {} ICE MESSAGE ({} lines) ====", left.name, left_len); + eprintln!("{}", left.message); + + eprintln!("=== {} ICE MESSAGE ({} lines) ====", right.name, right_len); + eprintln!("{}", right.message); + + eprintln!("===================================="); + panic!( + "ICE message length mismatch: {} has {} lines but {} has {} lines", + left.name, left_len, right.name, right_len + ); + } +} + +fn find_ice_dumps_in_dir>(dir: P) -> Vec { + shallow_find_files(dir, |path| has_prefix(path, "rustc-ice") && has_extension(path, "txt")) +} + +// Assert only one `rustc-ice*.txt` ICE file exists, and extract the ICE message from the ICE file. +#[track_caller] +fn extract_exactly_one_ice_file>(name: &'static str, dir: P) -> IceDump { + let ice_files = find_ice_dumps_in_dir(dir); + assert_eq!(ice_files.len(), 1); // There should only be 1 ICE file. + let path = ice_files.get(0).unwrap(); + let message = rfs::read_to_string(path); + IceDump { name, path: path.to_path_buf(), message } +} + +fn main() { + // Establish baseline ICE message. + let mut default_ice_dump = OnceCell::new(); + run_in_tmpdir(|| { + rustc().env("RUSTC_ICE", cwd()).input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); + let dump = extract_exactly_one_ice_file("baseline", cwd()); + // Ensure that the ICE dump path doesn't contain `:`, because they cause problems on + // Windows. + assert!(!filename_contains(&dump.path, ":"), "{} contains `:`", dump.path.display()); + // Some of the expected strings in an ICE file should appear. + assert!(dump.message.contains("thread 'rustc' panicked at")); + assert!(dump.message.contains("stack backtrace:")); + default_ice_dump.set(dump).unwrap(); + }); + let default_ice_dump = default_ice_dump.get().unwrap(); + + test_backtrace_short(default_ice_dump); + test_backtrace_full(default_ice_dump); + test_backtrace_disabled(default_ice_dump); + test_ice_dump_disabled(); + + test_metrics_dir(default_ice_dump); +} + +#[track_caller] +fn test_backtrace_short(baseline: &IceDump) { + run_in_tmpdir(|| { + rustc() + .env("RUSTC_ICE", cwd()) + .input("lib.rs") + .env("RUST_BACKTRACE", "short") + .arg("-Ztreat-err-as-bug=1") + .run_fail(); + let dump = extract_exactly_one_ice_file("RUST_BACKTRACE=short", cwd()); + // Backtrace length in dump shouldn't be changed by `RUST_BACKTRACE`. + assert_ice_len_equals(baseline, &dump); + }); +} + +#[track_caller] +fn test_backtrace_full(baseline: &IceDump) { + run_in_tmpdir(|| { + rustc() + .env("RUSTC_ICE", cwd()) + .input("lib.rs") + .env("RUST_BACKTRACE", "full") + .arg("-Ztreat-err-as-bug=1") + .run_fail(); + let dump = extract_exactly_one_ice_file("RUST_BACKTRACE=full", cwd()); + // Backtrace length in dump shouldn't be changed by `RUST_BACKTRACE`. + assert_ice_len_equals(baseline, &dump); + }); +} + +#[track_caller] +fn test_backtrace_disabled(baseline: &IceDump) { + run_in_tmpdir(|| { + rustc() + .env("RUSTC_ICE", cwd()) + .input("lib.rs") + .env("RUST_BACKTRACE", "0") + .arg("-Ztreat-err-as-bug=1") + .run_fail(); + let dump = extract_exactly_one_ice_file("RUST_BACKTRACE=disabled", cwd()); + // Backtrace length in dump shouldn't be changed by `RUST_BACKTRACE`. + assert_ice_len_equals(baseline, &dump); + }); +} + +#[track_caller] +fn test_ice_dump_disabled() { + // The ICE dump is explicitly disabled. Therefore, this should produce no files. + run_in_tmpdir(|| { + rustc().env("RUSTC_ICE", "0").input("lib.rs").arg("-Ztreat-err-as-bug=1").run_fail(); + let ice_files = find_ice_dumps_in_dir(cwd()); + assert!(ice_files.is_empty(), "there should be no ICE files if `RUSTC_ICE=0` is set"); + }); +} + +#[track_caller] +fn test_metrics_dir(baseline: &IceDump) { + test_flag_only(baseline); + test_flag_and_env(baseline); +} + +#[track_caller] +fn test_flag_only(baseline: &IceDump) { + run_in_tmpdir(|| { + let metrics_arg = format!("-Zmetrics-dir={}", cwd().display()); + rustc() + .env_remove("RUSTC_ICE") // prevent interference from environment + .input("lib.rs") + .arg("-Ztreat-err-as-bug=1") + .arg(metrics_arg) + .run_fail(); + let dump = extract_exactly_one_ice_file("-Zmetrics-dir only", cwd()); + assert_ice_len_equals(baseline, &dump); + }); +} + +#[track_caller] +fn test_flag_and_env(baseline: &IceDump) { + run_in_tmpdir(|| { + let metrics_arg = format!("-Zmetrics-dir={}", cwd().display()); + let real_dir = cwd().join("actually_put_ice_here"); + rfs::create_dir(&real_dir); + rustc() + .input("lib.rs") + .env("RUSTC_ICE", &real_dir) + .arg("-Ztreat-err-as-bug=1") + .arg(metrics_arg) + .run_fail(); + + let cwd_ice_files = find_ice_dumps_in_dir(cwd()); + assert!(cwd_ice_files.is_empty(), "RUSTC_ICE should override -Zmetrics-dir"); + + let dump = extract_exactly_one_ice_file("RUSTC_ICE overrides -Zmetrics-dir", real_dir); + assert_ice_len_equals(baseline, &dump); + }); } diff --git a/tests/run-make/fmt-write-bloat/main.rs b/tests/run-make/fmt-write-bloat/main.rs index e86c48014c3a..6f206d6515a3 100644 --- a/tests/run-make/fmt-write-bloat/main.rs +++ b/tests/run-make/fmt-write-bloat/main.rs @@ -5,7 +5,7 @@ use core::fmt; use core::fmt::Write; -#[link(name = "c")] +#[cfg_attr(not(windows), link(name = "c"))] extern "C" {} struct Dummy; diff --git a/tests/run-make/fmt-write-bloat/rmake.rs b/tests/run-make/fmt-write-bloat/rmake.rs index 4ae226ec0e23..6875ef9ddc05 100644 --- a/tests/run-make/fmt-write-bloat/rmake.rs +++ b/tests/run-make/fmt-write-bloat/rmake.rs @@ -15,9 +15,12 @@ //! `NO_DEBUG_ASSERTIONS=1`). If debug assertions are disabled, then we can check for the absence of //! additional `usize` formatting and padding related symbols. -// Reason: This test is `ignore-windows` because the `no_std` test (using `#[link(name = "c")])` -// doesn't link on windows. //@ ignore-windows +// Reason: +// - MSVC targets really need to parse the .pdb file (aka the debug information). +// On Windows there's an API for that (dbghelp) which maybe we can use +// - MinGW targets have a lot of symbols included in their runtime which we can't avoid. +// We would need to make the symbols we're looking for more specific for this test to work. //@ ignore-cross-compile use run_make_support::env::no_debug_assertions; diff --git a/tests/run-make/link-args-order/rmake.rs b/tests/run-make/link-args-order/rmake.rs index d238ad23f27c..b7ef8333267f 100644 --- a/tests/run-make/link-args-order/rmake.rs +++ b/tests/run-make/link-args-order/rmake.rs @@ -3,15 +3,14 @@ // checks that linker arguments remain intact and in the order they were originally passed in. // See https://github.com/rust-lang/rust/pull/70665 -//@ ignore-msvc -// Reason: the ld linker does not exist on Windows. - -use run_make_support::rustc; +use run_make_support::{is_msvc, rustc}; fn main() { + let linker = if is_msvc() { "msvc" } else { "ld" }; + rustc() .input("empty.rs") - .linker_flavor("ld") + .linker_flavor(linker) .link_arg("a") .link_args("b c") .link_args("d e") @@ -20,7 +19,7 @@ fn main() { .assert_stderr_contains(r#""a" "b" "c" "d" "e" "f""#); rustc() .input("empty.rs") - .linker_flavor("ld") + .linker_flavor(linker) .arg("-Zpre-link-arg=a") .arg("-Zpre-link-args=b c") .arg("-Zpre-link-args=d e") diff --git a/tests/run-make/link-dedup/rmake.rs b/tests/run-make/link-dedup/rmake.rs index 9bff3a4b44c7..6075f3109542 100644 --- a/tests/run-make/link-dedup/rmake.rs +++ b/tests/run-make/link-dedup/rmake.rs @@ -5,20 +5,37 @@ // Without the --cfg flag, there should be a single -ltesta, no more, no less. // See https://github.com/rust-lang/rust/pull/84794 -//@ ignore-msvc +use std::fmt::Write; -use run_make_support::rustc; +use run_make_support::{is_msvc, rustc}; fn main() { rustc().input("depa.rs").run(); rustc().input("depb.rs").run(); rustc().input("depc.rs").run(); + let output = rustc().input("empty.rs").cfg("bar").run_fail(); - output.assert_stderr_contains(r#""-ltesta" "-ltestb" "-ltesta""#); + output.assert_stderr_contains(needle_from_libs(&["testa", "testb", "testa"])); + let output = rustc().input("empty.rs").run_fail(); - output.assert_stderr_contains(r#""-ltesta""#); - let output = rustc().input("empty.rs").run_fail(); - output.assert_stderr_not_contains(r#""-ltestb""#); - let output = rustc().input("empty.rs").run_fail(); - output.assert_stderr_not_contains(r#""-ltesta" "-ltesta" "-ltesta""#); + output.assert_stderr_contains(needle_from_libs(&["testa"])); + output.assert_stderr_not_contains(needle_from_libs(&["testb"])); + output.assert_stderr_not_contains(needle_from_libs(&["testa", "testa", "testa"])); + // Adjacent identical native libraries are no longer deduplicated if + // they come from different crates (https://github.com/rust-lang/rust/pull/103311) + // so the following will fail: + //output.assert_stderr_not_contains(needle_from_libs(&["testa", "testa"])); +} + +fn needle_from_libs(libs: &[&str]) -> String { + let mut needle = String::new(); + for lib in libs { + if is_msvc() { + let _ = needle.write_fmt(format_args!(r#""{lib}.lib" "#)); + } else { + let _ = needle.write_fmt(format_args!(r#""-l{lib}" "#)); + } + } + needle.pop(); // remove trailing space + needle } diff --git a/tests/run-make/long-linker-command-lines-cmd-exe/Makefile b/tests/run-make/long-linker-command-lines-cmd-exe/Makefile deleted file mode 100644 index e43aab7f8e0f..000000000000 --- a/tests/run-make/long-linker-command-lines-cmd-exe/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) foo.rs -g - cp foo.bat $(TMPDIR)/ - OUT_DIR="$(TMPDIR)" RUSTC="$(RUSTC_ORIGINAL)" $(call RUN,foo) diff --git a/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs b/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs index 1d5202dcdb49..a28cc7909fef 100644 --- a/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs +++ b/tests/run-make/long-linker-command-lines-cmd-exe/foo.rs @@ -1,16 +1,3 @@ -// Like the `long-linker-command-lines` test this test attempts to blow -// a command line limit for running the linker. Unlike that test, however, -// this test is testing `cmd.exe` specifically rather than the OS. -// -// Unfortunately `cmd.exe` has a 8192 limit which is relatively small -// in the grand scheme of things and anyone sripting rustc's linker -// is probably using a `*.bat` script and is likely to hit this limit. -// -// This test uses a `foo.bat` script as the linker which just simply -// delegates back to this program. The compiler should use a lower -// limit for arguments before passing everything via `@`, which -// means that everything should still succeed here. - use std::env; use std::fs::{self, File}; use std::io::{BufWriter, Read, Write}; @@ -18,13 +5,8 @@ use std::path::PathBuf; use std::process::Command; fn main() { - if !cfg!(windows) { - return; - } - - let tmpdir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - let ok = tmpdir.join("ok"); - let not_ok = tmpdir.join("not_ok"); + let ok = PathBuf::from("ok"); + let not_ok = PathBuf::from("not_ok"); if env::var("YOU_ARE_A_LINKER").is_ok() { match env::args_os().find(|a| a.to_string_lossy().contains("@")) { Some(file) => { @@ -45,7 +27,7 @@ fn main() { for i in (1..).map(|i| i * 10) { println!("attempt: {}", i); - let file = tmpdir.join("bar.rs"); + let file = PathBuf::from("bar.rs"); let mut f = BufWriter::new(File::create(&file).unwrap()); let mut lib_name = String::new(); for _ in 0..i { @@ -63,8 +45,6 @@ fn main() { .arg(&file) .arg("-C") .arg(&bat_linker) - .arg("--out-dir") - .arg(&tmpdir) .env("YOU_ARE_A_LINKER", "1") .env("MY_LINKER", &me) .status() diff --git a/tests/run-make/long-linker-command-lines-cmd-exe/rmake.rs b/tests/run-make/long-linker-command-lines-cmd-exe/rmake.rs new file mode 100644 index 000000000000..60ed2c5bcd20 --- /dev/null +++ b/tests/run-make/long-linker-command-lines-cmd-exe/rmake.rs @@ -0,0 +1,26 @@ +// Like the `long-linker-command-lines` test this test attempts to blow +// a command line limit for running the linker. Unlike that test, however, +// this test is testing `cmd.exe` specifically rather than the OS. +// +// Unfortunately, the maximum length of the string that you can use at the +// command prompt (`cmd.exe`) is 8191 characters. +// Anyone scripting rustc's linker +// is probably using a `*.bat` script and is likely to hit this limit. +// +// This test uses a `foo.bat` script as the linker which just simply +// delegates back to this program. The compiler should use a lower +// limit for arguments before passing everything via `@`, which +// means that everything should still succeed here. +// See https://github.com/rust-lang/rust/pull/47507 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed +//@ only-windows +// Reason: this test is specific to Windows executables + +use run_make_support::{run, rustc}; + +fn main() { + rustc().input("foo.rs").arg("-g").run(); + run("foo"); +} diff --git a/tests/run-make/long-linker-command-lines/Makefile b/tests/run-make/long-linker-command-lines/Makefile deleted file mode 100644 index b573038e344a..000000000000 --- a/tests/run-make/long-linker-command-lines/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -export LD_LIBRARY_PATH := $(HOST_RPATH_DIR) - -all: - $(RUSTC) foo.rs -g -O - RUSTC="$(RUSTC_ORIGINAL)" $(call RUN,foo) diff --git a/tests/run-make/long-linker-command-lines/foo.rs b/tests/run-make/long-linker-command-lines/foo.rs index 9d4a701ad876..5b30c06fac9e 100644 --- a/tests/run-make/long-linker-command-lines/foo.rs +++ b/tests/run-make/long-linker-command-lines/foo.rs @@ -1,12 +1,3 @@ -// This is a test which attempts to blow out the system limit with how many -// arguments can be passed to a process. This'll successively call rustc with -// larger and larger argument lists in an attempt to find one that's way too -// big for the system at hand. This file itself is then used as a "linker" to -// detect when the process creation succeeds. -// -// Eventually we should see an argument that looks like `@` as we switch from -// passing literal arguments to passing everything in the file. - use std::collections::HashSet; use std::env; use std::fs::{self, File}; @@ -43,8 +34,7 @@ fn read_linker_args(path: &Path) -> String { } fn main() { - let tmpdir = PathBuf::from(env::var_os("TMPDIR").unwrap()); - let ok = tmpdir.join("ok"); + let ok = PathBuf::from("ok"); if env::var("YOU_ARE_A_LINKER").is_ok() { if let Some(file) = env::args_os().find(|a| a.to_string_lossy().contains("@")) { let file = file.to_str().expect("non-utf8 file argument"); @@ -53,11 +43,11 @@ fn main() { return; } - let rustc = env::var_os("RUSTC").unwrap_or("rustc".into()); + let rustc = env::var_os("RUSTC").unwrap(); let me_as_linker = format!("linker={}", env::current_exe().unwrap().display()); for i in (1..).map(|i| i * 100) { println!("attempt: {}", i); - let file = tmpdir.join("bar.rs"); + let file = PathBuf::from("bar.rs"); let mut expected_libs = write_test_case(&file, i); drop(fs::remove_file(&ok)); @@ -65,8 +55,6 @@ fn main() { .arg(&file) .arg("-C") .arg(&me_as_linker) - .arg("--out-dir") - .arg(&tmpdir) .env("YOU_ARE_A_LINKER", "1") .output() .unwrap(); diff --git a/tests/run-make/long-linker-command-lines/rmake.rs b/tests/run-make/long-linker-command-lines/rmake.rs new file mode 100644 index 000000000000..e832d7f03e27 --- /dev/null +++ b/tests/run-make/long-linker-command-lines/rmake.rs @@ -0,0 +1,19 @@ +// This is a test which attempts to blow out the system limit with how many +// arguments can be passed to a process. This'll successively call rustc with +// larger and larger argument lists in an attempt to find one that's way too +// big for the system at hand. This file itself is then used as a "linker" to +// detect when the process creation succeeds. +// +// Eventually we should see an argument that looks like `@` as we switch from +// passing literal arguments to passing everything in the file. +// See https://github.com/rust-lang/rust/issues/41190 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{run, rustc}; + +fn main() { + rustc().input("foo.rs").arg("-g").opt().run(); + run("foo"); +} diff --git a/tests/run-make/mte-ffi/bar.h b/tests/run-make/mte-ffi/bar.h new file mode 100644 index 000000000000..a2292ae02a30 --- /dev/null +++ b/tests/run-make/mte-ffi/bar.h @@ -0,0 +1,43 @@ +#ifndef __BAR_H +#define __BAR_H + +#include +#include +#include +#include +#include + +// Set the allocation tag on the destination address using the STG instruction. +#define set_tag(tagged_addr) do { \ + asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \ +} while (0) + +int mte_enabled() { + return (getauxval(AT_HWCAP2)) & HWCAP2_MTE; +} + +void *alloc_page() { + // Enable MTE with synchronous checking + if (prctl(PR_SET_TAGGED_ADDR_CTRL, + PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | (0xfffe << PR_MTE_TAG_SHIFT), + 0, 0, 0)) + { + perror("prctl() failed"); + } + + // Using `mmap` allows us to ensure that, on systems which support MTE, the allocated + // memory is 16-byte aligned for MTE. + // This also allows us to explicitly specify whether the region should be protected by + // MTE or not. + if (mte_enabled()) { + void *ptr = mmap(NULL, sysconf(_SC_PAGESIZE), + PROT_READ | PROT_WRITE | PROT_MTE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + } else { + void *ptr = mmap(NULL, sysconf(_SC_PAGESIZE), + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0); + } +} + +#endif // __BAR_H diff --git a/tests/run-make/mte-ffi/bar_float.c b/tests/run-make/mte-ffi/bar_float.c new file mode 100644 index 000000000000..a1590f62765a --- /dev/null +++ b/tests/run-make/mte-ffi/bar_float.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include "bar.h" + +extern void foo(char*); + +void bar(char *ptr) { + if (((uintptr_t)ptr >> 56) != 0x1f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } +} + +int main(void) +{ + float *ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (float *)((uintptr_t)ptr | 0x1fl << 56); + + if (mte_enabled()) { + set_tag(ptr); + } + + ptr[0] = 2.0f; + ptr[1] = 1.5f; + + foo(ptr); // should change the contents of the page and call `bar` + + if (ptr[0] != 0.5f || ptr[1] != 0.2f) { + fprintf(stderr, "invalid data in memory; expected '0.5 0.2', got '%f %f'\n", + ptr[0], ptr[1]); + return EXIT_FAILURE; + } + + return 0; +} diff --git a/tests/run-make/mte-ffi/bar_function.c b/tests/run-make/mte-ffi/bar_function.c new file mode 100644 index 000000000000..1fa48d32a0c8 --- /dev/null +++ b/tests/run-make/mte-ffi/bar_function.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include "bar.h" + +typedef void (*fp)(int (*)()); + +extern void foo(fp); + +void bar(int (*ptr)()) { + if (((uintptr_t)ptr >> 56) != 0x2f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } + + int r = (*ptr)(); + if (r != 32) { + fprintf(stderr, "invalid return value; expected 32, got '%d'\n", r); + exit(1); + } +} + +int main(void) +{ + fp ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (fp)((uintptr_t)&bar | 0x1fl << 56); + + foo(ptr); + + return 0; +} diff --git a/tests/run-make/mte-ffi/bar_int.c b/tests/run-make/mte-ffi/bar_int.c new file mode 100644 index 000000000000..d1c79e95dc9c --- /dev/null +++ b/tests/run-make/mte-ffi/bar_int.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include "bar.h" + +extern void foo(unsigned int *); + +void bar(char *ptr) { + if (((uintptr_t)ptr >> 56) != 0x1f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } +} + +int main(void) +{ + // Construct a pointer with an arbitrary tag in bits 56-59, simulating an MTE tag. + // It's only necessary that the tag is preserved across FFI bounds for this test. + unsigned int *ptr; + + ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56); + + if (mte_enabled()) { + set_tag(ptr); + } + + ptr[0] = 61; + ptr[1] = 62; + + foo(ptr); // should change the contents of the page to start with 0x63 0x64 and call `bar` + + if (ptr[0] != 0x63 || ptr[1] != 0x64) { + fprintf(stderr, "invalid data in memory; expected '63 64', got '%d %d'\n", ptr[0], ptr[1]); + return EXIT_FAILURE; + } + + return 0; +} diff --git a/tests/run-make/mte-ffi/bar_string.c b/tests/run-make/mte-ffi/bar_string.c new file mode 100644 index 000000000000..5669ffd6695e --- /dev/null +++ b/tests/run-make/mte-ffi/bar_string.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include "bar.h" + +extern void foo(char*); + +void bar(char *ptr) { + if (((uintptr_t)ptr >> 56) != 0x2f) { + fprintf(stderr, "Top byte corrupted on Rust -> C FFI boundary!\n"); + exit(1); + } + + if (strcmp(ptr, "cd")) { + fprintf(stderr, "invalid data in memory; expected 'cd', got '%s'\n", ptr); + exit(1); + } +} + +int main(void) +{ + // Construct a pointer with an arbitrary tag in bits 56-59, simulating an MTE tag. + // It's only necessary that the tag is preserved across FFI bounds for this test. + char *ptr; + + ptr = alloc_page(); + if (ptr == MAP_FAILED) + { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + // Store an arbitrary tag in bits 56-59 of the pointer (where an MTE tag may be), + // and a different value in the ignored top 4 bits. + ptr = (unsigned int *)((uintptr_t)ptr | 0x1fl << 56); + + if (mte_enabled()) { + set_tag(ptr); + } + + ptr[0] = 'a'; + ptr[1] = 'b'; + ptr[2] = '\0'; + + foo(ptr); + + return 0; +} diff --git a/tests/run-make/mte-ffi/foo_float.rs b/tests/run-make/mte-ffi/foo_float.rs new file mode 100644 index 000000000000..c1bedd524945 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_float.rs @@ -0,0 +1,19 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +use std::os::raw::c_float; + +extern "C" { + fn bar(ptr: *const c_float); +} + +#[no_mangle] +pub extern "C" fn foo(ptr: *mut c_float) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + unsafe { + *ptr = 0.5; + *ptr.wrapping_add(1) = 0.2; + bar(ptr); + } +} diff --git a/tests/run-make/mte-ffi/foo_function.rs b/tests/run-make/mte-ffi/foo_function.rs new file mode 100644 index 000000000000..2c8e0b262385 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_function.rs @@ -0,0 +1,17 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +extern "C" fn ret32() -> i32 { + 32 +} + +#[no_mangle] +pub extern "C" fn foo(ptr: extern "C" fn(extern "C" fn() -> i32)) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + // Store an arbitrary tag in the tag bits, and convert back to the correct pointer type. + let p = ((ret32 as usize) | (0x2f << 56)) as *const (); + let p: extern "C" fn() -> i32 = unsafe { std::mem::transmute(p) }; + + unsafe { ptr(p) } +} diff --git a/tests/run-make/mte-ffi/foo_int.rs b/tests/run-make/mte-ffi/foo_int.rs new file mode 100644 index 000000000000..106d863cb812 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_int.rs @@ -0,0 +1,19 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +use std::os::raw::c_uint; + +extern "C" { + fn bar(ptr: *const c_uint); +} + +#[no_mangle] +pub extern "C" fn foo(ptr: *mut c_uint) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + unsafe { + *ptr = 0x63; + *ptr.wrapping_add(1) = 0x64; + bar(ptr); + } +} diff --git a/tests/run-make/mte-ffi/foo_string.rs b/tests/run-make/mte-ffi/foo_string.rs new file mode 100644 index 000000000000..547448024489 --- /dev/null +++ b/tests/run-make/mte-ffi/foo_string.rs @@ -0,0 +1,27 @@ +#![crate_type = "cdylib"] +#![crate_name = "foo"] + +use std::arch::asm; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +extern "C" { + fn bar(ptr: *const c_char); +} + +#[no_mangle] +pub extern "C" fn foo(ptr: *const c_char) { + assert_eq!((ptr as usize) >> 56, 0x1f); + + let s = unsafe { CStr::from_ptr(ptr) }; + assert_eq!(s.to_str().unwrap(), "ab"); + + let s = CString::from_vec_with_nul("cd\0".into()).unwrap(); + let mut p = ((s.as_ptr() as usize) | (0x2f << 56)) as *const c_char; + unsafe { + #[cfg(target_feature = "mte")] + asm!("stg {p}, [{p}]", p = inout(reg) p); + + bar(p); + } +} diff --git a/tests/run-make/mte-ffi/rmake.rs b/tests/run-make/mte-ffi/rmake.rs new file mode 100644 index 000000000000..f4fafb796e3c --- /dev/null +++ b/tests/run-make/mte-ffi/rmake.rs @@ -0,0 +1,38 @@ +// Tests that MTE tags and values stored in the top byte of a pointer (TBI) are +// preserved across FFI boundaries (C <-> Rust). +// This test does not require MTE: whilst the test will use MTE if available, if it is not, +// arbitrary tag bits are set using TBI. + +// This test is only valid for AArch64. +// The linker must be explicitly specified when cross-compiling, so it is limited to +// `aarch64-unknown-linux-gnu`. +//@ only-aarch64-unknown-linux-gnu + +use run_make_support::{cc, dynamic_lib_name, extra_c_flags, run, rustc, target}; + +fn main() { + run_test("int"); + run_test("float"); + run_test("string"); + run_test("function"); +} + +fn run_test(variant: &str) { + let flags = { + let mut flags = extra_c_flags(); + flags.push("-march=armv8.5-a+memtag"); + flags + }; + println!("{variant} test..."); + rustc() + .input(format!("foo_{variant}.rs")) + .target(target()) + .linker("aarch64-linux-gnu-gcc") + .run(); + cc().input(format!("bar_{variant}.c")) + .input(dynamic_lib_name("foo")) + .out_exe("test") + .args(&flags) + .run(); + run("test"); +} diff --git a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs new file mode 100644 index 000000000000..f00123f006b2 --- /dev/null +++ b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs @@ -0,0 +1,89 @@ +#![feature(naked_functions, asm_const, linkage)] +#![crate_type = "dylib"] + +use std::arch::asm; + +pub trait TraitWithConst { + const COUNT: u32; +} + +struct Test; + +impl TraitWithConst for Test { + const COUNT: u32 = 1; +} + +#[no_mangle] +fn entry() { + private_vanilla(); + private_naked(); + + public_vanilla_generic::(); + public_naked_generic::(); +} + +extern "C" fn private_vanilla() -> u32 { + 42 +} + +#[naked] +extern "C" fn private_naked() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +#[no_mangle] +pub extern "C" fn public_vanilla() -> u32 { + 42 +} + +#[naked] +#[no_mangle] +pub extern "C" fn public_naked() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +pub extern "C" fn public_vanilla_generic() -> u32 { + T::COUNT +} + +#[naked] +pub extern "C" fn public_naked_generic() -> u32 { + unsafe { asm!("mov rax, {}", "ret", const T::COUNT, options(noreturn)) } +} + +#[linkage = "external"] +extern "C" fn vanilla_external_linkage() -> u32 { + 42 +} + +#[naked] +#[linkage = "external"] +extern "C" fn naked_external_linkage() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +#[cfg(not(windows))] +#[linkage = "weak"] +extern "C" fn vanilla_weak_linkage() -> u32 { + 42 +} + +#[naked] +#[cfg(not(windows))] +#[linkage = "weak"] +extern "C" fn naked_weak_linkage() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +// functions that are declared in an `extern "C"` block are currently not exported +// this maybe should change in the future, this is just tracking the current behavior +// reported in https://github.com/rust-lang/rust/issues/128071 +std::arch::global_asm! { + ".globl function_defined_in_global_asm", + "function_defined_in_global_asm:", + "ret", +} + +extern "C" { + pub fn function_defined_in_global_asm(); +} diff --git a/tests/run-make/naked-symbol-visibility/rmake.rs b/tests/run-make/naked-symbol-visibility/rmake.rs new file mode 100644 index 000000000000..a32e62326eb3 --- /dev/null +++ b/tests/run-make/naked-symbol-visibility/rmake.rs @@ -0,0 +1,98 @@ +//@ ignore-windows +//@ only-x86_64 +use run_make_support::object::read::{File, Object, Symbol}; +use run_make_support::object::ObjectSymbol; +use run_make_support::targets::is_windows; +use run_make_support::{dynamic_lib_name, env_var, rfs, rustc}; + +fn main() { + let rdylib_name = dynamic_lib_name("a_rust_dylib"); + rustc().arg("-Zshare-generics=no").input("a_rust_dylib.rs").run(); + + let binary_data = rfs::read(&rdylib_name); + let rdylib = File::parse(&*binary_data).unwrap(); + + // naked should mirror vanilla + not_exported(&rdylib, "private_vanilla"); + not_exported(&rdylib, "private_naked"); + + global_function(&rdylib, "public_vanilla"); + global_function(&rdylib, "public_naked"); + + not_exported(&rdylib, "public_vanilla_generic"); + not_exported(&rdylib, "public_naked_generic"); + + global_function(&rdylib, "vanilla_external_linkage"); + global_function(&rdylib, "naked_external_linkage"); + + // FIXME: make this work on windows (gnu and msvc). See the PR + // https://github.com/rust-lang/rust/pull/128362 for some approaches + // that don't work + // + // #[linkage = "weak"] does not work well on windows, we get + // + // lib.def : error LNK2001: unresolved external symbol naked_weak_linkage␍ + // lib.def : error LNK2001: unresolved external symbol vanilla_weak_linkage + // + // so just skip weak symbols on windows (for now) + if !is_windows() { + weak_function(&rdylib, "vanilla_weak_linkage"); + weak_function(&rdylib, "naked_weak_linkage"); + } + + // functions that are declared in an `extern "C"` block are currently not exported + // this maybe should change in the future, this is just tracking the current behavior + // reported in https://github.com/rust-lang/rust/issues/128071 + not_exported(&rdylib, "function_defined_in_global_asm"); + + // share generics should expose the generic functions + rustc().arg("-Zshare-generics=yes").input("a_rust_dylib.rs").run(); + let binary_data = rfs::read(&rdylib_name); + let rdylib = File::parse(&*binary_data).unwrap(); + + global_function(&rdylib, "public_vanilla_generic"); + global_function(&rdylib, "public_naked_generic"); +} + +#[track_caller] +fn global_function(file: &File, symbol_name: &str) { + let symbols = find_dynamic_symbol(file, symbol_name); + let [symbol] = symbols.as_slice() else { + panic!("symbol {symbol_name} occurs {} times", symbols.len()) + }; + + assert!(symbol.is_definition(), "`{symbol_name}` is not a function"); + assert!(symbol.is_global(), "`{symbol_name}` is not marked as global"); +} + +#[track_caller] +fn weak_function(file: &File, symbol_name: &str) { + let symbols = find_dynamic_symbol(file, symbol_name); + let [symbol] = symbols.as_slice() else { + panic!("symbol {symbol_name} occurs {} times", symbols.len()) + }; + + assert!(symbol.is_definition(), "`{symbol_name}` is not a function"); + assert!(symbol.is_weak(), "`{symbol_name}` is not marked as weak"); +} + +#[track_caller] +fn not_exported(file: &File, symbol_name: &str) { + assert_eq!(find_dynamic_symbol(file, symbol_name).len(), 0) +} + +fn find_subsequence(haystack: &[u8], needle: &[u8]) -> bool { + haystack.windows(needle.len()).any(|window| window == needle) +} + +fn find_dynamic_symbol<'file, 'data>( + file: &'file File<'data>, + expected: &str, +) -> Vec> { + file.exports() + .unwrap() + .into_iter() + .filter(|e| find_subsequence(e.name(), expected.as_bytes())) + .filter_map(|e| file.symbol_by_name_bytes(e.name())) + .collect() +} diff --git a/tests/run-make/no-duplicate-libs/main.rs b/tests/run-make/no-duplicate-libs/main.rs index b25ef35ada68..d8d5d58bc477 100644 --- a/tests/run-make/no-duplicate-libs/main.rs +++ b/tests/run-make/no-duplicate-libs/main.rs @@ -1,6 +1,6 @@ -#[link(name = "foo")] // linker should drop this library, no symbols used -#[link(name = "bar")] // symbol comes from this library -#[link(name = "foo")] // now linker picks up `foo` b/c `bar` library needs it +#[link(name = "foo", kind = "static")] // linker should drop this library, no symbols used +#[link(name = "bar", kind = "static")] // symbol comes from this library +#[link(name = "foo", kind = "static")] // now linker picks up `foo` b/c `bar` library needs it extern "C" { fn bar(); } diff --git a/tests/run-make/no-duplicate-libs/rmake.rs b/tests/run-make/no-duplicate-libs/rmake.rs index 469348e266cb..b67067909b24 100644 --- a/tests/run-make/no-duplicate-libs/rmake.rs +++ b/tests/run-make/no-duplicate-libs/rmake.rs @@ -9,9 +9,6 @@ //@ ignore-cross-compile // Reason: the compiled binary is executed -//@ ignore-msvc -// Reason: native compilation results in an unresolved external symbol - use run_make_support::{build_native_static_lib, run, rustc}; fn main() { diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/Makefile b/tests/run-make/pdb-buildinfo-cl-cmd/Makefile deleted file mode 100644 index a7be301a5b0d..000000000000 --- a/tests/run-make/pdb-buildinfo-cl-cmd/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -include ../tools.mk - -# only-windows-msvc - -# tests if the pdb contains the following information in the LF_BUILDINFO: -# 1. the commandline args to compile it (cmd) -# 2. full path to the compiler (cl) - -# we just do a stringsearch on the pdb, as these need to show up at least once, as the LF_BUILDINFO is created for each cgu -# actual parsing would be better, but this is a simple and good enough solution for now - -all: - $(RUSTC_ORIGINAL) main.rs -g --crate-name my_crate_name --crate-type bin -C metadata=dc9ef878b0a48666 --out-dir $(TMPDIR) - cat '$(TMPDIR)/my_crate_name.pdb' | grep -F '$(RUSTC_ORIGINAL)' -# using a file containing the string so I don't have problems with escaping quotes and spaces - cat '$(TMPDIR)/my_crate_name.pdb' | grep -f 'stringlist.txt' diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs b/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs new file mode 100644 index 000000000000..2ab9057b24c1 --- /dev/null +++ b/tests/run-make/pdb-buildinfo-cl-cmd/rmake.rs @@ -0,0 +1,39 @@ +// Check if the pdb file contains the following information in the LF_BUILDINFO: +// 1. full path to the compiler (cl) +// 2. the commandline args to compile it (cmd) +// This is because these used to be missing in #96475. +// See https://github.com/rust-lang/rust/pull/113492 + +//@ only-windows-msvc +// Reason: pdb files are unique to this architecture + +use run_make_support::{assert_contains, bstr, env_var, rfs, rustc}; + +fn main() { + rustc() + .input("main.rs") + .arg("-g") + .crate_name("my_crate_name") + .crate_type("bin") + .metadata("dc9ef878b0a48666") + .run(); + let tests = [ + &env_var("RUSTC"), + r#""main.rs""#, + r#""-g""#, + r#""--crate-name""#, + r#""my_crate_name""#, + r#""--crate-type""#, + r#""bin""#, + r#""-Cmetadata=dc9ef878b0a48666""#, + ]; + for test in tests { + assert_pdb_contains(test); + } +} + +fn assert_pdb_contains(needle: &str) { + let needle = needle.as_bytes(); + use bstr::ByteSlice; + assert!(&rfs::read("my_crate_name.pdb").find(needle).is_some()); +} diff --git a/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt b/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt deleted file mode 100644 index 634e9f19e897..000000000000 --- a/tests/run-make/pdb-buildinfo-cl-cmd/stringlist.txt +++ /dev/null @@ -1 +0,0 @@ -"main.rs" "-g" "--crate-name" "my_crate_name" "--crate-type" "bin" "-C" "metadata=dc9ef878b0a48666" "--out-dir" \ No newline at end of file diff --git a/tests/run-make/pgo-gen-lto/Makefile b/tests/run-make/pgo-gen-lto/Makefile deleted file mode 100644 index 54164c995222..000000000000 --- a/tests/run-make/pgo-gen-lto/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# needs-profiler-support -# ignore-cross-compile - -include ../tools.mk - -COMPILE_FLAGS=-Copt-level=3 -Clto=fat -Cprofile-generate="$(TMPDIR)" - -all: - $(RUSTC) $(COMPILE_FLAGS) test.rs - $(call RUN,test) || exit 1 - [ -e "$(TMPDIR)"/default_*.profraw ] || (echo "No .profraw file"; exit 1) diff --git a/tests/run-make/pgo-gen-lto/rmake.rs b/tests/run-make/pgo-gen-lto/rmake.rs new file mode 100644 index 000000000000..53d1623bf580 --- /dev/null +++ b/tests/run-make/pgo-gen-lto/rmake.rs @@ -0,0 +1,22 @@ +// A simple smoke test: when rustc compiles with profiling enabled, a profraw file +// should be generated. +// See https://github.com/rust-lang/rust/pull/48346 + +//@ needs-profiler-support +// Reason: this exercises LTO profiling +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{cwd, has_extension, has_prefix, run, rustc, shallow_find_files}; + +fn main() { + rustc().opt_level("3").arg("-Clto=fat").profile_generate(cwd()).input("test.rs").run(); + run("test"); + assert_eq!( + shallow_find_files(cwd(), |path| { + has_prefix(path, "default_") && has_extension(path, "profraw") + }) + .len(), + 1 + ); +} diff --git a/tests/run-make/pgo-indirect-call-promotion/Makefile b/tests/run-make/pgo-indirect-call-promotion/Makefile deleted file mode 100644 index 8d1e69c4aba3..000000000000 --- a/tests/run-make/pgo-indirect-call-promotion/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# needs-profiler-support -# ignore-cross-compile - -include ../tools.mk - -all: - # We don't compile `opaque` with either optimizations or instrumentation. - # We don't compile `opaque` with either optimizations or instrumentation. - $(RUSTC) $(COMMON_FLAGS) opaque.rs - # Compile the test program with instrumentation - mkdir -p "$(TMPDIR)"/prof_data_dir - $(RUSTC) $(COMMON_FLAGS) interesting.rs \ - -Cprofile-generate="$(TMPDIR)"/prof_data_dir -O -Ccodegen-units=1 - $(RUSTC) $(COMMON_FLAGS) main.rs -Cprofile-generate="$(TMPDIR)"/prof_data_dir -O - # The argument below generates to the expected branch weights - $(call RUN,main) || exit 1 - "$(LLVM_BIN_DIR)"/llvm-profdata merge \ - -o "$(TMPDIR)"/prof_data_dir/merged.profdata \ - "$(TMPDIR)"/prof_data_dir - $(RUSTC) $(COMMON_FLAGS) interesting.rs \ - -Cprofile-use="$(TMPDIR)"/prof_data_dir/merged.profdata -O \ - -Ccodegen-units=1 --emit=llvm-ir - cat "$(TMPDIR)"/interesting.ll | "$(LLVM_FILECHECK)" filecheck-patterns.txt diff --git a/tests/run-make/pgo-indirect-call-promotion/rmake.rs b/tests/run-make/pgo-indirect-call-promotion/rmake.rs new file mode 100644 index 000000000000..d0ccfd8a4d7e --- /dev/null +++ b/tests/run-make/pgo-indirect-call-promotion/rmake.rs @@ -0,0 +1,33 @@ +// This test checks that indirect call promotion is performed. The test +// programs calls the same function a thousand times through a function pointer. +// Only PGO data provides the information that it actually always is the same +// function. We verify that the indirect call promotion pass inserts a check +// whether it can make a direct call instead of the indirect call. +// See https://github.com/rust-lang/rust/pull/66631 + +//@ needs-profiler-support +// Reason: llvm_profdata is used +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{llvm_filecheck, llvm_profdata, rfs, run, rustc}; + +fn main() { + // We don't compile `opaque` with either optimizations or instrumentation. + rustc().input("opaque.rs").run(); + // Compile the test program with instrumentation + rfs::create_dir("prof_data_dir"); + rustc().input("interesting.rs").profile_generate("prof_data_dir").opt().codegen_units(1).run(); + rustc().input("main.rs").profile_generate("prof_data_dir").opt().run(); + // The argument below generates to the expected branch weights + run("main"); + llvm_profdata().merge().output("prof_data_dir/merged.profdata").input("prof_data_dir").run(); + rustc() + .input("interesting.rs") + .profile_use("prof_data_dir/merged.profdata") + .opt() + .codegen_units(1) + .emit("llvm-ir") + .run(); + llvm_filecheck().patterns("filecheck-patterns.txt").stdin(rfs::read("interesting.ll")).run(); +} diff --git a/tests/run-make/raw-dylib-alt-calling-convention/Makefile b/tests/run-make/raw-dylib-alt-calling-convention/Makefile deleted file mode 100644 index 14d23a5d2010..000000000000 --- a/tests/run-make/raw-dylib-alt-calling-convention/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# Test the behavior of #[link(.., kind = "raw-dylib")] with alternative calling conventions. - -# only-x86 -# only-windows - -include ../tools.mk - -all: - $(RUSTC) --crate-type lib --crate-name raw_dylib_alt_calling_convention_test lib.rs - $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" - $(call COMPILE_OBJ,"$(TMPDIR)"/extern.obj,extern.c) -ifdef IS_MSVC - $(CC) "$(TMPDIR)"/extern.obj -link -dll -out:"$(TMPDIR)"/extern.dll -noimplib -else - $(CC) "$(TMPDIR)"/extern.obj -shared -o "$(TMPDIR)"/extern.dll -endif - - "$(TMPDIR)"/driver > "$(TMPDIR)"/output.txt - $(RUSTC_TEST_OP) "$(TMPDIR)"/output.txt output.txt - -ifdef IS_MSVC - "$(TMPDIR)"/driver true > "$(TMPDIR)"/output.msvc.txt - $(RUSTC_TEST_OP) "$(TMPDIR)"/output.msvc.txt output.msvc.txt -endif diff --git a/tests/run-make/raw-dylib-alt-calling-convention/rmake.rs b/tests/run-make/raw-dylib-alt-calling-convention/rmake.rs new file mode 100644 index 000000000000..1a1622f27542 --- /dev/null +++ b/tests/run-make/raw-dylib-alt-calling-convention/rmake.rs @@ -0,0 +1,32 @@ +// `raw-dylib` is a Windows-specific attribute which emits idata sections for the items in the +// attached extern block, +// so they may be linked against without linking against an import library. +// To learn more, read https://github.com/rust-lang/rfcs/blob/master/text/2627-raw-dylib-kind.md +// This test uses this feature alongside alternative calling conventions, checking that both +// features are compatible and result in the expected output upon execution of the binary. +// See https://github.com/rust-lang/rust/pull/84171 + +//@ only-x86 +//@ only-windows + +use run_make_support::{build_native_dynamic_lib, diff, is_msvc, run, run_with_args, rustc}; + +fn main() { + rustc() + .crate_type("lib") + .crate_name("raw_dylib_alt_calling_convention_test") + .input("lib.rs") + .run(); + rustc().crate_type("bin").input("driver.rs").run(); + build_native_dynamic_lib("extern"); + let out = run("driver").stdout_utf8(); + diff().expected_file("output.txt").actual_text("actual", out).normalize(r#"\r"#, "").run(); + if is_msvc() { + let out_msvc = run_with_args("driver", &["true"]).stdout_utf8(); + diff() + .expected_file("output.msvc.txt") + .actual_text("actual", out_msvc) + .normalize(r#"\r"#, "") + .run(); + } +} diff --git a/tests/run-make/raw-dylib-c/Makefile b/tests/run-make/raw-dylib-c/Makefile deleted file mode 100644 index af5c4a6edd7b..000000000000 --- a/tests/run-make/raw-dylib-c/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# Test the behavior of #[link(.., kind = "raw-dylib")] on windows-msvc - -# only-windows - -include ../tools.mk - -all: - $(RUSTC) --crate-type lib --crate-name raw_dylib_test lib.rs - $(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" - $(RUSTC) --crate-type bin --crate-name raw_dylib_test_bin lib.rs - $(call COMPILE_OBJ,"$(TMPDIR)"/extern_1.obj,extern_1.c) - $(call COMPILE_OBJ,"$(TMPDIR)"/extern_2.obj,extern_2.c) -ifdef IS_MSVC - $(CC) "$(TMPDIR)"/extern_1.obj -link -dll -out:"$(TMPDIR)"/extern_1.dll -noimplib - $(CC) "$(TMPDIR)"/extern_2.obj -link -dll -out:"$(TMPDIR)"/extern_2.dll -noimplib -else - $(CC) "$(TMPDIR)"/extern_1.obj -shared -o "$(TMPDIR)"/extern_1.dll - $(CC) "$(TMPDIR)"/extern_2.obj -shared -o "$(TMPDIR)"/extern_2.dll -endif - "$(TMPDIR)"/driver | tr -d '\r' > "$(TMPDIR)"/output.txt - "$(TMPDIR)"/raw_dylib_test_bin > "$(TMPDIR)"/output_bin.txt - -ifdef RUSTC_BLESS_TEST - cp "$(TMPDIR)"/output.txt output.txt -else - $(DIFF) output.txt "$(TMPDIR)"/output.txt - $(DIFF) output.txt "$(TMPDIR)"/output_bin.txt -endif diff --git a/tests/run-make/raw-dylib-c/rmake.rs b/tests/run-make/raw-dylib-c/rmake.rs new file mode 100644 index 000000000000..3cfd8cb400bb --- /dev/null +++ b/tests/run-make/raw-dylib-c/rmake.rs @@ -0,0 +1,29 @@ +// `raw-dylib` is a Windows-specific attribute which emits idata sections for the items in the +// attached extern block, +// so they may be linked against without linking against an import library. +// To learn more, read https://github.com/rust-lang/rfcs/blob/master/text/2627-raw-dylib-kind.md +// This test is the simplest of the raw-dylib tests, simply smoke-testing that the feature +// can be used to build an executable binary with an expected output with native C files +// compiling into dynamic libraries. +// See https://github.com/rust-lang/rust/pull/86419 + +//@ only-windows + +use run_make_support::{build_native_dynamic_lib, diff, run, rustc}; + +fn main() { + rustc().crate_type("lib").crate_name("raw_dylib_test").input("lib.rs").run(); + rustc().crate_type("bin").input("driver.rs").run(); + rustc().crate_type("bin").crate_name("raw_dylib_test_bin").input("lib.rs").run(); + build_native_dynamic_lib("extern_1"); + build_native_dynamic_lib("extern_2"); + let out_driver = run("driver").stdout_utf8(); + let out_raw = run("raw_dylib_test_bin").stdout_utf8(); + + diff() + .expected_file("output.txt") + .actual_text("actual", out_driver) + .normalize(r#"\r"#, "") + .run(); + diff().expected_file("output.txt").actual_text("actual", out_raw).normalize(r#"\r"#, "").run(); +} diff --git a/tests/run-make/redundant-libs/Makefile b/tests/run-make/redundant-libs/Makefile deleted file mode 100644 index 0a48b2b28013..000000000000 --- a/tests/run-make/redundant-libs/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# ignore-windows-msvc - -# rustc will remove one of the two redundant references to foo below. Depending -# on which one gets removed, we'll get a linker error on SOME platforms (like -# Linux). On these platforms, when a library is referenced, the linker will -# only pull in the symbols needed _at that point in time_. If a later library -# depends on additional symbols from the library, they will not have been pulled -# in, and you'll get undefined symbols errors. -# -# So in this example, we need to ensure that rustc keeps the _later_ reference -# to foo, and not the former one. -RUSTC_FLAGS = \ - -l static=bar \ - -l foo \ - -l static=baz \ - -l foo \ - --print link-args - -all: $(call DYLIB,foo) $(call STATICLIB,bar) $(call STATICLIB,baz) - $(RUSTC) $(RUSTC_FLAGS) main.rs - $(call RUN,main) diff --git a/tests/run-make/redundant-libs/foo.c b/tests/run-make/redundant-libs/foo.c index 339ee86c99ea..551b85d18245 100644 --- a/tests/run-make/redundant-libs/foo.c +++ b/tests/run-make/redundant-libs/foo.c @@ -1,2 +1,8 @@ -void foo1() {} -void foo2() {} +#ifdef _MSC_VER +#define DllExport __declspec(dllexport) +#else +#define DllExport +#endif + +DllExport void foo1() {} +DllExport void foo2() {} diff --git a/tests/run-make/redundant-libs/rmake.rs b/tests/run-make/redundant-libs/rmake.rs new file mode 100644 index 000000000000..43bb30bde702 --- /dev/null +++ b/tests/run-make/redundant-libs/rmake.rs @@ -0,0 +1,28 @@ +// rustc will remove one of the two redundant references to foo below. Depending +// on which one gets removed, we'll get a linker error on SOME platforms (like +// Linux). On these platforms, when a library is referenced, the linker will +// only pull in the symbols needed _at that point in time_. If a later library +// depends on additional symbols from the library, they will not have been pulled +// in, and you'll get undefined symbols errors. +// +// So in this example, we need to ensure that rustc keeps the _later_ reference +// to foo, and not the former one. + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{ + build_native_dynamic_lib, build_native_static_lib, cwd, is_msvc, rfs, run, rustc, +}; + +fn main() { + build_native_dynamic_lib("foo"); + build_native_static_lib("bar"); + build_native_static_lib("baz"); + rustc() + .args(&["-lstatic=bar", "-lfoo", "-lstatic=baz", "-lfoo"]) + .input("main.rs") + .print("link-args") + .run(); + run("main"); +} diff --git a/tests/run-make/rust-lld-compress-debug-sections/main.rs b/tests/run-make/rust-lld-compress-debug-sections/main.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/tests/run-make/rust-lld-compress-debug-sections/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/rust-lld-compress-debug-sections/rmake.rs b/tests/run-make/rust-lld-compress-debug-sections/rmake.rs new file mode 100644 index 000000000000..df9691ccbcf3 --- /dev/null +++ b/tests/run-make/rust-lld-compress-debug-sections/rmake.rs @@ -0,0 +1,39 @@ +// Checks the `compress-debug-sections` option on rust-lld. + +//@ needs-rust-lld +//@ only-linux +//@ ignore-cross-compile + +// FIXME: This test isn't comprehensive and isn't covering all possible combinations. + +use run_make_support::{assert_contains, cmd, llvm_readobj, run_in_tmpdir, rustc}; + +fn check_compression(compression: &str, to_find: &str) { + run_in_tmpdir(|| { + let out = rustc() + .arg("-Zlinker-features=+lld") + .arg("-Clink-self-contained=+linker") + .arg("-Zunstable-options") + .arg("-Cdebuginfo=full") + .link_arg(&format!("-Wl,--compress-debug-sections={compression}")) + .input("main.rs") + .run_unchecked(); + let stderr = out.stderr_utf8(); + if stderr.is_empty() { + llvm_readobj().arg("-t").arg("main").run().assert_stdout_contains(to_find); + } else { + assert_contains( + stderr, + format!( + "LLVM was not built with LLVM_ENABLE_{to_find} \ + or did not find {compression} at build time" + ), + ); + } + }); +} + +fn main() { + check_compression("zlib", "ZLIB"); + check_compression("zstd", "ZSTD"); +} diff --git a/tests/run-make/rust-lld/rmake.rs b/tests/run-make/rust-lld/rmake.rs index 87477c122301..d0bc19130d7c 100644 --- a/tests/run-make/rust-lld/rmake.rs +++ b/tests/run-make/rust-lld/rmake.rs @@ -2,15 +2,17 @@ // see https://github.com/rust-lang/compiler-team/issues/510 for more info //@ needs-rust-lld -//@ ignore-msvc //@ ignore-s390x lld does not yet support s390x as target use std::process::Output; use run_make_support::regex::Regex; -use run_make_support::rustc; +use run_make_support::{is_msvc, rustc}; fn main() { + // lld-link is used if msvc, otherwise a gnu-compatible lld is used. + let linker_version_flag = if is_msvc() { "--version" } else { "-Wl,-v" }; + // Opt-in to lld and the self-contained linker, to link with rust-lld. We'll check that by // asking the linker to display its version number with a link-arg. let output = rustc() @@ -18,7 +20,7 @@ fn main() { .arg("-Zlinker-features=+lld") .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") - .link_arg("-Wl,-v") + .link_arg(linker_version_flag) .input("main.rs") .run(); assert!( @@ -27,10 +29,10 @@ fn main() { output.stderr_utf8() ); - // It should not be used when we explictly opt-out of lld. + // It should not be used when we explicitly opt-out of lld. let output = rustc() .env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info") - .link_arg("-Wl,-v") + .link_arg(linker_version_flag) .arg("-Zlinker-features=-lld") .input("main.rs") .run(); @@ -44,7 +46,7 @@ fn main() { // times to rustc. let output = rustc() .env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info") - .link_arg("-Wl,-v") + .link_arg(linker_version_flag) .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") .arg("-Zlinker-features=-lld") diff --git a/tests/run-make/simd-ffi/Makefile b/tests/run-make/simd-ffi/Makefile deleted file mode 100644 index 297353470411..000000000000 --- a/tests/run-make/simd-ffi/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -include ../tools.mk - -TARGETS = -ifeq ($(filter arm,$(LLVM_COMPONENTS)),arm) -# construct a fairly exhaustive list of platforms that we -# support. These ones don't follow a pattern -TARGETS += arm-linux-androideabi arm-unknown-linux-gnueabihf arm-unknown-linux-gnueabi -endif - -ifeq ($(filter x86,$(LLVM_COMPONENTS)),x86) -X86_ARCHS = i686 x86_64 -else -X86_ARCHS = -endif - -# these ones do, each OS lists the architectures it supports -LINUX=$(filter aarch64 mips,$(LLVM_COMPONENTS)) $(X86_ARCHS) -ifeq ($(filter mips,$(LLVM_COMPONENTS)),mips) -LINUX += mipsel -endif - -WINDOWS=$(X86_ARCHS) -# fails with: failed to get iphonesimulator SDK path: no such file or directory -#IOS=i386 aarch64 armv7 -DARWIN=$(X86_ARCHS) - -$(foreach arch,$(LINUX),$(eval TARGETS += $(arch)-unknown-linux-gnu)) -$(foreach arch,$(WINDOWS),$(eval TARGETS += $(arch)-pc-windows-gnu)) -#$(foreach arch,$(IOS),$(eval TARGETS += $(arch)-apple-ios)) -$(foreach arch,$(DARWIN),$(eval TARGETS += $(arch)-apple-darwin)) - -all: $(TARGETS) - -define MK_TARGETS -# compile the rust file to the given target, but only to asm and IR -# form, to avoid having to have an appropriate linker. -# -# we need some features because the integer SIMD instructions are not -# enabled by-default for i686 and ARM; these features will be invalid -# on some platforms, but LLVM just prints a warning so that's fine for -# now. -$(1): simd.rs - $$(RUSTC) --target=$(1) --emit=llvm-ir,asm simd.rs \ - -C target-feature='+neon,+sse2' -C extra-filename=-$(1) -endef - -$(foreach targetxxx,$(TARGETS),$(eval $(call MK_TARGETS,$(targetxxx)))) diff --git a/tests/run-make/simd-ffi/rmake.rs b/tests/run-make/simd-ffi/rmake.rs new file mode 100644 index 000000000000..04990c8bdf43 --- /dev/null +++ b/tests/run-make/simd-ffi/rmake.rs @@ -0,0 +1,63 @@ +// Using SIMD types in a program with foreign-function interfaces used to result in an ICE +// (internal compiler error). Since this was fixed in #21233, it should be checked that +// compilation of SIMD and FFI together should be successful on all the most common +// architectures. +// Note that this test does not check linking or binary execution. +// See https://github.com/rust-lang/rust/pull/21233 + +use run_make_support::{llvm_components_contain, rustc}; + +fn main() { + let mut targets = Vec::new(); + // arm-specific targets. + if llvm_components_contain("arm") { + targets.append(&mut vec![ + "arm-linux-androideabi".to_owned(), + "arm-unknown-linux-gnueabihf".to_owned(), + "arm-unknown-linux-gnueabi".to_owned(), + ]); + } + let mut x86_archs = Vec::new(); + if llvm_components_contain("x86") { + x86_archs.append(&mut vec!["i686", "x86_64"]); + } + // Linux has all x86 targets, plus aarch64 and mips. + let mut extra_targets = x86_archs.clone(); + if llvm_components_contain("aarch64") { + extra_targets.push("aarch64"); + } + if llvm_components_contain("mips") { + extra_targets.append(&mut vec!["mips", "mipsel"]); + } + + for target in extra_targets { + let linux = format!("{target}-unknown-linux-gnu"); + targets.push(linux); + } + + // Windows and Darwin (OSX) only receive x86 targets. + let extra_targets = x86_archs.clone(); + for target in extra_targets { + let windows = format!("{target}-pc-windows-gnu"); + let darwin = format!("{target}-apple-darwin"); + targets.push(windows); + targets.push(darwin); + } + + for target in targets { + // compile the rust file to the given target, but only to asm and IR + // form, to avoid having to have an appropriate linker. + // + // we need some features because the integer SIMD instructions are not + // enabled by-default for i686 and ARM; these features will be invalid + // on some platforms, but LLVM just prints a warning so that's fine for + // now. + rustc() + .target(&target) + .emit("llvm-ir,asm") + .input("simd.rs") + .arg("-Ctarget-feature=+neon,+sse") + .arg(&format!("-Cextra-filename=-{target}")) + .run(); + } +} diff --git a/tests/run-make/staticlib-dylib-linkage/Makefile b/tests/run-make/staticlib-dylib-linkage/Makefile deleted file mode 100644 index a1e86a7ce4b6..000000000000 --- a/tests/run-make/staticlib-dylib-linkage/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -include ../tools.mk - -# ignore-cross-compile -# ignore-msvc FIXME(bjorn3) can't figure out how to link with the MSVC toolchain -# ignore-wasm wasm doesn't support dynamic libraries - -all: - $(RUSTC) -C prefer-dynamic bar.rs - $(RUSTC) foo.rs --crate-type staticlib --print native-static-libs \ - -Z staticlib-allow-rdylib-deps 2>&1 | grep 'note: native-static-libs: ' \ - | sed 's/note: native-static-libs: \(.*\)/\1/' > $(TMPDIR)/libs.txt - cat $(TMPDIR)/libs.txt - -ifdef IS_MSVC - $(CC) $(CFLAGS) /c foo.c /Fo:$(TMPDIR)/foo.o - $(RUSTC_LINKER) $(TMPDIR)/foo.o $(TMPDIR)/foo.lib $$(cat $(TMPDIR)/libs.txt) $(call OUT_EXE,foo) -else - $(CC) $(CFLAGS) foo.c -L $(TMPDIR) -lfoo $$(cat $(TMPDIR)/libs.txt) -o $(call RUN_BINFILE,foo) -endif - - $(call RUN,foo) diff --git a/tests/run-make/staticlib-dylib-linkage/rmake.rs b/tests/run-make/staticlib-dylib-linkage/rmake.rs new file mode 100644 index 000000000000..8dd1ac0ffbdc --- /dev/null +++ b/tests/run-make/staticlib-dylib-linkage/rmake.rs @@ -0,0 +1,36 @@ +// A basic smoke test to check that rustc supports linking to a rust dylib with +// --crate-type staticlib. bar is a dylib, on which foo is dependent - the native +// static lib search paths are collected and used to compile foo.c, the final executable +// which depends on both foo and bar. +// See https://github.com/rust-lang/rust/pull/106560 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed. +//@ ignore-wasm +// Reason: WASM does not support dynamic libraries + +use run_make_support::{cc, is_msvc, regex, run, rustc, static_lib_name}; + +fn main() { + rustc().arg("-Cprefer-dynamic").input("bar.rs").run(); + let libs = rustc() + .input("foo.rs") + .crate_type("staticlib") + .print("native-static-libs") + .arg("-Zstaticlib-allow-rdylib-deps") + .run() + .assert_stderr_contains("note: native-static-libs: ") + .stderr_utf8(); + let re = regex::Regex::new(r#"note: native-static-libs:\s*(.+)"#).unwrap(); + let libs = re.find(&libs).unwrap().as_str().trim(); + // remove the note + let (_, native_link_args) = libs.split_once("note: native-static-libs: ").unwrap(); + // divide the command-line arguments in a vec + let mut native_link_args = native_link_args.split(' ').collect::>(); + if is_msvc() { + // For MSVC pass the arguments on to the linker. + native_link_args.insert(0, "-link"); + } + cc().input("foo.c").input(static_lib_name("foo")).args(native_link_args).out_exe("foo").run(); + run("foo"); +} diff --git a/tests/run-make/thumb-none-cortex-m/Makefile b/tests/run-make/thumb-none-cortex-m/Makefile deleted file mode 100644 index e941fc4a78e1..000000000000 --- a/tests/run-make/thumb-none-cortex-m/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -include ../tools.mk - -# How to run this -# $ ./x.py clean -# $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make - -# Supported targets: -# - thumbv6m-none-eabi (Bare Cortex-M0, M0+, M1) -# - thumbv7em-none-eabi (Bare Cortex-M4, M7) -# - thumbv7em-none-eabihf (Bare Cortex-M4F, M7F, FPU, hardfloat) -# - thumbv7m-none-eabi (Bare Cortex-M3) - -# only-thumb - -# For cargo setting -RUSTC := $(RUSTC_ORIGINAL) -LD_LIBRARY_PATH := $(HOST_RPATH_DIR) -# We need to be outside of 'src' dir in order to run cargo -WORK_DIR := $(TMPDIR) - -HERE := $(shell pwd) - -CRATE := cortex-m -CRATE_URL := https://github.com/rust-embedded/cortex-m -CRATE_SHA1 := a448e9156e2cb1e556e5441fd65426952ef4b927 # 0.5.0 - -# Don't make lints fatal, but they need to at least warn or they break Cargo's target info parsing. -export RUSTFLAGS := --cap-lints=warn - -all: - env - mkdir -p $(WORK_DIR) - -cd $(WORK_DIR) && rm -rf $(CRATE) - cd $(WORK_DIR) && bash -x $(HERE)/../git_clone_sha1.sh $(CRATE) $(CRATE_URL) $(CRATE_SHA1) - # HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features. - # These come from the top-level Rust workspace, that this crate is not a - # member of, but Cargo tries to load the workspace `Cargo.toml` anyway. - cd $(WORK_DIR) && cd $(CRATE) && env RUSTC_BOOTSTRAP=1 $(BOOTSTRAP_CARGO) build --target $(TARGET) -v diff --git a/tests/run-make/thumb-none-cortex-m/rmake.rs b/tests/run-make/thumb-none-cortex-m/rmake.rs new file mode 100644 index 000000000000..0ddb91d378fb --- /dev/null +++ b/tests/run-make/thumb-none-cortex-m/rmake.rs @@ -0,0 +1,59 @@ +//! Test building of the `cortex-m` crate, a foundational crate in the embedded ecosystem +//! for a collection of thumb targets. This is a smoke test that verifies that both cargo +//! and rustc work in this case. +//! +//! How to run this +//! $ ./x.py clean +//! $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make +//! +//! Supported targets: +//! - thumbv6m-none-eabi (Bare Cortex-M0, M0+, M1) +//! - thumbv7em-none-eabi (Bare Cortex-M4, M7) +//! - thumbv7em-none-eabihf (Bare Cortex-M4F, M7F, FPU, hardfloat) +//! - thumbv7m-none-eabi (Bare Cortex-M3) + +//@ only-thumb + +use std::path::PathBuf; + +use run_make_support::rfs::create_dir; +use run_make_support::{cmd, env_var, target}; + +const CRATE: &str = "cortex-m"; +const CRATE_URL: &str = "https://github.com/rust-embedded/cortex-m"; +const CRATE_SHA1: &str = "a448e9156e2cb1e556e5441fd65426952ef4b927"; // v0.5.0 + +fn main() { + // FIXME: requires an internet connection https://github.com/rust-lang/rust/issues/128733 + // See below link for git usage: + // https://stackoverflow.com/questions/3489173#14091182 + cmd("git").args(["clone", CRATE_URL, CRATE]).run(); + std::env::set_current_dir(CRATE).unwrap(); + cmd("git").args(["reset", "--hard", CRATE_SHA1]).run(); + + let target_dir = PathBuf::from("target"); + let manifest_path = PathBuf::from("Cargo.toml"); + + let path = env_var("PATH"); + let rustc = env_var("RUSTC"); + let bootstrap_cargo = env_var("BOOTSTRAP_CARGO"); + // FIXME: extract bootstrap cargo invocations to a proper command + // https://github.com/rust-lang/rust/issues/128734 + let mut cmd = cmd(bootstrap_cargo); + cmd.args(&[ + "build", + "--manifest-path", + manifest_path.to_str().unwrap(), + "-Zbuild-std=core", + "--target", + &target(), + ]) + .env("PATH", path) + .env("RUSTC", rustc) + .env("CARGO_TARGET_DIR", &target_dir) + // Don't make lints fatal, but they need to at least warn + // or they break Cargo's target info parsing. + .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes --cap-lints=warn"); + + cmd.run(); +} diff --git a/tests/run-make/thumb-none-qemu/Makefile b/tests/run-make/thumb-none-qemu/Makefile deleted file mode 100644 index eea6ca349928..000000000000 --- a/tests/run-make/thumb-none-qemu/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -include ../tools.mk - -# only-thumb - -# How to run this -# $ ./x.py clean -# $ ./x.py test --target thumbv7m-none-eabi tests/run-make - -# For cargo setting -export RUSTC := $(RUSTC_ORIGINAL) -export LD_LIBRARY_PATH := $(HOST_RPATH_DIR) -# We need to be outside of 'src' dir in order to run cargo -export WORK_DIR := $(TMPDIR) -export HERE := $(shell pwd) - -## clean up unused env variables which might cause harm. -unexport RUSTC_LINKER -unexport RUSTC_BOOTSTRAP -unexport RUST_BUILD_STAGE -unexport RUST_TEST_THREADS -unexport RUST_TEST_TMPDIR -unexport AR -unexport CC -unexport CXX - -all: - bash script.sh diff --git a/tests/run-make/thumb-none-qemu/example/.cargo/config b/tests/run-make/thumb-none-qemu/example/.cargo/config.toml similarity index 90% rename from tests/run-make/thumb-none-qemu/example/.cargo/config rename to tests/run-make/thumb-none-qemu/example/.cargo/config.toml index 8b30310e7d43..7152e81b5022 100644 --- a/tests/run-make/thumb-none-qemu/example/.cargo/config +++ b/tests/run-make/thumb-none-qemu/example/.cargo/config.toml @@ -1,6 +1,5 @@ [target.thumbv6m-none-eabi] -# FIXME: Should be Cortex-M0, but Qemu used by CI is too old -runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" +runner = "qemu-system-arm -cpu cortex-m0 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" [target.thumbv7m-none-eabi] runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" @@ -12,7 +11,7 @@ runner = "qemu-system-arm -cpu cortex-m4 -machine lm3s6965evb -nographic -semiho runner = "qemu-system-arm -cpu cortex-m4 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" [target.thumbv8m.base-none-eabi] -# FIXME: Should be the Cortex-M23, bt Qemu does not currently support it +# FIXME: Should be the Cortex-M23, but Qemu does not currently support it runner = "qemu-system-arm -cpu cortex-m33 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" [target.thumbv8m.main-none-eabi] diff --git a/tests/run-make/thumb-none-qemu/rmake.rs b/tests/run-make/thumb-none-qemu/rmake.rs new file mode 100644 index 000000000000..d0f42bc88089 --- /dev/null +++ b/tests/run-make/thumb-none-qemu/rmake.rs @@ -0,0 +1,62 @@ +//! This test runs a basic application for thumb targets, using the cortex-m crate. +//! +//! These targets are very bare-metal: the first instruction the core runs on +//! power-on is already user code. The cortex-m-rt has to initialize the stack, .data, +//! .bss, enable the FPU if present, etc. +//! +//! This test builds and runs the applications for various thumb targets using qemu. +//! +//! How to run this +//! $ ./x.py clean +//! $ ./x.py test --target thumbv6m-none-eabi,thumbv7m-none-eabi tests/run-make +//! +//! For supported targets, see `example/.cargo/config.toml` +//! +//! FIXME: https://github.com/rust-lang/rust/issues/128733 this test uses external +//! dependencies, and needs an active internet connection +//! +//! FIXME: https://github.com/rust-lang/rust/issues/128734 extract bootstrap cargo +//! to a proper command + +//@ only-thumb + +use std::path::PathBuf; + +use run_make_support::{cmd, env_var, path_helpers, target}; + +const CRATE: &str = "example"; + +fn main() { + std::env::set_current_dir(CRATE).unwrap(); + + let bootstrap_cargo = env_var("BOOTSTRAP_CARGO"); + let path = env_var("PATH"); + let rustc = env_var("RUSTC"); + + let target_dir = path_helpers::path("target"); + let manifest_path = path_helpers::path("Cargo.toml"); + + let debug = { + let mut cmd = cmd(&bootstrap_cargo); + cmd.args(&["run", "--target", &target()]) + .env("RUSTFLAGS", "-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x") + .env("CARGO_TARGET_DIR", &target_dir) + .env("PATH", &path) + .env("RUSTC", &rustc); + cmd.run() + }; + + debug.assert_stdout_contains("x = 42"); + + let release = { + let mut cmd = cmd(&bootstrap_cargo); + cmd.args(&["run", "--release", "--target", &target()]) + .env("RUSTFLAGS", "-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x") + .env("CARGO_TARGET_DIR", &target_dir) + .env("PATH", &path) + .env("RUSTC", &rustc); + cmd.run() + }; + + release.assert_stdout_contains("x = 42"); +} diff --git a/tests/run-make/thumb-none-qemu/script.sh b/tests/run-make/thumb-none-qemu/script.sh deleted file mode 100644 index a8aa72af1845..000000000000 --- a/tests/run-make/thumb-none-qemu/script.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -set -exuo pipefail - -CRATE=example - -env | sort -mkdir -p $WORK_DIR -pushd $WORK_DIR - rm -rf $CRATE || echo OK - cp -a $HERE/example . - pushd $CRATE - # HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features. - # These come from the top-level Rust workspace, that this crate is not a - # member of, but Cargo tries to load the workspace `Cargo.toml` anyway. - env RUSTC_BOOTSTRAP=1 RUSTFLAGS="-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x" \ - $BOOTSTRAP_CARGO run --target $TARGET | grep "x = 42" - env RUSTC_BOOTSTRAP=1 RUSTFLAGS="-C linker=arm-none-eabi-ld -C link-arg=-Tlink.x" \ - $BOOTSTRAP_CARGO run --target $TARGET --release | grep "x = 42" - popd -popd diff --git a/tests/run-make/zero-extend-abi-param-passing/param_passing.rs b/tests/run-make/zero-extend-abi-param-passing/param_passing.rs index c11f3cc72bdf..addde6b8ee36 100644 --- a/tests/run-make/zero-extend-abi-param-passing/param_passing.rs +++ b/tests/run-make/zero-extend-abi-param-passing/param_passing.rs @@ -2,7 +2,7 @@ // LLVM optimization choices. See additional note below for an // example. -#[link(name = "bad")] +#[link(name = "bad", kind = "static")] extern "C" { pub fn c_read_value(a: u32, b: u32, c: u32) -> u16; } diff --git a/tests/run-make/zero-extend-abi-param-passing/rmake.rs b/tests/run-make/zero-extend-abi-param-passing/rmake.rs index aed27f7f5ab8..96dbbd0627c2 100644 --- a/tests/run-make/zero-extend-abi-param-passing/rmake.rs +++ b/tests/run-make/zero-extend-abi-param-passing/rmake.rs @@ -6,20 +6,13 @@ // while simultaneously interfacing with a C library and using the -O3 flag. // See https://github.com/rust-lang/rust/issues/97463 -//@ ignore-msvc -// Reason: the rustc compilation fails due to an unresolved external symbol - //@ ignore-cross-compile // Reason: The compiled binary is executed. - -use run_make_support::{cc, is_msvc, llvm_ar, run, rustc, static_lib_name}; +use run_make_support::{build_native_static_lib_optimized, run, rustc}; fn main() { - // The issue exercised by this test specifically needs needs `-O` - // flags (like `-O3`) to reproduce. Thus, we call `cc()` instead of - // the nicer `build_native_static_lib`. - cc().arg("-c").arg("-O3").out_exe("bad.o").input("bad.c").run(); - llvm_ar().obj_to_ar().output_input(static_lib_name("bad"), "bad.o").run(); - rustc().input("param_passing.rs").arg("-lbad").opt_level("3").run(); + // The issue exercised by this test specifically needs an optimized native static lib. + build_native_static_lib_optimized("bad"); + rustc().input("param_passing.rs").opt_level("3").run(); run("param_passing"); } diff --git a/tests/rustdoc-json/impls/pub_for_hidden_private.rs b/tests/rustdoc-json/impls/pub_for_hidden_private.rs new file mode 100644 index 000000000000..261ffbfeb4a4 --- /dev/null +++ b/tests/rustdoc-json/impls/pub_for_hidden_private.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --document-private-items --document-hidden-items + +pub trait TheTrait {} + +#[doc(hidden)] +struct Value {} + +//@ has '$.index[*][?(@.docs=="THE IMPL")]' +/// THE IMPL +impl TheTrait for Value {} diff --git a/tests/rustdoc-json/pub_mod_in_private_mod.rs b/tests/rustdoc-json/pub_mod_in_private_mod.rs new file mode 100644 index 000000000000..112ab9c68f00 --- /dev/null +++ b/tests/rustdoc-json/pub_mod_in_private_mod.rs @@ -0,0 +1,6 @@ +// See https://github.com/rust-lang/rust/issues/101105 + +//@ !has "$.index[*][?(@.name=='nucleus')]" +mod corpus { + pub mod nucleus {} +} diff --git a/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.rs b/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.rs new file mode 100644 index 000000000000..571bc94e30f9 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.rs @@ -0,0 +1,12 @@ +//@ compile-flags:--test +//@ check-pass +#![allow(rustdoc::invalid_codeblock_attributes)] + +// https://github.com/rust-lang/rust/pull/124577#issuecomment-2276034737 + +// Test that invalid langstrings don't get run. + +/// ```{rust,ignore} +/// panic!(); +/// ``` +pub struct Foo; diff --git a/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.stdout b/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.stdout new file mode 100644 index 000000000000..e5c27bebbdb2 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-no-run-invalid-langstring-124577.stdout @@ -0,0 +1,5 @@ + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s + diff --git a/tests/rustdoc-ui/intra-doc/.gitattributes b/tests/rustdoc-ui/intra-doc/.gitattributes index 6c125fac52f5..0ac2e3c27f43 100644 --- a/tests/rustdoc-ui/intra-doc/.gitattributes +++ b/tests/rustdoc-ui/intra-doc/.gitattributes @@ -1 +1 @@ -warning-crlf.rs eol=crlf +warning-crlf.rs -text diff --git a/tests/rustdoc-ui/intra-doc/warning-crlf.rs b/tests/rustdoc-ui/intra-doc/warning-crlf.rs index ab096b860f81..9d3a4673c9d0 100644 --- a/tests/rustdoc-ui/intra-doc/warning-crlf.rs +++ b/tests/rustdoc-ui/intra-doc/warning-crlf.rs @@ -1,26 +1,26 @@ -// ignore-tidy-cr -//@ check-pass - -// This file checks the spans of intra-link warnings in a file with CRLF line endings. The -// .gitattributes file in this directory should enforce it. - -/// [error] -pub struct A; -//~^^ WARNING `error` - -/// -/// docs [error1] -//~^ WARNING `error1` - -/// docs [error2] -/// -pub struct B; -//~^^^ WARNING `error2` - -/** - * This is a multi-line comment. - * - * It also has an [error]. - */ -pub struct C; -//~^^^ WARNING `error` +// ignore-tidy-cr +//@ check-pass + +// This file checks the spans of intra-link warnings in a file with CRLF line endings. The +// .gitattributes file in this directory should enforce it. + +/// [error] +pub struct A; +//~^^ WARNING `error` + +/// +/// docs [error1] +//~^ WARNING `error1` + +/// docs [error2] +/// +pub struct B; +//~^^^ WARNING `error2` + +/** + * This is a multi-line comment. + * + * It also has an [error]. + */ +pub struct C; +//~^^^ WARNING `error` diff --git a/tests/rustdoc/negative-impl-no-items.rs b/tests/rustdoc/negative-impl-no-items.rs new file mode 100644 index 000000000000..c628e5420336 --- /dev/null +++ b/tests/rustdoc/negative-impl-no-items.rs @@ -0,0 +1,26 @@ +// This test ensures that negative impls don't have items listed inside them. + +#![feature(negative_impls)] +#![crate_name = "foo"] + +pub struct Thing; + +//@ has 'foo/struct.Thing.html' +// We check the full path to ensure there is no `
` element. +//@ has - '//div[@id="trait-implementations-list"]/section[@id="impl-Iterator-for-Thing"]/h3' \ +// 'impl !Iterator for Thing' +impl !Iterator for Thing {} + +// This struct will allow us to compare both paths. +pub struct Witness; + +//@ has 'foo/struct.Witness.html' +//@ has - '//div[@id="trait-implementations-list"]/details//section[@id="impl-Iterator-for-Witness"]/h3' \ +// 'impl Iterator for Witness' +impl Iterator for Witness { + type Item = u8; + + fn next(&mut self) -> Option { + None + } +} diff --git a/tests/ui/async-await/async-closures/box-deref-in-debuginfo.rs b/tests/ui/async-await/async-closures/box-deref-in-debuginfo.rs new file mode 100644 index 000000000000..8b2de578b249 --- /dev/null +++ b/tests/ui/async-await/async-closures/box-deref-in-debuginfo.rs @@ -0,0 +1,35 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ run-pass + +#![feature(async_closure)] + +extern crate block_on; + +pub trait Trait { + fn callback(&mut self); +} +impl Trait for (i32,) { + fn callback(&mut self) { + println!("hi {}", self.0); + self.0 += 1; + } +} + +async fn call_once(f: impl async FnOnce()) { + f().await; +} + +async fn run(mut loader: Box) { + let f = async move || { + loader.callback(); + loader.callback(); + }; + call_once(f).await; +} + +fn main() { + block_on::block_on(async { + run(Box::new((42,))).await; + }); +} diff --git a/tests/ui/async-await/async-closures/fn-exception-target-features.rs b/tests/ui/async-await/async-closures/fn-exception-target-features.rs new file mode 100644 index 000000000000..de62fc8bf7e3 --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception-target-features.rs @@ -0,0 +1,17 @@ +//@ edition: 2021 +//@ only-x86_64 + +#![feature(async_closure, target_feature_11)] +// `target_feature_11` just to test safe functions w/ target features. + +use std::pin::Pin; +use std::future::Future; + +#[target_feature(enable = "sse2")] +fn target_feature() -> Pin + 'static>> { todo!() } + +fn test(f: impl async Fn()) {} + +fn main() { + test(target_feature); //~ ERROR the trait bound +} diff --git a/tests/ui/async-await/async-closures/fn-exception-target-features.stderr b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr new file mode 100644 index 000000000000..396167f40705 --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `fn() -> Pin + 'static)>> {target_feature}: AsyncFn<()>` is not satisfied + --> $DIR/fn-exception-target-features.rs:16:10 + | +LL | test(target_feature); + | ---- ^^^^^^^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `fn() -> Pin + 'static)>> {target_feature}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `test` + --> $DIR/fn-exception-target-features.rs:13:17 + | +LL | fn test(f: impl async Fn()) {} + | ^^^^^^^^^^ required by this bound in `test` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/async-closures/fn-exception.rs b/tests/ui/async-await/async-closures/fn-exception.rs new file mode 100644 index 000000000000..0e06ebf48a4b --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception.rs @@ -0,0 +1,21 @@ +//@ edition: 2021 + +#![feature(async_closure)] + +use std::pin::Pin; +use std::future::Future; + +unsafe extern "Rust" { + pub unsafe fn unsafety() -> Pin + 'static>>; +} + +unsafe extern "C" { + pub safe fn abi() -> Pin + 'static>>; +} + +fn test(f: impl async Fn()) {} + +fn main() { + test(unsafety); //~ ERROR the trait bound + test(abi); //~ ERROR the trait bound +} diff --git a/tests/ui/async-await/async-closures/fn-exception.stderr b/tests/ui/async-await/async-closures/fn-exception.stderr new file mode 100644 index 000000000000..bacd079af0f2 --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `unsafe fn() -> Pin + 'static)>> {unsafety}: AsyncFn<()>` is not satisfied + --> $DIR/fn-exception.rs:19:10 + | +LL | test(unsafety); + | ---- ^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `unsafe fn() -> Pin + 'static)>> {unsafety}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `test` + --> $DIR/fn-exception.rs:16:17 + | +LL | fn test(f: impl async Fn()) {} + | ^^^^^^^^^^ required by this bound in `test` + +error[E0277]: the trait bound `extern "C" fn() -> Pin + 'static)>> {abi}: AsyncFn<()>` is not satisfied + --> $DIR/fn-exception.rs:20:10 + | +LL | test(abi); + | ---- ^^^ the trait `AsyncFn<()>` is not implemented for fn item `extern "C" fn() -> Pin + 'static)>> {abi}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `test` + --> $DIR/fn-exception.rs:16:17 + | +LL | fn test(f: impl async Fn()) {} + | ^^^^^^^^^^ required by this bound in `test` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/async-closures/non-copy-arg-does-not-force-inner-move.rs b/tests/ui/async-await/async-closures/non-copy-arg-does-not-force-inner-move.rs new file mode 100644 index 000000000000..cd9d98d07992 --- /dev/null +++ b/tests/ui/async-await/async-closures/non-copy-arg-does-not-force-inner-move.rs @@ -0,0 +1,17 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ build-pass + +#![feature(async_closure)] + +extern crate block_on; + +fn wrapper(f: impl Fn(String)) -> impl async Fn(String) { + async move |s| f(s) +} + +fn main() { + block_on::block_on(async { + wrapper(|who| println!("Hello, {who}!"))(String::from("world")).await; + }); +} diff --git a/tests/ui/async-await/in-trait/generics-mismatch.rs b/tests/ui/async-await/in-trait/generics-mismatch.rs index d3d1284982a9..a5c81a929981 100644 --- a/tests/ui/async-await/in-trait/generics-mismatch.rs +++ b/tests/ui/async-await/in-trait/generics-mismatch.rs @@ -6,7 +6,7 @@ trait Foo { impl Foo for () { async fn foo() {} - //~^ ERROR: method `foo` has an incompatible generic parameter for trait `Foo` [E0053] + //~^ ERROR: associated function `foo` has an incompatible generic parameter for trait `Foo` [E0053] } fn main() {} diff --git a/tests/ui/async-await/in-trait/generics-mismatch.stderr b/tests/ui/async-await/in-trait/generics-mismatch.stderr index c0357dc7f3e6..cb0f95e8d098 100644 --- a/tests/ui/async-await/in-trait/generics-mismatch.stderr +++ b/tests/ui/async-await/in-trait/generics-mismatch.stderr @@ -1,4 +1,4 @@ -error[E0053]: method `foo` has an incompatible generic parameter for trait `Foo` +error[E0053]: associated function `foo` has an incompatible generic parameter for trait `Foo` --> $DIR/generics-mismatch.rs:8:18 | LL | trait Foo { diff --git a/tests/ui/attributes/no-sanitize.rs b/tests/ui/attributes/no-sanitize.rs new file mode 100644 index 000000000000..82b7a22d5701 --- /dev/null +++ b/tests/ui/attributes/no-sanitize.rs @@ -0,0 +1,34 @@ +#![feature(no_sanitize)] +#![feature(stmt_expr_attributes)] +#![deny(unused_attributes)] +#![allow(dead_code)] + +fn invalid() { + #[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition + { + 1 + }; +} + +#[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition +type InvalidTy = (); + +#[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition +mod invalid_module {} + +fn main() { + let _ = #[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition + (|| 1); +} + +#[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition +struct F; + +#[no_sanitize(memory)] //~ ERROR attribute should be applied to a function definition +impl F { + #[no_sanitize(memory)] + fn valid(&self) {} +} + +#[no_sanitize(memory)] +fn valid() {} diff --git a/tests/ui/attributes/no-sanitize.stderr b/tests/ui/attributes/no-sanitize.stderr new file mode 100644 index 000000000000..f742ba0beed1 --- /dev/null +++ b/tests/ui/attributes/no-sanitize.stderr @@ -0,0 +1,55 @@ +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:7:5 + | +LL | #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | / { +LL | | 1 +LL | | }; + | |_____- not a function definition + +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:13:1 + | +LL | #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | type InvalidTy = (); + | -------------------- not a function definition + +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:16:1 + | +LL | #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | mod invalid_module {} + | --------------------- not a function definition + +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:20:13 + | +LL | let _ = #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | (|| 1); + | ------ not a function definition + +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:24:1 + | +LL | #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | struct F; + | --------- not a function definition + +error: attribute should be applied to a function definition + --> $DIR/no-sanitize.rs:27:1 + | +LL | #[no_sanitize(memory)] + | ^^^^^^^^^^^^^^^^^^^^^^ +LL | / impl F { +LL | | #[no_sanitize(memory)] +LL | | fn valid(&self) {} +LL | | } + | |_- not a function definition + +error: aborting due to 6 previous errors + diff --git a/tests/ui/cfg/disallowed-cli-cfgs-allow.rs b/tests/ui/cfg/disallowed-cli-cfgs-allow.rs new file mode 100644 index 000000000000..0a9b07429d5f --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs-allow.rs @@ -0,0 +1,4 @@ +//@ check-pass +//@ compile-flags: --cfg unix -Aexplicit_builtin_cfgs_in_flags + +fn main() {} diff --git a/tests/ui/cfg/disallowed-cli-cfgs.debug_assertions_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.debug_assertions_.stderr new file mode 100644 index 000000000000..ec3e15b85c79 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.debug_assertions_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg debug_assertions` flag + | + = note: config `debug_assertions` is only supposed to be controlled by `-C debug-assertions` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.overflow_checks_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.overflow_checks_.stderr new file mode 100644 index 000000000000..ba8ed3f2e65d --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.overflow_checks_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg overflow_checks` flag + | + = note: config `overflow_checks` is only supposed to be controlled by `-C overflow-checks` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.panic_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.panic_.stderr new file mode 100644 index 000000000000..5b375694b130 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.panic_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg panic="abort"` flag + | + = note: config `panic` is only supposed to be controlled by `-C panic` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.proc_macro_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.proc_macro_.stderr new file mode 100644 index 000000000000..c2e299088572 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.proc_macro_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg proc_macro` flag + | + = note: config `proc_macro` is only supposed to be controlled by `--crate-type proc-macro` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.relocation_model_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.relocation_model_.stderr new file mode 100644 index 000000000000..65ada551cba9 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.relocation_model_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg relocation_model="a"` flag + | + = note: config `relocation_model` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.rs b/tests/ui/cfg/disallowed-cli-cfgs.rs new file mode 100644 index 000000000000..714c01f4bc6e --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.rs @@ -0,0 +1,35 @@ +//@ check-fail +//@ revisions: overflow_checks_ debug_assertions_ ub_checks_ sanitize_ +//@ revisions: sanitizer_cfi_generalize_pointers_ sanitizer_cfi_normalize_integers_ +//@ revisions: proc_macro_ panic_ target_feature_ unix_ windows_ target_abi_ +//@ revisions: target_arch_ target_endian_ target_env_ target_family_ target_os_ +//@ revisions: target_pointer_width_ target_vendor_ target_has_atomic_ +//@ revisions: target_has_atomic_equal_alignment_ target_has_atomic_load_store_ +//@ revisions: target_thread_local_ relocation_model_ + +//@ [overflow_checks_]compile-flags: --cfg overflow_checks +//@ [debug_assertions_]compile-flags: --cfg debug_assertions +//@ [ub_checks_]compile-flags: --cfg ub_checks +//@ [sanitize_]compile-flags: --cfg sanitize="cfi" +//@ [sanitizer_cfi_generalize_pointers_]compile-flags: --cfg sanitizer_cfi_generalize_pointers +//@ [sanitizer_cfi_normalize_integers_]compile-flags: --cfg sanitizer_cfi_normalize_integers +//@ [proc_macro_]compile-flags: --cfg proc_macro +//@ [panic_]compile-flags: --cfg panic="abort" +//@ [target_feature_]compile-flags: --cfg target_feature="sse3" +//@ [unix_]compile-flags: --cfg unix +//@ [windows_]compile-flags: --cfg windows +//@ [target_abi_]compile-flags: --cfg target_abi="gnu" +//@ [target_arch_]compile-flags: --cfg target_arch="arm" +//@ [target_endian_]compile-flags: --cfg target_endian="little" +//@ [target_env_]compile-flags: --cfg target_env +//@ [target_family_]compile-flags: --cfg target_family="unix" +//@ [target_os_]compile-flags: --cfg target_os="linux" +//@ [target_pointer_width_]compile-flags: --cfg target_pointer_width="32" +//@ [target_vendor_]compile-flags: --cfg target_vendor +//@ [target_has_atomic_]compile-flags: --cfg target_has_atomic="32" +//@ [target_has_atomic_equal_alignment_]compile-flags: --cfg target_has_atomic_equal_alignment="32" +//@ [target_has_atomic_load_store_]compile-flags: --cfg target_has_atomic_load_store="32" +//@ [target_thread_local_]compile-flags: --cfg target_thread_local +//@ [relocation_model_]compile-flags: --cfg relocation_model="a" + +fn main() {} diff --git a/tests/ui/cfg/disallowed-cli-cfgs.sanitize_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.sanitize_.stderr new file mode 100644 index 000000000000..b1f3b19dd866 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.sanitize_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg sanitize="cfi"` flag + | + = note: config `sanitize` is only supposed to be controlled by `-Z sanitizer` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_generalize_pointers_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_generalize_pointers_.stderr new file mode 100644 index 000000000000..49106f99b6bc --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_generalize_pointers_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg sanitizer_cfi_generalize_pointers` flag + | + = note: config `sanitizer_cfi_generalize_pointers` is only supposed to be controlled by `-Z sanitizer=cfi` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_normalize_integers_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_normalize_integers_.stderr new file mode 100644 index 000000000000..f9bb9e76297a --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.sanitizer_cfi_normalize_integers_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg sanitizer_cfi_normalize_integers` flag + | + = note: config `sanitizer_cfi_normalize_integers` is only supposed to be controlled by `-Z sanitizer=cfi` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_abi_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_abi_.stderr new file mode 100644 index 000000000000..f5c09110ce85 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_abi_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_abi="gnu"` flag + | + = note: config `target_abi` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_arch_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_arch_.stderr new file mode 100644 index 000000000000..7f616e75d4e1 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_arch_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_arch="arm"` flag + | + = note: config `target_arch` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_endian_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_endian_.stderr new file mode 100644 index 000000000000..755f3708d67f --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_endian_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_endian="little"` flag + | + = note: config `target_endian` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_env_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_env_.stderr new file mode 100644 index 000000000000..ccd027dbebec --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_env_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_env` flag + | + = note: config `target_env` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_family_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_family_.stderr new file mode 100644 index 000000000000..e376e4e16a19 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_family_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_family="unix"` flag + | + = note: config `target_family` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_feature_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_feature_.stderr new file mode 100644 index 000000000000..457cca6b8854 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_feature_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_feature="sse3"` flag + | + = note: config `target_feature` is only supposed to be controlled by `-C target-feature` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_.stderr new file mode 100644 index 000000000000..160741198ad8 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_has_atomic="32"` flag + | + = note: config `target_has_atomic` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_equal_alignment_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_equal_alignment_.stderr new file mode 100644 index 000000000000..096490a03f61 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_equal_alignment_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_has_atomic_equal_alignment="32"` flag + | + = note: config `target_has_atomic_equal_alignment` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_load_store_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_load_store_.stderr new file mode 100644 index 000000000000..5253987cb126 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_has_atomic_load_store_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_has_atomic_load_store="32"` flag + | + = note: config `target_has_atomic_load_store` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_os_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_os_.stderr new file mode 100644 index 000000000000..ba2c967bfa16 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_os_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_os="linux"` flag + | + = note: config `target_os` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_pointer_width_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_pointer_width_.stderr new file mode 100644 index 000000000000..983313bc5172 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_pointer_width_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_pointer_width="32"` flag + | + = note: config `target_pointer_width` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_thread_local_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_thread_local_.stderr new file mode 100644 index 000000000000..4f66ea17ee40 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_thread_local_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_thread_local` flag + | + = note: config `target_thread_local` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.target_vendor_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.target_vendor_.stderr new file mode 100644 index 000000000000..e8f325984459 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.target_vendor_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg target_vendor` flag + | + = note: config `target_vendor` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr new file mode 100644 index 000000000000..96b5beb02106 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg test` flag + | + = note: config `test` is only supposed to be controlled by `--test` + = note: see for more information + = note: `#[deny(unexpected_builtin_cfgs)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.ub_checks_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.ub_checks_.stderr new file mode 100644 index 000000000000..e61e5ac69d52 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.ub_checks_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg ub_checks` flag + | + = note: config `ub_checks` is only supposed to be controlled by `-Z ub-checks` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.unix_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.unix_.stderr new file mode 100644 index 000000000000..f3c6bb0ef1b0 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.unix_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg unix` flag + | + = note: config `unix` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/cfg/disallowed-cli-cfgs.windows_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.windows_.stderr new file mode 100644 index 000000000000..171a3f95bbd4 --- /dev/null +++ b/tests/ui/cfg/disallowed-cli-cfgs.windows_.stderr @@ -0,0 +1,8 @@ +error: unexpected `--cfg windows` flag + | + = note: config `windows` is only supposed to be controlled by `--target` + = note: manually setting a built-in cfg can and does create incoherent behaviors + = note: `#[deny(explicit_builtin_cfgs_in_flags)]` on by default + +error: aborting due to 1 previous error + diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs b/tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs index 52ed008137fc..74f37b514e4a 100644 --- a/tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs +++ b/tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs @@ -1,10 +1,9 @@ // Test precise capture of a multi-variant enum (when remaining variants are // visibly uninhabited). -//@ revisions: min_exhaustive_patterns exhaustive_patterns +//@ revisions: normal exhaustive_patterns //@ edition:2021 //@ run-pass #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))] -#![cfg_attr(min_exhaustive_patterns, feature(min_exhaustive_patterns))] #![feature(never_type)] pub fn main() { diff --git a/tests/ui/codemap_tests/huge_multispan_highlight.svg b/tests/ui/codemap_tests/huge_multispan_highlight.svg index 7b6dbb17c6f9..12058176dc0f 100644 --- a/tests/ui/codemap_tests/huge_multispan_highlight.svg +++ b/tests/ui/codemap_tests/huge_multispan_highlight.svg @@ -1,4 +1,4 @@ - + { fn main() { Holder { - inner: Box::new(()), + inner: Box::new(()), //~ ERROR: the trait `Provider` cannot be made into an object }; } diff --git a/tests/ui/generic-associated-types/issue-71176.stderr b/tests/ui/generic-associated-types/issue-71176.stderr index a1913bb618bc..9f83c162c02c 100644 --- a/tests/ui/generic-associated-types/issue-71176.stderr +++ b/tests/ui/generic-associated-types/issue-71176.stderr @@ -64,7 +64,23 @@ LL | type A<'a>; = help: consider moving `A` to another trait = help: only type `()` implements the trait, consider using it directly instead -error: aborting due to 4 previous errors +error[E0038]: the trait `Provider` cannot be made into an object + --> $DIR/issue-71176.rs:19:16 + | +LL | inner: Box::new(()), + | ^^^^^^^^^^^^ `Provider` 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 + --> $DIR/issue-71176.rs:2:10 + | +LL | trait Provider { + | -------- this trait cannot be made into an object... +LL | type A<'a>; + | ^ ...because it contains the generic associated type `A` + = help: consider moving `A` to another trait + = help: only type `()` implements the trait, consider using it directly instead + +error: aborting due to 5 previous errors Some errors have detailed explanations: E0038, E0107. For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/impl-trait/in-trait/cannot-capture-intersection.rs b/tests/ui/impl-trait/in-trait/cannot-capture-intersection.rs new file mode 100644 index 000000000000..d7b62436d2d3 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/cannot-capture-intersection.rs @@ -0,0 +1,29 @@ +#![feature(precise_capturing)] + +use std::future::Future; +use std::pin::Pin; + +trait MyTrait { + fn foo<'a, 'b>(&'a self, x: &'b i32) -> impl Future; +} + +trait ErasedMyTrait { + fn foo<'life0, 'life1, 'dynosaur>(&'life0 self, x: &'life1 i32) + -> Pin + 'dynosaur>> + where + 'life0: 'dynosaur, + 'life1: 'dynosaur; +} + +struct DynMyTrait { + ptr: T, +} + +impl MyTrait for DynMyTrait { + fn foo<'a, 'b>(&'a self, x: &'b i32) -> impl Future { + self.ptr.foo(x) + //~^ ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/in-trait/cannot-capture-intersection.stderr b/tests/ui/impl-trait/in-trait/cannot-capture-intersection.stderr new file mode 100644 index 000000000000..92ef66c55043 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/cannot-capture-intersection.stderr @@ -0,0 +1,13 @@ +error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds + --> $DIR/cannot-capture-intersection.rs:24:9 + | +LL | fn foo<'a, 'b>(&'a self, x: &'b i32) -> impl Future { + | ------------------------- opaque type defined here +LL | self.ptr.foo(x) + | ^^^^^^^^^^^^^^^ + | + = note: hidden type `Pin>>` captures lifetime `'_` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/impl-trait/in-trait/trait-more-generics-than-impl.rs b/tests/ui/impl-trait/in-trait/trait-more-generics-than-impl.rs index d9fac0238e16..0477de3315a2 100644 --- a/tests/ui/impl-trait/in-trait/trait-more-generics-than-impl.rs +++ b/tests/ui/impl-trait/in-trait/trait-more-generics-than-impl.rs @@ -6,7 +6,7 @@ trait Foo { impl Foo for S { fn bar() -> impl Sized {} - //~^ ERROR method `bar` has 0 type parameters but its trait declaration has 1 type parameter + //~^ ERROR associated function `bar` has 0 type parameters but its trait declaration has 1 type parameter } fn main() { diff --git a/tests/ui/impl-trait/in-trait/trait-more-generics-than-impl.stderr b/tests/ui/impl-trait/in-trait/trait-more-generics-than-impl.stderr index eed4c07710e0..ca67db403627 100644 --- a/tests/ui/impl-trait/in-trait/trait-more-generics-than-impl.stderr +++ b/tests/ui/impl-trait/in-trait/trait-more-generics-than-impl.stderr @@ -1,4 +1,4 @@ -error[E0049]: method `bar` has 0 type parameters but its trait declaration has 1 type parameter +error[E0049]: associated function `bar` has 0 type parameters but its trait declaration has 1 type parameter --> $DIR/trait-more-generics-than-impl.rs:8:11 | LL | fn bar() -> impl Sized; diff --git a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr index 4d4ba58c9744..b7cee7d0b1f7 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr @@ -2,17 +2,12 @@ error[E0700]: hidden type for `impl Trait<'d, 'e>` captures lifetime that does n --> $DIR/ordinary-bounds-unrelated.rs:28:33 | LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> - | -- ------------------ opaque type defined here - | | - | hidden type `Ordinary<'b>` captures the lifetime `'b` as defined here + | ------------------ opaque type defined here ... LL | if condition() { a } else { b } | ^ | -help: to declare that `impl Trait<'d, 'e>` captures `'b`, you can add an explicit `'b` lifetime bound - | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + 'b - | ++++ + = note: hidden type `Ordinary<'_>` captures lifetime `'_` error: aborting due to 1 previous error diff --git a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr index 060eaa7e64a1..d1190da6c9f8 100644 --- a/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr +++ b/tests/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr @@ -2,17 +2,12 @@ error[E0700]: hidden type for `impl Trait<'a, 'b>` captures lifetime that does n --> $DIR/ordinary-bounds-unsuited.rs:31:33 | LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> - | -- ------------------ opaque type defined here - | | - | hidden type `Ordinary<'b>` captures the lifetime `'b` as defined here + | ------------------ opaque type defined here ... LL | if condition() { a } else { b } | ^ | -help: to declare that `impl Trait<'a, 'b>` captures `'b`, you can add an explicit `'b` lifetime bound - | -LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> + 'b - | ++++ + = note: hidden type `Ordinary<'_>` captures lifetime `'_` error: aborting due to 1 previous error diff --git a/tests/ui/inline-const/using-late-bound-from-closure.rs b/tests/ui/inline-const/using-late-bound-from-closure.rs new file mode 100644 index 000000000000..2b12b2e26a22 --- /dev/null +++ b/tests/ui/inline-const/using-late-bound-from-closure.rs @@ -0,0 +1,16 @@ +// Test for ICE: cannot convert ReLateParam to a region vid +// https://github.com/rust-lang/rust/issues/125873 + +#![feature(closure_lifetime_binder)] +fn foo() { + let a = for<'a> |b: &'a ()| -> &'a () { + const { + let awd = (); + let _: &'a () = &awd; + //~^ `awd` does not live long enough + }; + b + }; +} + +fn main() {} diff --git a/tests/ui/inline-const/using-late-bound-from-closure.stderr b/tests/ui/inline-const/using-late-bound-from-closure.stderr new file mode 100644 index 000000000000..d6e1579aa343 --- /dev/null +++ b/tests/ui/inline-const/using-late-bound-from-closure.stderr @@ -0,0 +1,19 @@ +error[E0597]: `awd` does not live long enough + --> $DIR/using-late-bound-from-closure.rs:9:29 + | +LL | let a = for<'a> |b: &'a ()| -> &'a () { + | -- lifetime `'a` defined here +LL | const { +LL | let awd = (); + | --- binding `awd` declared here +LL | let _: &'a () = &awd; + | ------ ^^^^ borrowed value does not live long enough + | | + | type annotation requires that `awd` is borrowed for `'a` +LL | +LL | }; + | - `awd` dropped here while still borrowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/issues/issue-19380.rs b/tests/ui/issues/issue-19380.rs index fce737cba18d..8b3fe4d2b099 100644 --- a/tests/ui/issues/issue-19380.rs +++ b/tests/ui/issues/issue-19380.rs @@ -15,5 +15,6 @@ struct Bar { const FOO : Foo = Foo; const BAR : Bar = Bar { foos: &[&FOO]}; //~^ ERROR E0038 +//~| ERROR E0038 fn main() { } diff --git a/tests/ui/issues/issue-19380.stderr b/tests/ui/issues/issue-19380.stderr index f6244d9d44f3..1d7aa6bd459e 100644 --- a/tests/ui/issues/issue-19380.stderr +++ b/tests/ui/issues/issue-19380.stderr @@ -45,6 +45,29 @@ help: alternatively, consider constraining `qiz` so it does not apply to trait o LL | fn qiz() where Self: Sized; | +++++++++++++++++ -error: aborting due to 2 previous errors +error[E0038]: the trait `Qiz` cannot be made into an object + --> $DIR/issue-19380.rs:16:31 + | +LL | const BAR : Bar = Bar { foos: &[&FOO]}; + | ^^^^^^^ `Qiz` 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 + --> $DIR/issue-19380.rs:2:6 + | +LL | trait Qiz { + | --- this trait cannot be made into an object... +LL | fn qiz(); + | ^^^ ...because associated function `qiz` has no `self` parameter + = help: only type `Foo` implements the trait, consider using it directly instead +help: consider turning `qiz` into a method by giving it a `&self` argument + | +LL | fn qiz(&self); + | +++++ +help: alternatively, consider constraining `qiz` so it does not apply to trait objects + | +LL | fn qiz() where Self: Sized; + | +++++++++++++++++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/lint/command-line-lint-group-forbid.stderr b/tests/ui/lint/command-line-lint-group-forbid.stderr index 7b527e7b8b46..ec9b6ca9664c 100644 --- a/tests/ui/lint/command-line-lint-group-forbid.stderr +++ b/tests/ui/lint/command-line-lint-group-forbid.stderr @@ -5,7 +5,6 @@ LL | let _InappropriateCamelCasing = true; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `_inappropriate_camel_casing` | = note: `-F non-snake-case` implied by `-F bad-style` - = help: to override `-F bad-style` add `#[allow(non_snake_case)]` error: aborting due to 1 previous error diff --git a/tests/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.stderr b/tests/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.stderr index 01c2ed84c635..d1b764b34143 100644 --- a/tests/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.stderr +++ b/tests/ui/lint/force-warn/cap-lints-warn-allowed-warn-by-default-lint.stderr @@ -7,7 +7,6 @@ LL | 0...100 => true, = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see = note: `--force-warn ellipsis-inclusive-range-patterns` implied by `--force-warn rust-2021-compatibility` - = help: to override `--force-warn rust-2021-compatibility` add `#[allow(ellipsis_inclusive_range_patterns)]` warning: 1 warning emitted diff --git a/tests/ui/lint/force-warn/lint-group-allow-warnings.stderr b/tests/ui/lint/force-warn/lint-group-allow-warnings.stderr index e925a195fb1c..dc7b1b7b98d0 100644 --- a/tests/ui/lint/force-warn/lint-group-allow-warnings.stderr +++ b/tests/ui/lint/force-warn/lint-group-allow-warnings.stderr @@ -5,7 +5,6 @@ LL | pub fn FUNCTION() {} | ^^^^^^^^ help: convert the identifier to snake case: `function` | = note: `--force-warn non-snake-case` implied by `--force-warn nonstandard-style` - = help: to override `--force-warn nonstandard-style` add `#[allow(non_snake_case)]` warning: 1 warning emitted diff --git a/tests/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr b/tests/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr index a74cda2239f5..dc85e8cf9617 100644 --- a/tests/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr +++ b/tests/ui/lint/force-warn/lint-group-allowed-cli-warn-by-default-lint.stderr @@ -7,7 +7,6 @@ LL | pub fn function(_x: Box) {} = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see = note: `--force-warn bare-trait-objects` implied by `--force-warn rust-2018-idioms` - = help: to override `--force-warn rust-2018-idioms` add `#[allow(bare_trait_objects)]` help: if this is an object-safe trait, use `dyn` | LL | pub fn function(_x: Box) {} diff --git a/tests/ui/lint/force-warn/lint-group-allowed-lint-group.stderr b/tests/ui/lint/force-warn/lint-group-allowed-lint-group.stderr index c9472a3b9b9d..55cfad838f84 100644 --- a/tests/ui/lint/force-warn/lint-group-allowed-lint-group.stderr +++ b/tests/ui/lint/force-warn/lint-group-allowed-lint-group.stderr @@ -7,7 +7,6 @@ LL | pub fn function(_x: Box) {} = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see = note: `--force-warn bare-trait-objects` implied by `--force-warn rust-2018-idioms` - = help: to override `--force-warn rust-2018-idioms` add `#[allow(bare_trait_objects)]` help: if this is an object-safe trait, use `dyn` | LL | pub fn function(_x: Box) {} diff --git a/tests/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.stderr b/tests/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.stderr index 558d5cbb5315..b7bf0c4ee212 100644 --- a/tests/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.stderr +++ b/tests/ui/lint/force-warn/lint-group-allowed-warn-by-default-lint.stderr @@ -7,7 +7,6 @@ LL | pub fn function(_x: Box) {} = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see = note: `--force-warn bare-trait-objects` implied by `--force-warn rust-2018-idioms` - = help: to override `--force-warn rust-2018-idioms` add `#[allow(bare_trait_objects)]` help: if this is an object-safe trait, use `dyn` | LL | pub fn function(_x: Box) {} diff --git a/tests/ui/lint/group-forbid-always-trumps-cli.stderr b/tests/ui/lint/group-forbid-always-trumps-cli.stderr index 21674ebae9ce..ed1242eacfce 100644 --- a/tests/ui/lint/group-forbid-always-trumps-cli.stderr +++ b/tests/ui/lint/group-forbid-always-trumps-cli.stderr @@ -5,7 +5,6 @@ LL | let x = 1; | ^ help: if this is intentional, prefix it with an underscore: `_x` | = note: `-F unused-variables` implied by `-F unused` - = help: to override `-F unused` add `#[allow(unused_variables)]` error: aborting due to 1 previous error diff --git a/tests/ui/lint/lint-unnecessary-parens.fixed b/tests/ui/lint/lint-unnecessary-parens.fixed index 760897c5143f..089aa1b7ab7d 100644 --- a/tests/ui/lint/lint-unnecessary-parens.fixed +++ b/tests/ui/lint/lint-unnecessary-parens.fixed @@ -1,6 +1,7 @@ //@ run-rustfix #![deny(unused_parens)] +#![feature(raw_ref_op)] #![allow(while_true)] // for rustfix #[derive(Eq, PartialEq)] @@ -125,4 +126,11 @@ fn main() { // FIXME: false positive. This parenthesis is required. unit! {} - One //~ ERROR unnecessary parentheses around block return value }; + + // Do *not* lint around `&raw` (but do lint when `&` creates a reference). + let mut x = 0; + let _r = &x; //~ ERROR unnecessary parentheses + let _r = &mut x; //~ ERROR unnecessary parentheses + let _r = (&raw const x); + let _r = (&raw mut x); } diff --git a/tests/ui/lint/lint-unnecessary-parens.rs b/tests/ui/lint/lint-unnecessary-parens.rs index 7cbaac8ae540..dc77ee003523 100644 --- a/tests/ui/lint/lint-unnecessary-parens.rs +++ b/tests/ui/lint/lint-unnecessary-parens.rs @@ -1,6 +1,7 @@ //@ run-rustfix #![deny(unused_parens)] +#![feature(raw_ref_op)] #![allow(while_true)] // for rustfix #[derive(Eq, PartialEq)] @@ -125,4 +126,11 @@ fn main() { // FIXME: false positive. This parenthesis is required. (unit! {} - One) //~ ERROR unnecessary parentheses around block return value }; + + // Do *not* lint around `&raw` (but do lint when `&` creates a reference). + let mut x = 0; + let _r = (&x); //~ ERROR unnecessary parentheses + let _r = (&mut x); //~ ERROR unnecessary parentheses + let _r = (&raw const x); + let _r = (&raw mut x); } diff --git a/tests/ui/lint/lint-unnecessary-parens.stderr b/tests/ui/lint/lint-unnecessary-parens.stderr index 755dd5fc3094..c9422437a9fc 100644 --- a/tests/ui/lint/lint-unnecessary-parens.stderr +++ b/tests/ui/lint/lint-unnecessary-parens.stderr @@ -1,5 +1,5 @@ error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:13:12 + --> $DIR/lint-unnecessary-parens.rs:14:12 | LL | return (1); | ^ ^ @@ -16,7 +16,7 @@ LL + return 1; | error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:16:12 + --> $DIR/lint-unnecessary-parens.rs:17:12 | LL | return (X { y }); | ^ ^ @@ -28,7 +28,7 @@ LL + return X { y }; | error: unnecessary parentheses around type - --> $DIR/lint-unnecessary-parens.rs:19:46 + --> $DIR/lint-unnecessary-parens.rs:20:46 | LL | pub fn unused_parens_around_return_type() -> (u32) { | ^ ^ @@ -40,7 +40,7 @@ LL + pub fn unused_parens_around_return_type() -> u32 { | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:25:9 + --> $DIR/lint-unnecessary-parens.rs:26:9 | LL | (5) | ^ ^ @@ -52,7 +52,7 @@ LL + 5 | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:27:5 + --> $DIR/lint-unnecessary-parens.rs:28:5 | LL | (5) | ^ ^ @@ -64,7 +64,7 @@ LL + 5 | error: unnecessary parentheses around `if` condition - --> $DIR/lint-unnecessary-parens.rs:39:7 + --> $DIR/lint-unnecessary-parens.rs:40:7 | LL | if(true) {} | ^ ^ @@ -76,7 +76,7 @@ LL + if true {} | error: unnecessary parentheses around `while` condition - --> $DIR/lint-unnecessary-parens.rs:40:10 + --> $DIR/lint-unnecessary-parens.rs:41:10 | LL | while(true) {} | ^ ^ @@ -88,7 +88,7 @@ LL + while true {} | error: unnecessary parentheses around `for` iterator expression - --> $DIR/lint-unnecessary-parens.rs:41:13 + --> $DIR/lint-unnecessary-parens.rs:42:13 | LL | for _ in(e) {} | ^ ^ @@ -100,7 +100,7 @@ LL + for _ in e {} | error: unnecessary parentheses around `match` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:42:10 + --> $DIR/lint-unnecessary-parens.rs:43:10 | LL | match(1) { _ => ()} | ^ ^ @@ -112,7 +112,7 @@ LL + match 1 { _ => ()} | error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:43:11 + --> $DIR/lint-unnecessary-parens.rs:44:11 | LL | return(1); | ^ ^ @@ -124,7 +124,7 @@ LL + return 1; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:74:31 + --> $DIR/lint-unnecessary-parens.rs:75:31 | LL | pub const CONST_ITEM: usize = (10); | ^ ^ @@ -136,7 +136,7 @@ LL + pub const CONST_ITEM: usize = 10; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:75:33 + --> $DIR/lint-unnecessary-parens.rs:76:33 | LL | pub static STATIC_ITEM: usize = (10); | ^ ^ @@ -148,7 +148,7 @@ LL + pub static STATIC_ITEM: usize = 10; | error: unnecessary parentheses around function argument - --> $DIR/lint-unnecessary-parens.rs:79:9 + --> $DIR/lint-unnecessary-parens.rs:80:9 | LL | bar((true)); | ^ ^ @@ -160,7 +160,7 @@ LL + bar(true); | error: unnecessary parentheses around `if` condition - --> $DIR/lint-unnecessary-parens.rs:81:8 + --> $DIR/lint-unnecessary-parens.rs:82:8 | LL | if (true) {} | ^ ^ @@ -172,7 +172,7 @@ LL + if true {} | error: unnecessary parentheses around `while` condition - --> $DIR/lint-unnecessary-parens.rs:82:11 + --> $DIR/lint-unnecessary-parens.rs:83:11 | LL | while (true) {} | ^ ^ @@ -184,7 +184,7 @@ LL + while true {} | error: unnecessary parentheses around `match` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:83:11 + --> $DIR/lint-unnecessary-parens.rs:84:11 | LL | match (true) { | ^ ^ @@ -196,7 +196,7 @@ LL + match true { | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:86:16 + --> $DIR/lint-unnecessary-parens.rs:87:16 | LL | if let 1 = (1) {} | ^ ^ @@ -208,7 +208,7 @@ LL + if let 1 = 1 {} | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:87:19 + --> $DIR/lint-unnecessary-parens.rs:88:19 | LL | while let 1 = (2) {} | ^ ^ @@ -220,7 +220,7 @@ LL + while let 1 = 2 {} | error: unnecessary parentheses around method argument - --> $DIR/lint-unnecessary-parens.rs:103:24 + --> $DIR/lint-unnecessary-parens.rs:104:24 | LL | X { y: false }.foo((true)); | ^ ^ @@ -232,7 +232,7 @@ LL + X { y: false }.foo(true); | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:105:18 + --> $DIR/lint-unnecessary-parens.rs:106:18 | LL | let mut _a = (0); | ^ ^ @@ -244,7 +244,7 @@ LL + let mut _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:106:10 + --> $DIR/lint-unnecessary-parens.rs:107:10 | LL | _a = (0); | ^ ^ @@ -256,7 +256,7 @@ LL + _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:107:11 + --> $DIR/lint-unnecessary-parens.rs:108:11 | LL | _a += (1); | ^ ^ @@ -268,7 +268,7 @@ LL + _a += 1; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:109:8 + --> $DIR/lint-unnecessary-parens.rs:110:8 | LL | let(mut _a) = 3; | ^ ^ @@ -280,7 +280,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:110:9 + --> $DIR/lint-unnecessary-parens.rs:111:9 | LL | let (mut _a) = 3; | ^ ^ @@ -292,7 +292,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:111:8 + --> $DIR/lint-unnecessary-parens.rs:112:8 | LL | let( mut _a) = 3; | ^^ ^ @@ -304,7 +304,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:113:8 + --> $DIR/lint-unnecessary-parens.rs:114:8 | LL | let(_a) = 3; | ^ ^ @@ -316,7 +316,7 @@ LL + let _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:114:9 + --> $DIR/lint-unnecessary-parens.rs:115:9 | LL | let (_a) = 3; | ^ ^ @@ -328,7 +328,7 @@ LL + let _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:115:8 + --> $DIR/lint-unnecessary-parens.rs:116:8 | LL | let( _a) = 3; | ^^ ^ @@ -340,7 +340,7 @@ LL + let _a = 3; | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:121:9 + --> $DIR/lint-unnecessary-parens.rs:122:9 | LL | (unit!() - One) | ^ ^ @@ -352,7 +352,7 @@ LL + unit!() - One | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:123:9 + --> $DIR/lint-unnecessary-parens.rs:124:9 | LL | (unit![] - One) | ^ ^ @@ -364,7 +364,7 @@ LL + unit![] - One | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:126:9 + --> $DIR/lint-unnecessary-parens.rs:127:9 | LL | (unit! {} - One) | ^ ^ @@ -375,5 +375,29 @@ LL - (unit! {} - One) LL + unit! {} - One | -error: aborting due to 31 previous errors +error: unnecessary parentheses around assigned value + --> $DIR/lint-unnecessary-parens.rs:132:14 + | +LL | let _r = (&x); + | ^ ^ + | +help: remove these parentheses + | +LL - let _r = (&x); +LL + let _r = &x; + | + +error: unnecessary parentheses around assigned value + --> $DIR/lint-unnecessary-parens.rs:133:14 + | +LL | let _r = (&mut x); + | ^ ^ + | +help: remove these parentheses + | +LL - let _r = (&mut x); +LL + let _r = &mut x; + | + +error: aborting due to 33 previous errors diff --git a/tests/ui/lint/unaligned_references.stderr b/tests/ui/lint/unaligned_references.current.stderr similarity index 93% rename from tests/ui/lint/unaligned_references.stderr rename to tests/ui/lint/unaligned_references.current.stderr index 328cafbd9864..0f980c5301f3 100644 --- a/tests/ui/lint/unaligned_references.stderr +++ b/tests/ui/lint/unaligned_references.current.stderr @@ -1,5 +1,5 @@ error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:28:13 + --> $DIR/unaligned_references.rs:32:13 | LL | &self.x; | ^^^^^^^ @@ -9,7 +9,7 @@ LL | &self.x; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:40:24 + --> $DIR/unaligned_references.rs:44:24 | LL | println!("{:?}", &*foo.0); | ^^^^^ @@ -19,7 +19,7 @@ LL | println!("{:?}", &*foo.0); = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:42:24 + --> $DIR/unaligned_references.rs:46:24 | LL | println!("{:?}", &*foo.0); | ^^^^^ @@ -29,7 +29,7 @@ LL | println!("{:?}", &*foo.0); = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:47:24 + --> $DIR/unaligned_references.rs:51:24 | LL | println!("{:?}", &*foo.0); | ^^^^^ @@ -39,7 +39,7 @@ LL | println!("{:?}", &*foo.0); = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:57:17 + --> $DIR/unaligned_references.rs:81:17 | LL | let _ = &good.ptr; | ^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let _ = &good.ptr; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:58:17 + --> $DIR/unaligned_references.rs:82:17 | LL | let _ = &good.data; | ^^^^^^^^^^ @@ -59,7 +59,7 @@ LL | let _ = &good.data; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:60:17 + --> $DIR/unaligned_references.rs:84:17 | LL | let _ = &good.data as *const _; | ^^^^^^^^^^ @@ -69,7 +69,7 @@ LL | let _ = &good.data as *const _; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:61:27 + --> $DIR/unaligned_references.rs:85:27 | LL | let _: *const _ = &good.data; | ^^^^^^^^^^ @@ -79,7 +79,7 @@ LL | let _: *const _ = &good.data; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:63:17 + --> $DIR/unaligned_references.rs:87:17 | LL | let _ = good.data.clone(); | ^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = good.data.clone(); = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:65:17 + --> $DIR/unaligned_references.rs:89:17 | LL | let _ = &good.data2[0]; | ^^^^^^^^^^^^^^ @@ -99,7 +99,7 @@ LL | let _ = &good.data2[0]; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:74:17 + --> $DIR/unaligned_references.rs:98:17 | LL | let _ = &packed2.x; | ^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | let _ = &packed2.x; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:113:20 + --> $DIR/unaligned_references.rs:137:20 | LL | let _ref = &m1.1.a; | ^^^^^^^ @@ -119,7 +119,7 @@ LL | let _ref = &m1.1.a; = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) error[E0793]: reference to packed field is unaligned - --> $DIR/unaligned_references.rs:116:20 + --> $DIR/unaligned_references.rs:140:20 | LL | let _ref = &m2.1.a; | ^^^^^^^ diff --git a/tests/ui/lint/unaligned_references.next.stderr b/tests/ui/lint/unaligned_references.next.stderr new file mode 100644 index 000000000000..0f980c5301f3 --- /dev/null +++ b/tests/ui/lint/unaligned_references.next.stderr @@ -0,0 +1,133 @@ +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:32:13 + | +LL | &self.x; + | ^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:44:24 + | +LL | println!("{:?}", &*foo.0); + | ^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:46:24 + | +LL | println!("{:?}", &*foo.0); + | ^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:51:24 + | +LL | println!("{:?}", &*foo.0); + | ^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:81:17 + | +LL | let _ = &good.ptr; + | ^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:82:17 + | +LL | let _ = &good.data; + | ^^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:84:17 + | +LL | let _ = &good.data as *const _; + | ^^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:85:27 + | +LL | let _: *const _ = &good.data; + | ^^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:87:17 + | +LL | let _ = good.data.clone(); + | ^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:89:17 + | +LL | let _ = &good.data2[0]; + | ^^^^^^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:98:17 + | +LL | let _ = &packed2.x; + | ^^^^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:137:20 + | +LL | let _ref = &m1.1.a; + | ^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error[E0793]: reference to packed field is unaligned + --> $DIR/unaligned_references.rs:140:20 + | +LL | let _ref = &m2.1.a; + | ^^^^^^^ + | + = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses + = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced) + = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers) + +error: aborting due to 13 previous errors + +For more information about this error, try `rustc --explain E0793`. diff --git a/tests/ui/lint/unaligned_references.rs b/tests/ui/lint/unaligned_references.rs index 3f6dab35475e..321e3ed135c4 100644 --- a/tests/ui/lint/unaligned_references.rs +++ b/tests/ui/lint/unaligned_references.rs @@ -1,5 +1,9 @@ -use std::mem::ManuallyDrop; +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + use std::fmt::Debug; +use std::mem::ManuallyDrop; #[repr(packed)] pub struct Good { @@ -50,6 +54,26 @@ fn packed_dyn() { println!("{:?}", &*foo.0); // no error! } +// Test for #115396 +fn packed_slice_behind_alias() { + trait Mirror { + type Assoc: ?Sized; + } + impl Mirror for T { + type Assoc = T; + } + + struct W(::Assoc); + + #[repr(packed)] + struct Unaligned(ManuallyDrop>); + + // Even if the actual alignment is 1, we cannot know that when looking at `dyn Debug.` + let ref local: Unaligned<[_; 3]> = Unaligned(ManuallyDrop::new(W([3, 5, 8u8]))); + let foo: &Unaligned<[u8]> = local; + let x = &foo.0; // Fine, since the tail of `foo` is `[_]` +} + fn main() { unsafe { let good = Good { data: 0, ptr: &0, data2: [0, 0], aligned: [0; 32] }; diff --git a/tests/ui/never_type/never-result.rs b/tests/ui/never_type/never-result.rs index bdd06ec5bd13..98ad14046662 100644 --- a/tests/ui/never_type/never-result.rs +++ b/tests/ui/never_type/never-result.rs @@ -2,9 +2,8 @@ #![allow(unused_variables)] #![allow(unreachable_code)] - +#![allow(unreachable_patterns)] // Test that we can extract a ! through pattern matching then use it as several different types. - #![feature(never_type)] fn main() { @@ -16,6 +15,6 @@ fn main() { let w: i32 = y; let e: String = y; y - }, + } } } diff --git a/tests/ui/offset-of/offset-of-slice-normalized.rs b/tests/ui/offset-of/offset-of-slice-normalized.rs new file mode 100644 index 000000000000..9d1fd9dd2ee1 --- /dev/null +++ b/tests/ui/offset-of/offset-of-slice-normalized.rs @@ -0,0 +1,37 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ run-pass + +#![feature(offset_of_slice)] + +use std::mem::offset_of; + +trait Mirror { + type Assoc: ?Sized; +} +impl Mirror for T { + type Assoc = T; +} + +#[repr(C)] +struct S { + a: u8, + b: (u8, u8), + c: <[i32] as Mirror>::Assoc, +} + +#[repr(C)] +struct T { + x: i8, + y: S, +} + +type Tup = (i16, <[i32] as Mirror>::Assoc); + +fn main() { + assert_eq!(offset_of!(S, c), 4); + assert_eq!(offset_of!(T, y), 4); + assert_eq!(offset_of!(T, y.c), 8); + assert_eq!(offset_of!(Tup, 1), 4); +} diff --git a/tests/ui/parser/suggest-remove-compount-assign-let-ice.rs b/tests/ui/parser/suggest-remove-compount-assign-let-ice.rs new file mode 100644 index 000000000000..1affee5678e4 --- /dev/null +++ b/tests/ui/parser/suggest-remove-compount-assign-let-ice.rs @@ -0,0 +1,16 @@ +//! Previously we would try to issue a suggestion for `let x = 1`, i.e. a compound assignment +//! within a `let` binding, to remove the ``. The suggestion code unfortunately incorrectly +//! assumed that the `` is an exactly-1-byte ASCII character, but this assumption is incorrect +//! because we also recover Unicode-confusables like `➖=` as `-=`. In this example, the suggestion +//! code used a `+ BytePos(1)` to calculate the span of the `` codepoint that looks like `-` but +//! the mult-byte Unicode look-alike would cause the suggested removal span to be inside a +//! multi-byte codepoint boundary, triggering a codepoint boundary assertion. +//! +//! issue: rust-lang/rust#128845 + +fn main() { + // Adapted from #128845 but with irrelevant components removed and simplified. + let x ➖= 1; + //~^ ERROR unknown start of token: \u{2796} + //~| ERROR: can't reassign to an uninitialized variable +} diff --git a/tests/ui/parser/suggest-remove-compount-assign-let-ice.stderr b/tests/ui/parser/suggest-remove-compount-assign-let-ice.stderr new file mode 100644 index 000000000000..59716d69b50f --- /dev/null +++ b/tests/ui/parser/suggest-remove-compount-assign-let-ice.stderr @@ -0,0 +1,26 @@ +error: unknown start of token: \u{2796} + --> $DIR/suggest-remove-compount-assign-let-ice.rs:13:11 + | +LL | let x ➖= 1; + | ^^ + | +help: Unicode character '➖' (Heavy Minus Sign) looks like '-' (Minus/Hyphen), but it is not + | +LL | let x -= 1; + | ~ + +error: can't reassign to an uninitialized variable + --> $DIR/suggest-remove-compount-assign-let-ice.rs:13:11 + | +LL | let x ➖= 1; + | ^^^ + | + = help: if you meant to overwrite, remove the `let` binding +help: initialize the variable + | +LL - let x ➖= 1; +LL + let x = 1; + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/pattern/usefulness/always-inhabited-union-ref.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/always-inhabited-union-ref.exhaustive_patterns.stderr index d6304a0b997d..bc74069b3e77 100644 --- a/tests/ui/pattern/usefulness/always-inhabited-union-ref.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/always-inhabited-union-ref.exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `&!` is non-empty - --> $DIR/always-inhabited-union-ref.rs:25:11 + --> $DIR/always-inhabited-union-ref.rs:24:11 | LL | match uninhab_ref() { | ^^^^^^^^^^^^^ @@ -14,13 +14,13 @@ LL + } | error[E0004]: non-exhaustive patterns: type `Foo` is non-empty - --> $DIR/always-inhabited-union-ref.rs:29:11 + --> $DIR/always-inhabited-union-ref.rs:28:11 | LL | match uninhab_union() { | ^^^^^^^^^^^^^^^ | note: `Foo` defined here - --> $DIR/always-inhabited-union-ref.rs:12:11 + --> $DIR/always-inhabited-union-ref.rs:11:11 | LL | pub union Foo { | ^^^ diff --git a/tests/ui/pattern/usefulness/always-inhabited-union-ref.min_exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/always-inhabited-union-ref.normal.stderr similarity index 87% rename from tests/ui/pattern/usefulness/always-inhabited-union-ref.min_exhaustive_patterns.stderr rename to tests/ui/pattern/usefulness/always-inhabited-union-ref.normal.stderr index d6304a0b997d..bc74069b3e77 100644 --- a/tests/ui/pattern/usefulness/always-inhabited-union-ref.min_exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/always-inhabited-union-ref.normal.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `&!` is non-empty - --> $DIR/always-inhabited-union-ref.rs:25:11 + --> $DIR/always-inhabited-union-ref.rs:24:11 | LL | match uninhab_ref() { | ^^^^^^^^^^^^^ @@ -14,13 +14,13 @@ LL + } | error[E0004]: non-exhaustive patterns: type `Foo` is non-empty - --> $DIR/always-inhabited-union-ref.rs:29:11 + --> $DIR/always-inhabited-union-ref.rs:28:11 | LL | match uninhab_union() { | ^^^^^^^^^^^^^^^ | note: `Foo` defined here - --> $DIR/always-inhabited-union-ref.rs:12:11 + --> $DIR/always-inhabited-union-ref.rs:11:11 | LL | pub union Foo { | ^^^ diff --git a/tests/ui/pattern/usefulness/always-inhabited-union-ref.rs b/tests/ui/pattern/usefulness/always-inhabited-union-ref.rs index 5088098d0ae3..335eff425abe 100644 --- a/tests/ui/pattern/usefulness/always-inhabited-union-ref.rs +++ b/tests/ui/pattern/usefulness/always-inhabited-union-ref.rs @@ -1,10 +1,9 @@ -//@ revisions: min_exhaustive_patterns exhaustive_patterns +//@ revisions: normal exhaustive_patterns // The precise semantics of inhabitedness with respect to unions and references is currently // undecided. This test file currently checks a conservative choice. #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))] -#![cfg_attr(min_exhaustive_patterns, feature(min_exhaustive_patterns))] #![feature(never_type)] #![allow(dead_code)] #![allow(unreachable_code)] diff --git a/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr index 9e700ee55ef5..1b65ff7aa575 100644 --- a/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-match-check-notes.exhaustive_patterns.stderr @@ -38,7 +38,7 @@ LL | _ if false => {} error[E0005]: refutable pattern in local binding --> $DIR/empty-match-check-notes.rs:39:9 | -LL | let None = x; +LL | let None = *x; | ^^^^ pattern `Some(_)` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant @@ -47,8 +47,8 @@ LL | let None = x; = note: the matched value is of type `Option` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let None = x { todo!() }; - | ++ +++++++++++ +LL | if let None = *x { todo!() }; + | ++ +++++++++++ error[E0004]: non-exhaustive patterns: `0_u8..=u8::MAX` not covered --> $DIR/empty-match-check-notes.rs:49:11 diff --git a/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr b/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr index 480ae7095a6c..1b65ff7aa575 100644 --- a/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-match-check-notes.normal.stderr @@ -38,16 +38,17 @@ LL | _ if false => {} error[E0005]: refutable pattern in local binding --> $DIR/empty-match-check-notes.rs:39:9 | -LL | let None = x; +LL | let None = *x; | ^^^^ pattern `Some(_)` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: pattern `Some(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future = note: the matched value is of type `Option` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let None = x { todo!() }; - | ++ +++++++++++ +LL | if let None = *x { todo!() }; + | ++ +++++++++++ error[E0004]: non-exhaustive patterns: `0_u8..=u8::MAX` not covered --> $DIR/empty-match-check-notes.rs:49:11 diff --git a/tests/ui/pattern/usefulness/empty-match-check-notes.rs b/tests/ui/pattern/usefulness/empty-match-check-notes.rs index 2eef283a21e4..61a75e6c801f 100644 --- a/tests/ui/pattern/usefulness/empty-match-check-notes.rs +++ b/tests/ui/pattern/usefulness/empty-match-check-notes.rs @@ -35,14 +35,14 @@ fn empty_foreign_enum(x: empty::EmptyForeignEnum) { } } -fn empty_foreign_enum_private(x: Option) { - let None = x; +fn empty_foreign_enum_private(x: &Option) { + let None = *x; //~^ ERROR refutable pattern in local binding //~| NOTE `let` bindings require an "irrefutable pattern" //~| NOTE for more information, visit //~| NOTE the matched value is of type //~| NOTE pattern `Some(_)` not covered - //[exhaustive_patterns]~| NOTE currently uninhabited, but this variant contains private fields + //~| NOTE currently uninhabited, but this variant contains private fields } fn main() { diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr index 5f895fab0fba..f2067f0341fc 100644 --- a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/empty-match.rs:46:20 + --> $DIR/empty-match.rs:47:20 | LL | match_no_arms!(0u8); | ^^^ @@ -8,7 +8,7 @@ LL | match_no_arms!(0u8); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `i8` is non-empty - --> $DIR/empty-match.rs:47:20 + --> $DIR/empty-match.rs:48:20 | LL | match_no_arms!(0i8); | ^^^ @@ -17,7 +17,7 @@ LL | match_no_arms!(0i8); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `usize` is non-empty - --> $DIR/empty-match.rs:48:20 + --> $DIR/empty-match.rs:49:20 | LL | match_no_arms!(0usize); | ^^^^^^ @@ -26,7 +26,7 @@ LL | match_no_arms!(0usize); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `isize` is non-empty - --> $DIR/empty-match.rs:49:20 + --> $DIR/empty-match.rs:50:20 | LL | match_no_arms!(0isize); | ^^^^^^ @@ -35,7 +35,7 @@ LL | match_no_arms!(0isize); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty - --> $DIR/empty-match.rs:50:20 + --> $DIR/empty-match.rs:51:20 | LL | match_no_arms!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | struct NonEmptyStruct1; = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty - --> $DIR/empty-match.rs:51:20 + --> $DIR/empty-match.rs:52:20 | LL | match_no_arms!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | struct NonEmptyStruct2(bool); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/empty-match.rs:52:20 + --> $DIR/empty-match.rs:53:20 | LL | match_no_arms!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | union NonEmptyUnion1 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/empty-match.rs:53:20 + --> $DIR/empty-match.rs:54:20 | LL | match_no_arms!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | union NonEmptyUnion2 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:54:20 + --> $DIR/empty-match.rs:55:20 | LL | match_no_arms!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -107,7 +107,7 @@ LL | Foo(bool), = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:55:20 + --> $DIR/empty-match.rs:56:20 | LL | match_no_arms!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -125,7 +125,7 @@ LL | Bar, = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:56:20 + --> $DIR/empty-match.rs:57:20 | LL | match_no_arms!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered @@ -148,8 +148,26 @@ LL | V5, = note: the matched value is of type `NonEmptyEnum5` = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms +error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty + --> $DIR/empty-match.rs:58:20 + | +LL | match_no_arms!(array0_of_empty); + | ^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `[!; 0]` + = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern + +error[E0004]: non-exhaustive patterns: type `[!; N]` is non-empty + --> $DIR/empty-match.rs:59:20 + | +LL | match_no_arms!(arrayN_of_empty); + | ^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `[!; N]` + = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern + error[E0004]: non-exhaustive patterns: `0_u8..=u8::MAX` not covered - --> $DIR/empty-match.rs:58:24 + --> $DIR/empty-match.rs:61:24 | LL | match_guarded_arm!(0u8); | ^^^ pattern `0_u8..=u8::MAX` not covered @@ -163,7 +181,7 @@ LL + 0_u8..=u8::MAX => todo!() | error[E0004]: non-exhaustive patterns: `i8::MIN..=i8::MAX` not covered - --> $DIR/empty-match.rs:59:24 + --> $DIR/empty-match.rs:62:24 | LL | match_guarded_arm!(0i8); | ^^^ pattern `i8::MIN..=i8::MAX` not covered @@ -177,7 +195,7 @@ LL + i8::MIN..=i8::MAX => todo!() | error[E0004]: non-exhaustive patterns: `0_usize..` not covered - --> $DIR/empty-match.rs:60:24 + --> $DIR/empty-match.rs:63:24 | LL | match_guarded_arm!(0usize); | ^^^^^^ pattern `0_usize..` not covered @@ -191,7 +209,7 @@ LL + 0_usize.. => todo!() | error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/empty-match.rs:61:24 + --> $DIR/empty-match.rs:64:24 | LL | match_guarded_arm!(0isize); | ^^^^^^ pattern `_` not covered @@ -205,7 +223,7 @@ LL + _ => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered - --> $DIR/empty-match.rs:62:24 + --> $DIR/empty-match.rs:65:24 | LL | match_guarded_arm!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered @@ -224,7 +242,7 @@ LL + NonEmptyStruct1 => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered - --> $DIR/empty-match.rs:63:24 + --> $DIR/empty-match.rs:66:24 | LL | match_guarded_arm!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered @@ -243,7 +261,7 @@ LL + NonEmptyStruct2(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/empty-match.rs:64:24 + --> $DIR/empty-match.rs:67:24 | LL | match_guarded_arm!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered @@ -262,7 +280,7 @@ LL + NonEmptyUnion1 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/empty-match.rs:65:24 + --> $DIR/empty-match.rs:68:24 | LL | match_guarded_arm!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered @@ -273,6 +291,7 @@ note: `NonEmptyUnion2` defined here LL | union NonEmptyUnion2 { | ^^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyUnion2` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | @@ -281,7 +300,7 @@ LL + NonEmptyUnion2 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:66:24 + --> $DIR/empty-match.rs:69:24 | LL | match_guarded_arm!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -302,7 +321,7 @@ LL + NonEmptyEnum1::Foo(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:67:24 + --> $DIR/empty-match.rs:70:24 | LL | match_guarded_arm!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -325,7 +344,7 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:68:24 + --> $DIR/empty-match.rs:71:24 | LL | match_guarded_arm!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered @@ -353,6 +372,34 @@ LL ~ _ if false => {}, LL + _ => todo!() | -error: aborting due to 22 previous errors +error[E0004]: non-exhaustive patterns: `[]` not covered + --> $DIR/empty-match.rs:72:24 + | +LL | match_guarded_arm!(array0_of_empty); + | ^^^^^^^^^^^^^^^ pattern `[]` not covered + | + = note: the matched value is of type `[!; 0]` + = note: match arms with guards don't count towards exhaustivity +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 ~ _ if false => {}, +LL + [] => todo!() + | + +error[E0004]: non-exhaustive patterns: `[]` not covered + --> $DIR/empty-match.rs:73:24 + | +LL | match_guarded_arm!(arrayN_of_empty); + | ^^^^^^^^^^^^^^^ pattern `[]` not covered + | + = note: the matched value is of type `[!; N]` + = note: match arms with guards don't count towards exhaustivity +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 ~ _ if false => {}, +LL + [] => todo!() + | + +error: aborting due to 26 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-match.normal.stderr b/tests/ui/pattern/usefulness/empty-match.normal.stderr index 5f895fab0fba..f2067f0341fc 100644 --- a/tests/ui/pattern/usefulness/empty-match.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `u8` is non-empty - --> $DIR/empty-match.rs:46:20 + --> $DIR/empty-match.rs:47:20 | LL | match_no_arms!(0u8); | ^^^ @@ -8,7 +8,7 @@ LL | match_no_arms!(0u8); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `i8` is non-empty - --> $DIR/empty-match.rs:47:20 + --> $DIR/empty-match.rs:48:20 | LL | match_no_arms!(0i8); | ^^^ @@ -17,7 +17,7 @@ LL | match_no_arms!(0i8); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `usize` is non-empty - --> $DIR/empty-match.rs:48:20 + --> $DIR/empty-match.rs:49:20 | LL | match_no_arms!(0usize); | ^^^^^^ @@ -26,7 +26,7 @@ LL | match_no_arms!(0usize); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `isize` is non-empty - --> $DIR/empty-match.rs:49:20 + --> $DIR/empty-match.rs:50:20 | LL | match_no_arms!(0isize); | ^^^^^^ @@ -35,7 +35,7 @@ LL | match_no_arms!(0isize); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty - --> $DIR/empty-match.rs:50:20 + --> $DIR/empty-match.rs:51:20 | LL | match_no_arms!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | struct NonEmptyStruct1; = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty - --> $DIR/empty-match.rs:51:20 + --> $DIR/empty-match.rs:52:20 | LL | match_no_arms!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | struct NonEmptyStruct2(bool); = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty - --> $DIR/empty-match.rs:52:20 + --> $DIR/empty-match.rs:53:20 | LL | match_no_arms!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | union NonEmptyUnion1 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty - --> $DIR/empty-match.rs:53:20 + --> $DIR/empty-match.rs:54:20 | LL | match_no_arms!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | union NonEmptyUnion2 { = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:54:20 + --> $DIR/empty-match.rs:55:20 | LL | match_no_arms!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -107,7 +107,7 @@ LL | Foo(bool), = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:55:20 + --> $DIR/empty-match.rs:56:20 | LL | match_no_arms!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -125,7 +125,7 @@ LL | Bar, = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:56:20 + --> $DIR/empty-match.rs:57:20 | LL | match_no_arms!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered @@ -148,8 +148,26 @@ LL | V5, = note: the matched value is of type `NonEmptyEnum5` = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms +error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty + --> $DIR/empty-match.rs:58:20 + | +LL | match_no_arms!(array0_of_empty); + | ^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `[!; 0]` + = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern + +error[E0004]: non-exhaustive patterns: type `[!; N]` is non-empty + --> $DIR/empty-match.rs:59:20 + | +LL | match_no_arms!(arrayN_of_empty); + | ^^^^^^^^^^^^^^^ + | + = note: the matched value is of type `[!; N]` + = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern + error[E0004]: non-exhaustive patterns: `0_u8..=u8::MAX` not covered - --> $DIR/empty-match.rs:58:24 + --> $DIR/empty-match.rs:61:24 | LL | match_guarded_arm!(0u8); | ^^^ pattern `0_u8..=u8::MAX` not covered @@ -163,7 +181,7 @@ LL + 0_u8..=u8::MAX => todo!() | error[E0004]: non-exhaustive patterns: `i8::MIN..=i8::MAX` not covered - --> $DIR/empty-match.rs:59:24 + --> $DIR/empty-match.rs:62:24 | LL | match_guarded_arm!(0i8); | ^^^ pattern `i8::MIN..=i8::MAX` not covered @@ -177,7 +195,7 @@ LL + i8::MIN..=i8::MAX => todo!() | error[E0004]: non-exhaustive patterns: `0_usize..` not covered - --> $DIR/empty-match.rs:60:24 + --> $DIR/empty-match.rs:63:24 | LL | match_guarded_arm!(0usize); | ^^^^^^ pattern `0_usize..` not covered @@ -191,7 +209,7 @@ LL + 0_usize.. => todo!() | error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/empty-match.rs:61:24 + --> $DIR/empty-match.rs:64:24 | LL | match_guarded_arm!(0isize); | ^^^^^^ pattern `_` not covered @@ -205,7 +223,7 @@ LL + _ => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered - --> $DIR/empty-match.rs:62:24 + --> $DIR/empty-match.rs:65:24 | LL | match_guarded_arm!(NonEmptyStruct1); | ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered @@ -224,7 +242,7 @@ LL + NonEmptyStruct1 => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered - --> $DIR/empty-match.rs:63:24 + --> $DIR/empty-match.rs:66:24 | LL | match_guarded_arm!(NonEmptyStruct2(true)); | ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered @@ -243,7 +261,7 @@ LL + NonEmptyStruct2(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered - --> $DIR/empty-match.rs:64:24 + --> $DIR/empty-match.rs:67:24 | LL | match_guarded_arm!((NonEmptyUnion1 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered @@ -262,7 +280,7 @@ LL + NonEmptyUnion1 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered - --> $DIR/empty-match.rs:65:24 + --> $DIR/empty-match.rs:68:24 | LL | match_guarded_arm!((NonEmptyUnion2 { foo: () })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered @@ -273,6 +291,7 @@ note: `NonEmptyUnion2` defined here LL | union NonEmptyUnion2 { | ^^^^^^^^^^^^^^ = note: the matched value is of type `NonEmptyUnion2` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | @@ -281,7 +300,7 @@ LL + NonEmptyUnion2 { .. } => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered - --> $DIR/empty-match.rs:66:24 + --> $DIR/empty-match.rs:69:24 | LL | match_guarded_arm!(NonEmptyEnum1::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered @@ -302,7 +321,7 @@ LL + NonEmptyEnum1::Foo(_) => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered - --> $DIR/empty-match.rs:67:24 + --> $DIR/empty-match.rs:70:24 | LL | match_guarded_arm!(NonEmptyEnum2::Foo(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered @@ -325,7 +344,7 @@ LL + NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!() | error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered - --> $DIR/empty-match.rs:68:24 + --> $DIR/empty-match.rs:71:24 | LL | match_guarded_arm!(NonEmptyEnum5::V1); | ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered @@ -353,6 +372,34 @@ LL ~ _ if false => {}, LL + _ => todo!() | -error: aborting due to 22 previous errors +error[E0004]: non-exhaustive patterns: `[]` not covered + --> $DIR/empty-match.rs:72:24 + | +LL | match_guarded_arm!(array0_of_empty); + | ^^^^^^^^^^^^^^^ pattern `[]` not covered + | + = note: the matched value is of type `[!; 0]` + = note: match arms with guards don't count towards exhaustivity +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 ~ _ if false => {}, +LL + [] => todo!() + | + +error[E0004]: non-exhaustive patterns: `[]` not covered + --> $DIR/empty-match.rs:73:24 + | +LL | match_guarded_arm!(arrayN_of_empty); + | ^^^^^^^^^^^^^^^ pattern `[]` not covered + | + = note: the matched value is of type `[!; N]` + = note: match arms with guards don't count towards exhaustivity +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 ~ _ if false => {}, +LL + [] => todo!() + | + +error: aborting due to 26 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-match.rs b/tests/ui/pattern/usefulness/empty-match.rs index 9b22b47a12b1..b34427a7c238 100644 --- a/tests/ui/pattern/usefulness/empty-match.rs +++ b/tests/ui/pattern/usefulness/empty-match.rs @@ -5,7 +5,7 @@ #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))] #![deny(unreachable_patterns)] -fn nonempty() { +fn nonempty(arrayN_of_empty: [!; N]) { macro_rules! match_no_arms { ($e:expr) => { match $e {} @@ -42,6 +42,7 @@ fn nonempty() { V4, V5, } + let array0_of_empty: [!; 0] = []; match_no_arms!(0u8); //~ ERROR type `u8` is non-empty match_no_arms!(0i8); //~ ERROR type `i8` is non-empty @@ -54,6 +55,8 @@ fn nonempty() { match_no_arms!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered match_no_arms!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered match_no_arms!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered + match_no_arms!(array0_of_empty); //~ ERROR type `[!; 0]` is non-empty + match_no_arms!(arrayN_of_empty); //~ ERROR type `[!; N]` is non-empty match_guarded_arm!(0u8); //~ ERROR `0_u8..=u8::MAX` not covered match_guarded_arm!(0i8); //~ ERROR `i8::MIN..=i8::MAX` not covered @@ -66,6 +69,8 @@ fn nonempty() { match_guarded_arm!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered match_guarded_arm!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered + match_guarded_arm!(array0_of_empty); //~ ERROR `[]` not covered + match_guarded_arm!(arrayN_of_empty); //~ ERROR `[]` not covered } fn main() {} diff --git a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr index 416a50b87b59..17cb6fbd94b8 100644 --- a/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/empty-types.exhaustive_patterns.stderr @@ -1,18 +1,18 @@ error: unreachable pattern - --> $DIR/empty-types.rs:51:9 + --> $DIR/empty-types.rs:49:9 | LL | _ => {} | ^ | = note: this pattern matches no values because `!` is uninhabited note: the lint level is defined here - --> $DIR/empty-types.rs:17:9 + --> $DIR/empty-types.rs:15:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/empty-types.rs:54:9 + --> $DIR/empty-types.rs:52:9 | LL | _x => {} | ^^ @@ -20,7 +20,7 @@ LL | _x => {} = note: this pattern matches no values because `!` is uninhabited error[E0004]: non-exhaustive patterns: type `&!` is non-empty - --> $DIR/empty-types.rs:58:11 + --> $DIR/empty-types.rs:56:11 | LL | match ref_never {} | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:73:9 + --> $DIR/empty-types.rs:70:9 | LL | (_, _) => {} | ^^^^^^ @@ -43,7 +43,7 @@ LL | (_, _) => {} = note: this pattern matches no values because `(u32, !)` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:80:9 + --> $DIR/empty-types.rs:76:9 | LL | _ => {} | ^ @@ -51,7 +51,7 @@ LL | _ => {} = note: this pattern matches no values because `(!, !)` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:83:9 + --> $DIR/empty-types.rs:79:9 | LL | (_, _) => {} | ^^^^^^ @@ -59,7 +59,7 @@ LL | (_, _) => {} = note: this pattern matches no values because `(!, !)` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:87:9 + --> $DIR/empty-types.rs:83:9 | LL | _ => {} | ^ @@ -67,7 +67,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error[E0004]: non-exhaustive patterns: `Ok(_)` not covered - --> $DIR/empty-types.rs:91:11 + --> $DIR/empty-types.rs:87:11 | LL | match res_u32_never {} | ^^^^^^^^^^^^^ pattern `Ok(_)` not covered @@ -86,7 +86,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:99:9 + --> $DIR/empty-types.rs:94:9 | LL | Err(_) => {} | ^^^^^^ @@ -94,7 +94,7 @@ LL | Err(_) => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:104:9 + --> $DIR/empty-types.rs:99:9 | LL | Err(_) => {} | ^^^^^^ @@ -102,7 +102,7 @@ LL | Err(_) => {} = note: this pattern matches no values because `!` is uninhabited error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered - --> $DIR/empty-types.rs:101:11 + --> $DIR/empty-types.rs:96:11 | LL | match res_u32_never { | ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered @@ -120,7 +120,7 @@ LL ~ Ok(1_u32..=u32::MAX) => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:108:9 + --> $DIR/empty-types.rs:102:9 | LL | let Ok(_x) = res_u32_never.as_ref(); | ^^^^^^ pattern `Err(_)` not covered @@ -133,6 +133,30 @@ help: you might want to use `let else` to handle the variant that isn't matched LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() }; | ++++++++++++++++ +error: unreachable pattern + --> $DIR/empty-types.rs:112:9 + | +LL | _ => {} + | ^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:115:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:118:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + error: unreachable pattern --> $DIR/empty-types.rs:119:9 | @@ -141,48 +165,24 @@ LL | _ => {} | = note: this pattern matches no values because `Result` is uninhabited +error: unreachable pattern + --> $DIR/empty-types.rs:122:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + error: unreachable pattern --> $DIR/empty-types.rs:123:9 | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:126:9 - | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:127:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:130:9 - | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:131:9 - | LL | Err(_) => {} | ^^^^^^ | = note: this pattern matches no values because `Result` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:140:13 + --> $DIR/empty-types.rs:132:13 | LL | _ => {} | ^ @@ -190,7 +190,7 @@ LL | _ => {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:143:13 + --> $DIR/empty-types.rs:135:13 | LL | _ if false => {} | ^ @@ -198,7 +198,7 @@ LL | _ if false => {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:152:13 + --> $DIR/empty-types.rs:143:13 | LL | Some(_) => {} | ^^^^^^^ @@ -206,7 +206,7 @@ LL | Some(_) => {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:156:13 + --> $DIR/empty-types.rs:147:13 | LL | None => {} | ---- matches all the values already @@ -214,7 +214,7 @@ LL | _ => {} | ^ unreachable pattern error: unreachable pattern - --> $DIR/empty-types.rs:208:13 + --> $DIR/empty-types.rs:199:13 | LL | _ => {} | ^ @@ -222,7 +222,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:213:13 + --> $DIR/empty-types.rs:204:13 | LL | _ => {} | ^ @@ -230,7 +230,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:218:13 + --> $DIR/empty-types.rs:209:13 | LL | _ => {} | ^ @@ -238,7 +238,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:223:13 + --> $DIR/empty-types.rs:214:13 | LL | _ => {} | ^ @@ -246,7 +246,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:229:13 + --> $DIR/empty-types.rs:220:13 | LL | _ => {} | ^ @@ -254,7 +254,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:288:9 + --> $DIR/empty-types.rs:279:9 | LL | _ => {} | ^ @@ -262,7 +262,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:291:9 + --> $DIR/empty-types.rs:282:9 | LL | (_, _) => {} | ^^^^^^ @@ -270,7 +270,7 @@ LL | (_, _) => {} = note: this pattern matches no values because `(!, !)` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:294:9 + --> $DIR/empty-types.rs:285:9 | LL | Ok(_) => {} | ^^^^^ @@ -278,7 +278,7 @@ LL | Ok(_) => {} = note: this pattern matches no values because `Result` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:295:9 + --> $DIR/empty-types.rs:286:9 | LL | Err(_) => {} | ^^^^^^ @@ -286,7 +286,7 @@ LL | Err(_) => {} = note: this pattern matches no values because `Result` is uninhabited error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty - --> $DIR/empty-types.rs:327:11 + --> $DIR/empty-types.rs:318:11 | LL | match slice_never {} | ^^^^^^^^^^^ @@ -300,7 +300,7 @@ LL + } | error[E0004]: non-exhaustive patterns: `&[]` not covered - --> $DIR/empty-types.rs:338:11 + --> $DIR/empty-types.rs:329:11 | LL | match slice_never { | ^^^^^^^^^^^ pattern `&[]` not covered @@ -313,7 +313,7 @@ LL + &[] => todo!() | error[E0004]: non-exhaustive patterns: `&[]` not covered - --> $DIR/empty-types.rs:352:11 + --> $DIR/empty-types.rs:343:11 | LL | match slice_never { | ^^^^^^^^^^^ pattern `&[]` not covered @@ -327,7 +327,7 @@ LL + &[] => todo!() | error[E0004]: non-exhaustive patterns: type `[!]` is non-empty - --> $DIR/empty-types.rs:359:11 + --> $DIR/empty-types.rs:350:11 | LL | match *slice_never {} | ^^^^^^^^^^^^ @@ -341,7 +341,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:369:9 + --> $DIR/empty-types.rs:359:9 | LL | _ => {} | ^ @@ -349,7 +349,7 @@ LL | _ => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:372:9 + --> $DIR/empty-types.rs:362:9 | LL | [_, _, _] => {} | ^^^^^^^^^ @@ -357,7 +357,7 @@ LL | [_, _, _] => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:375:9 + --> $DIR/empty-types.rs:365:9 | LL | [_, ..] => {} | ^^^^^^^ @@ -365,7 +365,7 @@ LL | [_, ..] => {} = note: this pattern matches no values because `[!; 3]` is uninhabited error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty - --> $DIR/empty-types.rs:389:11 + --> $DIR/empty-types.rs:379:11 | LL | match array_0_never {} | ^^^^^^^^^^^^^ @@ -379,7 +379,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:396:9 + --> $DIR/empty-types.rs:386:9 | LL | [] => {} | -- matches all the values already @@ -387,7 +387,7 @@ LL | _ => {} | ^ unreachable pattern error[E0004]: non-exhaustive patterns: `[]` not covered - --> $DIR/empty-types.rs:398:11 + --> $DIR/empty-types.rs:388:11 | LL | match array_0_never { | ^^^^^^^^^^^^^ pattern `[]` not covered @@ -401,7 +401,7 @@ LL + [] => todo!() | error: unreachable pattern - --> $DIR/empty-types.rs:417:9 + --> $DIR/empty-types.rs:407:9 | LL | Some(_) => {} | ^^^^^^^ @@ -409,7 +409,7 @@ LL | Some(_) => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:422:9 + --> $DIR/empty-types.rs:412:9 | LL | Some(_a) => {} | ^^^^^^^^ @@ -417,7 +417,7 @@ LL | Some(_a) => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:427:9 + --> $DIR/empty-types.rs:417:9 | LL | None => {} | ---- matches all the values already @@ -426,7 +426,7 @@ LL | _ => {} | ^ unreachable pattern error: unreachable pattern - --> $DIR/empty-types.rs:432:9 + --> $DIR/empty-types.rs:422:9 | LL | None => {} | ---- matches all the values already @@ -435,7 +435,7 @@ LL | _a => {} | ^^ unreachable pattern error: unreachable pattern - --> $DIR/empty-types.rs:604:9 + --> $DIR/empty-types.rs:594:9 | LL | _ => {} | ^ @@ -443,7 +443,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:607:9 + --> $DIR/empty-types.rs:597:9 | LL | _x => {} | ^^ @@ -451,7 +451,7 @@ LL | _x => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:610:9 + --> $DIR/empty-types.rs:600:9 | LL | _ if false => {} | ^ @@ -459,7 +459,7 @@ LL | _ if false => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:613:9 + --> $DIR/empty-types.rs:603:9 | LL | _x if false => {} | ^^ diff --git a/tests/ui/pattern/usefulness/empty-types.never_pats.stderr b/tests/ui/pattern/usefulness/empty-types.never_pats.stderr index 4856a2f8e08d..1ecb15f2cae3 100644 --- a/tests/ui/pattern/usefulness/empty-types.never_pats.stderr +++ b/tests/ui/pattern/usefulness/empty-types.never_pats.stderr @@ -1,5 +1,5 @@ warning: the feature `never_patterns` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/empty-types.rs:14:33 + --> $DIR/empty-types.rs:12:33 | LL | #![cfg_attr(never_pats, feature(never_patterns))] | ^^^^^^^^^^^^^^ @@ -8,20 +8,20 @@ LL | #![cfg_attr(never_pats, feature(never_patterns))] = note: `#[warn(incomplete_features)]` on by default error: unreachable pattern - --> $DIR/empty-types.rs:51:9 + --> $DIR/empty-types.rs:49:9 | LL | _ => {} | ^ | = note: this pattern matches no values because `!` is uninhabited note: the lint level is defined here - --> $DIR/empty-types.rs:17:9 + --> $DIR/empty-types.rs:15:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/empty-types.rs:54:9 + --> $DIR/empty-types.rs:52:9 | LL | _x => {} | ^^ @@ -29,7 +29,7 @@ LL | _x => {} = note: this pattern matches no values because `!` is uninhabited error[E0004]: non-exhaustive patterns: type `&!` is non-empty - --> $DIR/empty-types.rs:58:11 + --> $DIR/empty-types.rs:56:11 | LL | match ref_never {} | ^^^^^^^^^ @@ -43,69 +43,43 @@ LL + _ => todo!(), LL + } | -error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:70:11 +error: unreachable pattern + --> $DIR/empty-types.rs:70:9 | -LL | match tuple_half_never {} - | ^^^^^^^^^^^^^^^^ - | - = note: the matched value is of type `(u32, !)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match tuple_half_never { -LL + _ => todo!(), -LL + } - | - -error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty - --> $DIR/empty-types.rs:77:11 - | -LL | match tuple_never {} - | ^^^^^^^^^^^ - | - = note: the matched value is of type `(!, !)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match tuple_never { -LL + _ => todo!(), -LL + } +LL | (_, _) => {} + | ^^^^^^ | + = note: this pattern matches no values because `(u32, !)` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:87:9 + --> $DIR/empty-types.rs:76:9 + | +LL | _ => {} + | ^ + | + = note: this pattern matches no values because `(!, !)` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:79:9 + | +LL | (_, _) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `(!, !)` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:83:9 | LL | _ => {} | ^ | = note: this pattern matches no values because `!` is uninhabited -error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(!)` not covered - --> $DIR/empty-types.rs:91:11 +error[E0004]: non-exhaustive patterns: `Ok(_)` not covered + --> $DIR/empty-types.rs:87:11 | LL | match res_u32_never {} - | ^^^^^^^^^^^^^ patterns `Ok(_)` and `Err(!)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ match res_u32_never { -LL + Ok(_) | Err(!) => todo!(), -LL + } - | - -error[E0004]: non-exhaustive patterns: `Err(!)` not covered - --> $DIR/empty-types.rs:93:11 - | -LL | match res_u32_never { - | ^^^^^^^^^^^^^ pattern `Err(!)` not covered + | ^^^^^^^^^^^^^ pattern `Ok(_)` not covered | note: `Result` defined here --> $SRC_DIR/core/src/result.rs:LL:COL @@ -115,12 +89,29 @@ note: `Result` defined here = note: the matched value is of type `Result` 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 ~ Ok(_) => {}, -LL + Err(!) +LL ~ match res_u32_never { +LL + Ok(_) => todo!(), +LL + } | +error: unreachable pattern + --> $DIR/empty-types.rs:94:9 + | +LL | Err(_) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `!` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:99:9 + | +LL | Err(_) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `!` is uninhabited + error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered - --> $DIR/empty-types.rs:101:11 + --> $DIR/empty-types.rs:96:11 | LL | match res_u32_never { | ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered @@ -138,21 +129,7 @@ LL ~ Ok(1_u32..=u32::MAX) => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:106:9 - | -LL | let Ok(_x) = res_u32_never; - | ^^^^^^ pattern `Err(!)` not covered - | - = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant - = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html - = note: the matched value is of type `Result` -help: you might want to use `let else` to handle the variant that isn't matched - | -LL | let Ok(_x) = res_u32_never else { todo!() }; - | ++++++++++++++++ - -error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:108:9 + --> $DIR/empty-types.rs:102:9 | LL | let Ok(_x) = res_u32_never.as_ref(); | ^^^^^^ pattern `Err(_)` not covered @@ -166,7 +143,7 @@ LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() }; | ++++++++++++++++ error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:112:9 + --> $DIR/empty-types.rs:106:9 | LL | let Ok(_x) = &res_u32_never; | ^^^^^^ pattern `&Err(!)` not covered @@ -179,47 +156,56 @@ help: you might want to use `let else` to handle the variant that isn't matched LL | let Ok(_x) = &res_u32_never else { todo!() }; | ++++++++++++++++ -error[E0004]: non-exhaustive patterns: `Ok(!)` and `Err(!)` not covered - --> $DIR/empty-types.rs:116:11 +error: unreachable pattern + --> $DIR/empty-types.rs:112:9 | -LL | match result_never {} - | ^^^^^^^^^^^^ patterns `Ok(!)` and `Err(!)` not covered +LL | _ => {} + | ^ | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ match result_never { -LL + Ok(!) | Err(!), -LL + } - | - -error[E0004]: non-exhaustive patterns: `Err(!)` not covered - --> $DIR/empty-types.rs:121:11 - | -LL | match result_never { - | ^^^^^^^^^^^^ pattern `Err(!)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -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 | Ok(_) => {}, Err(!) - | ++++++++ + = note: this pattern matches no values because `Result` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:140:13 + --> $DIR/empty-types.rs:115:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:118:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:119:9 + | +LL | _ => {} + | ^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:122:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:123:9 + | +LL | Err(_) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:132:13 | LL | _ => {} | ^ @@ -227,33 +213,31 @@ LL | _ => {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:143:13 + --> $DIR/empty-types.rs:135:13 | LL | _ if false => {} | ^ | = note: this pattern matches no values because `Void` is uninhabited -error[E0004]: non-exhaustive patterns: `Some(!)` not covered - --> $DIR/empty-types.rs:146:15 +error: unreachable pattern + --> $DIR/empty-types.rs:143:13 | -LL | match opt_void { - | ^^^^^^^^ pattern `Some(!)` not covered +LL | Some(_) => {} + | ^^^^^^^ | -note: `Option` 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` -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(!) + = note: this pattern matches no values because `Void` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:147:13 | +LL | None => {} + | ---- matches all the values already +LL | _ => {} + | ^ unreachable pattern error[E0004]: non-exhaustive patterns: `Some(!)` not covered - --> $DIR/empty-types.rs:165:15 + --> $DIR/empty-types.rs:156:15 | LL | match *ref_opt_void { | ^^^^^^^^^^^^^ pattern `Some(!)` not covered @@ -264,6 +248,7 @@ note: `Option` defined here | = note: not covered = note: the matched value is of type `Option` + = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 => {}, @@ -271,7 +256,7 @@ LL + Some(!) | error: unreachable pattern - --> $DIR/empty-types.rs:208:13 + --> $DIR/empty-types.rs:199:13 | LL | _ => {} | ^ @@ -279,7 +264,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:213:13 + --> $DIR/empty-types.rs:204:13 | LL | _ => {} | ^ @@ -287,7 +272,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:218:13 + --> $DIR/empty-types.rs:209:13 | LL | _ => {} | ^ @@ -295,7 +280,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:223:13 + --> $DIR/empty-types.rs:214:13 | LL | _ => {} | ^ @@ -303,7 +288,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:229:13 + --> $DIR/empty-types.rs:220:13 | LL | _ => {} | ^ @@ -311,15 +296,39 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:288:9 + --> $DIR/empty-types.rs:279:9 | LL | _ => {} | ^ | = note: this pattern matches no values because `!` is uninhabited +error: unreachable pattern + --> $DIR/empty-types.rs:282:9 + | +LL | (_, _) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `(!, !)` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:285:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:286:9 + | +LL | Err(_) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:316:11 + --> $DIR/empty-types.rs:307:11 | LL | match *x {} | ^^ @@ -333,7 +342,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty - --> $DIR/empty-types.rs:318:11 + --> $DIR/empty-types.rs:309:11 | LL | match *x {} | ^^ @@ -347,7 +356,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: `Ok(!)` and `Err(!)` not covered - --> $DIR/empty-types.rs:320:11 + --> $DIR/empty-types.rs:311:11 | LL | match *x {} | ^^ patterns `Ok(!)` and `Err(!)` not covered @@ -369,7 +378,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty - --> $DIR/empty-types.rs:322:11 + --> $DIR/empty-types.rs:313:11 | LL | match *x {} | ^^ @@ -383,7 +392,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty - --> $DIR/empty-types.rs:327:11 + --> $DIR/empty-types.rs:318:11 | LL | match slice_never {} | ^^^^^^^^^^^ @@ -397,12 +406,13 @@ LL + } | error[E0004]: non-exhaustive patterns: `&[!, ..]` not covered - --> $DIR/empty-types.rs:329:11 + --> $DIR/empty-types.rs:320:11 | LL | match slice_never { | ^^^^^^^^^^^ pattern `&[!, ..]` not covered | = note: the matched value is of type `&[!]` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 ~ [] => {}, @@ -410,7 +420,7 @@ LL + &[!, ..] | error[E0004]: non-exhaustive patterns: `&[]`, `&[!]` and `&[!, !]` not covered - --> $DIR/empty-types.rs:338:11 + --> $DIR/empty-types.rs:329:11 | LL | match slice_never { | ^^^^^^^^^^^ patterns `&[]`, `&[!]` and `&[!, !]` not covered @@ -423,7 +433,7 @@ LL + &[] | &[!] | &[!, !] => todo!() | error[E0004]: non-exhaustive patterns: `&[]` and `&[!, ..]` not covered - --> $DIR/empty-types.rs:352:11 + --> $DIR/empty-types.rs:343:11 | LL | match slice_never { | ^^^^^^^^^^^ patterns `&[]` and `&[!, ..]` not covered @@ -437,7 +447,7 @@ LL + &[] | &[!, ..] => todo!() | error[E0004]: non-exhaustive patterns: type `[!]` is non-empty - --> $DIR/empty-types.rs:359:11 + --> $DIR/empty-types.rs:350:11 | LL | match *slice_never {} | ^^^^^^^^^^^^ @@ -450,22 +460,32 @@ LL + _ => todo!(), LL + } | -error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty - --> $DIR/empty-types.rs:366:11 +error: unreachable pattern + --> $DIR/empty-types.rs:359:9 | -LL | match array_3_never {} - | ^^^^^^^^^^^^^ +LL | _ => {} + | ^ | - = note: the matched value is of type `[!; 3]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + = note: this pattern matches no values because `[!; 3]` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:362:9 | -LL ~ match array_3_never { -LL + _ => todo!(), -LL + } +LL | [_, _, _] => {} + | ^^^^^^^^^ | + = note: this pattern matches no values because `[!; 3]` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:365:9 + | +LL | [_, ..] => {} + | ^^^^^^^ + | + = note: this pattern matches no values because `[!; 3]` is uninhabited error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty - --> $DIR/empty-types.rs:389:11 + --> $DIR/empty-types.rs:379:11 | LL | match array_0_never {} | ^^^^^^^^^^^^^ @@ -479,7 +499,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:396:9 + --> $DIR/empty-types.rs:386:9 | LL | [] => {} | -- matches all the values already @@ -487,7 +507,7 @@ LL | _ => {} | ^ unreachable pattern error[E0004]: non-exhaustive patterns: `[]` not covered - --> $DIR/empty-types.rs:398:11 + --> $DIR/empty-types.rs:388:11 | LL | match array_0_never { | ^^^^^^^^^^^^^ pattern `[]` not covered @@ -500,8 +520,42 @@ LL ~ [..] if false => {}, LL + [] => todo!() | +error: unreachable pattern + --> $DIR/empty-types.rs:407:9 + | +LL | Some(_) => {} + | ^^^^^^^ + | + = note: this pattern matches no values because `!` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:412:9 + | +LL | Some(_a) => {} + | ^^^^^^^^ + | + = note: this pattern matches no values because `!` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:417:9 + | +LL | None => {} + | ---- matches all the values already +LL | // !useful, !reachable +LL | _ => {} + | ^ unreachable pattern + +error: unreachable pattern + --> $DIR/empty-types.rs:422:9 + | +LL | None => {} + | ---- matches all the values already +LL | // !useful, !reachable +LL | _a => {} + | ^^ unreachable pattern + error[E0004]: non-exhaustive patterns: `&Some(!)` not covered - --> $DIR/empty-types.rs:452:11 + --> $DIR/empty-types.rs:442:11 | LL | match ref_opt_never { | ^^^^^^^^^^^^^ pattern `&Some(!)` not covered @@ -512,6 +566,7 @@ note: `Option` defined here | = note: not covered = note: the matched value is of type `&Option` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 => {}, @@ -519,7 +574,7 @@ LL + &Some(!) | error[E0004]: non-exhaustive patterns: `Some(!)` not covered - --> $DIR/empty-types.rs:493:11 + --> $DIR/empty-types.rs:483:11 | LL | match *ref_opt_never { | ^^^^^^^^^^^^^^ pattern `Some(!)` not covered @@ -530,6 +585,7 @@ note: `Option` defined here | = note: not covered = note: the matched value is of type `Option` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 => {}, @@ -537,7 +593,7 @@ LL + Some(!) | error[E0004]: non-exhaustive patterns: `Err(!)` not covered - --> $DIR/empty-types.rs:541:11 + --> $DIR/empty-types.rs:531:11 | LL | match *ref_res_never { | ^^^^^^^^^^^^^^ pattern `Err(!)` not covered @@ -548,6 +604,7 @@ note: `Result` defined here | = note: not covered = note: the matched value is of type `Result` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 ~ Ok(_) => {}, @@ -555,7 +612,7 @@ LL + Err(!) | error[E0004]: non-exhaustive patterns: `Err(!)` not covered - --> $DIR/empty-types.rs:552:11 + --> $DIR/empty-types.rs:542:11 | LL | match *ref_res_never { | ^^^^^^^^^^^^^^ pattern `Err(!)` not covered @@ -566,6 +623,7 @@ note: `Result` defined here | = note: not covered = note: the matched value is of type `Result` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 ~ Ok(_a) => {}, @@ -573,7 +631,7 @@ LL + Err(!) | error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:571:11 + --> $DIR/empty-types.rs:561:11 | LL | match *ref_tuple_half_never {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -587,7 +645,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:604:9 + --> $DIR/empty-types.rs:594:9 | LL | _ => {} | ^ @@ -595,7 +653,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:607:9 + --> $DIR/empty-types.rs:597:9 | LL | _x => {} | ^^ @@ -603,7 +661,7 @@ LL | _x => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:610:9 + --> $DIR/empty-types.rs:600:9 | LL | _ if false => {} | ^ @@ -611,7 +669,7 @@ LL | _ if false => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:613:9 + --> $DIR/empty-types.rs:603:9 | LL | _x if false => {} | ^^ @@ -619,12 +677,13 @@ LL | _x if false => {} = note: this pattern matches no values because `!` is uninhabited error[E0004]: non-exhaustive patterns: `&!` not covered - --> $DIR/empty-types.rs:638:11 + --> $DIR/empty-types.rs:628:11 | LL | match ref_never { | ^^^^^^^^^ pattern `&!` not covered | = note: the matched value is of type `&!` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required = note: references are always considered inhabited = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown @@ -634,7 +693,7 @@ LL + &! | error[E0004]: non-exhaustive patterns: `Ok(!)` not covered - --> $DIR/empty-types.rs:654:11 + --> $DIR/empty-types.rs:644:11 | LL | match *ref_result_never { | ^^^^^^^^^^^^^^^^^ pattern `Ok(!)` not covered @@ -645,6 +704,7 @@ note: `Result` defined here | = note: not covered = note: the matched value is of type `Result` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 ~ Err(_) => {}, @@ -652,7 +712,7 @@ LL + Ok(!) | error[E0004]: non-exhaustive patterns: `Some(!)` not covered - --> $DIR/empty-types.rs:674:11 + --> $DIR/empty-types.rs:664:11 | LL | match *x { | ^^ pattern `Some(!)` not covered @@ -663,13 +723,14 @@ note: `Option>` defined here | = note: not covered = note: the matched value is of type `Option>` + = note: `Result` is uninhabited but is not being matched by value, so a wildcard `_` is required 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(!) | -error: aborting due to 49 previous errors; 1 warning emitted +error: aborting due to 64 previous errors; 1 warning emitted Some errors have detailed explanations: E0004, E0005. For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-types.normal.stderr b/tests/ui/pattern/usefulness/empty-types.normal.stderr index 78db9ee349b4..c3421cd592ed 100644 --- a/tests/ui/pattern/usefulness/empty-types.normal.stderr +++ b/tests/ui/pattern/usefulness/empty-types.normal.stderr @@ -1,18 +1,18 @@ error: unreachable pattern - --> $DIR/empty-types.rs:51:9 + --> $DIR/empty-types.rs:49:9 | LL | _ => {} | ^ | = note: this pattern matches no values because `!` is uninhabited note: the lint level is defined here - --> $DIR/empty-types.rs:17:9 + --> $DIR/empty-types.rs:15:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/empty-types.rs:54:9 + --> $DIR/empty-types.rs:52:9 | LL | _x => {} | ^^ @@ -20,7 +20,7 @@ LL | _x => {} = note: this pattern matches no values because `!` is uninhabited error[E0004]: non-exhaustive patterns: type `&!` is non-empty - --> $DIR/empty-types.rs:58:11 + --> $DIR/empty-types.rs:56:11 | LL | match ref_never {} | ^^^^^^^^^ @@ -34,69 +34,43 @@ LL + _ => todo!(), LL + } | -error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:70:11 +error: unreachable pattern + --> $DIR/empty-types.rs:70:9 | -LL | match tuple_half_never {} - | ^^^^^^^^^^^^^^^^ - | - = note: the matched value is of type `(u32, !)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match tuple_half_never { -LL + _ => todo!(), -LL + } - | - -error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty - --> $DIR/empty-types.rs:77:11 - | -LL | match tuple_never {} - | ^^^^^^^^^^^ - | - = note: the matched value is of type `(!, !)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match tuple_never { -LL + _ => todo!(), -LL + } +LL | (_, _) => {} + | ^^^^^^ | + = note: this pattern matches no values because `(u32, !)` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:87:9 + --> $DIR/empty-types.rs:76:9 + | +LL | _ => {} + | ^ + | + = note: this pattern matches no values because `(!, !)` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:79:9 + | +LL | (_, _) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `(!, !)` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:83:9 | LL | _ => {} | ^ | = note: this pattern matches no values because `!` is uninhabited -error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered - --> $DIR/empty-types.rs:91:11 +error[E0004]: non-exhaustive patterns: `Ok(_)` not covered + --> $DIR/empty-types.rs:87:11 | LL | match res_u32_never {} - | ^^^^^^^^^^^^^ patterns `Ok(_)` and `Err(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ match res_u32_never { -LL + Ok(_) | Err(_) => todo!(), -LL + } - | - -error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/empty-types.rs:93:11 - | -LL | match res_u32_never { - | ^^^^^^^^^^^^^ pattern `Err(_)` not covered + | ^^^^^^^^^^^^^ pattern `Ok(_)` not covered | note: `Result` defined here --> $SRC_DIR/core/src/result.rs:LL:COL @@ -106,12 +80,29 @@ note: `Result` defined here = note: the matched value is of type `Result` 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 ~ Ok(_) => {}, -LL + Err(_) => todo!() +LL ~ match res_u32_never { +LL + Ok(_) => todo!(), +LL + } | +error: unreachable pattern + --> $DIR/empty-types.rs:94:9 + | +LL | Err(_) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `!` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:99:9 + | +LL | Err(_) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `!` is uninhabited + error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered - --> $DIR/empty-types.rs:101:11 + --> $DIR/empty-types.rs:96:11 | LL | match res_u32_never { | ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered @@ -129,21 +120,7 @@ LL ~ Ok(1_u32..=u32::MAX) => todo!() | error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:106:9 - | -LL | let Ok(_x) = res_u32_never; - | ^^^^^^ pattern `Err(_)` not covered - | - = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant - = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html - = note: the matched value is of type `Result` -help: you might want to use `let else` to handle the variant that isn't matched - | -LL | let Ok(_x) = res_u32_never else { todo!() }; - | ++++++++++++++++ - -error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:108:9 + --> $DIR/empty-types.rs:102:9 | LL | let Ok(_x) = res_u32_never.as_ref(); | ^^^^^^ pattern `Err(_)` not covered @@ -157,7 +134,7 @@ LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() }; | ++++++++++++++++ error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:112:9 + --> $DIR/empty-types.rs:106:9 | LL | let Ok(_x) = &res_u32_never; | ^^^^^^ pattern `&Err(_)` not covered @@ -170,47 +147,56 @@ help: you might want to use `let else` to handle the variant that isn't matched LL | let Ok(_x) = &res_u32_never else { todo!() }; | ++++++++++++++++ -error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered - --> $DIR/empty-types.rs:116:11 +error: unreachable pattern + --> $DIR/empty-types.rs:112:9 | -LL | match result_never {} - | ^^^^^^^^^^^^ patterns `Ok(_)` and `Err(_)` not covered +LL | _ => {} + | ^ | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ match result_never { -LL + Ok(_) | Err(_) => todo!(), -LL + } - | - -error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/empty-types.rs:121:11 - | -LL | match result_never { - | ^^^^^^^^^^^^ pattern `Err(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -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 | Ok(_) => {}, Err(_) => todo!() - | +++++++++++++++++++ + = note: this pattern matches no values because `Result` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:140:13 + --> $DIR/empty-types.rs:115:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:118:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:119:9 + | +LL | _ => {} + | ^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:122:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:123:9 + | +LL | Err(_) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:132:13 | LL | _ => {} | ^ @@ -218,33 +204,31 @@ LL | _ => {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:143:13 + --> $DIR/empty-types.rs:135:13 | LL | _ if false => {} | ^ | = note: this pattern matches no values because `Void` is uninhabited -error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:146:15 +error: unreachable pattern + --> $DIR/empty-types.rs:143:13 | -LL | match opt_void { - | ^^^^^^^^ pattern `Some(_)` not covered +LL | Some(_) => {} + | ^^^^^^^ | -note: `Option` 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` -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!() + = note: this pattern matches no values because `Void` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:147:13 | +LL | None => {} + | ---- matches all the values already +LL | _ => {} + | ^ unreachable pattern error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:165:15 + --> $DIR/empty-types.rs:156:15 | LL | match *ref_opt_void { | ^^^^^^^^^^^^^ pattern `Some(_)` not covered @@ -255,6 +239,7 @@ note: `Option` defined here | = note: not covered = note: the matched value is of type `Option` + = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 => {}, @@ -262,7 +247,7 @@ LL + Some(_) => todo!() | error: unreachable pattern - --> $DIR/empty-types.rs:208:13 + --> $DIR/empty-types.rs:199:13 | LL | _ => {} | ^ @@ -270,7 +255,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:213:13 + --> $DIR/empty-types.rs:204:13 | LL | _ => {} | ^ @@ -278,7 +263,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:218:13 + --> $DIR/empty-types.rs:209:13 | LL | _ => {} | ^ @@ -286,7 +271,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:223:13 + --> $DIR/empty-types.rs:214:13 | LL | _ => {} | ^ @@ -294,7 +279,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:229:13 + --> $DIR/empty-types.rs:220:13 | LL | _ => {} | ^ @@ -302,15 +287,39 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:288:9 + --> $DIR/empty-types.rs:279:9 | LL | _ => {} | ^ | = note: this pattern matches no values because `!` is uninhabited +error: unreachable pattern + --> $DIR/empty-types.rs:282:9 + | +LL | (_, _) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `(!, !)` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:285:9 + | +LL | Ok(_) => {} + | ^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:286:9 + | +LL | Err(_) => {} + | ^^^^^^ + | + = note: this pattern matches no values because `Result` is uninhabited + error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:316:11 + --> $DIR/empty-types.rs:307:11 | LL | match *x {} | ^^ @@ -324,7 +333,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty - --> $DIR/empty-types.rs:318:11 + --> $DIR/empty-types.rs:309:11 | LL | match *x {} | ^^ @@ -338,7 +347,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered - --> $DIR/empty-types.rs:320:11 + --> $DIR/empty-types.rs:311:11 | LL | match *x {} | ^^ patterns `Ok(_)` and `Err(_)` not covered @@ -360,7 +369,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty - --> $DIR/empty-types.rs:322:11 + --> $DIR/empty-types.rs:313:11 | LL | match *x {} | ^^ @@ -374,7 +383,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty - --> $DIR/empty-types.rs:327:11 + --> $DIR/empty-types.rs:318:11 | LL | match slice_never {} | ^^^^^^^^^^^ @@ -388,12 +397,13 @@ LL + } | error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered - --> $DIR/empty-types.rs:329:11 + --> $DIR/empty-types.rs:320:11 | LL | match slice_never { | ^^^^^^^^^^^ pattern `&[_, ..]` not covered | = note: the matched value is of type `&[!]` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 ~ [] => {}, @@ -401,7 +411,7 @@ LL + &[_, ..] => todo!() | error[E0004]: non-exhaustive patterns: `&[]`, `&[_]` and `&[_, _]` not covered - --> $DIR/empty-types.rs:338:11 + --> $DIR/empty-types.rs:329:11 | LL | match slice_never { | ^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _]` not covered @@ -414,7 +424,7 @@ LL + &[] | &[_] | &[_, _] => todo!() | error[E0004]: non-exhaustive patterns: `&[]` and `&[_, ..]` not covered - --> $DIR/empty-types.rs:352:11 + --> $DIR/empty-types.rs:343:11 | LL | match slice_never { | ^^^^^^^^^^^ patterns `&[]` and `&[_, ..]` not covered @@ -428,7 +438,7 @@ LL + &[] | &[_, ..] => todo!() | error[E0004]: non-exhaustive patterns: type `[!]` is non-empty - --> $DIR/empty-types.rs:359:11 + --> $DIR/empty-types.rs:350:11 | LL | match *slice_never {} | ^^^^^^^^^^^^ @@ -441,22 +451,32 @@ LL + _ => todo!(), LL + } | -error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty - --> $DIR/empty-types.rs:366:11 +error: unreachable pattern + --> $DIR/empty-types.rs:359:9 | -LL | match array_3_never {} - | ^^^^^^^^^^^^^ +LL | _ => {} + | ^ | - = note: the matched value is of type `[!; 3]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown + = note: this pattern matches no values because `[!; 3]` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:362:9 | -LL ~ match array_3_never { -LL + _ => todo!(), -LL + } +LL | [_, _, _] => {} + | ^^^^^^^^^ | + = note: this pattern matches no values because `[!; 3]` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:365:9 + | +LL | [_, ..] => {} + | ^^^^^^^ + | + = note: this pattern matches no values because `[!; 3]` is uninhabited error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty - --> $DIR/empty-types.rs:389:11 + --> $DIR/empty-types.rs:379:11 | LL | match array_0_never {} | ^^^^^^^^^^^^^ @@ -470,7 +490,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:396:9 + --> $DIR/empty-types.rs:386:9 | LL | [] => {} | -- matches all the values already @@ -478,7 +498,7 @@ LL | _ => {} | ^ unreachable pattern error[E0004]: non-exhaustive patterns: `[]` not covered - --> $DIR/empty-types.rs:398:11 + --> $DIR/empty-types.rs:388:11 | LL | match array_0_never { | ^^^^^^^^^^^^^ pattern `[]` not covered @@ -491,8 +511,42 @@ LL ~ [..] if false => {}, LL + [] => todo!() | +error: unreachable pattern + --> $DIR/empty-types.rs:407:9 + | +LL | Some(_) => {} + | ^^^^^^^ + | + = note: this pattern matches no values because `!` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:412:9 + | +LL | Some(_a) => {} + | ^^^^^^^^ + | + = note: this pattern matches no values because `!` is uninhabited + +error: unreachable pattern + --> $DIR/empty-types.rs:417:9 + | +LL | None => {} + | ---- matches all the values already +LL | // !useful, !reachable +LL | _ => {} + | ^ unreachable pattern + +error: unreachable pattern + --> $DIR/empty-types.rs:422:9 + | +LL | None => {} + | ---- matches all the values already +LL | // !useful, !reachable +LL | _a => {} + | ^^ unreachable pattern + error[E0004]: non-exhaustive patterns: `&Some(_)` not covered - --> $DIR/empty-types.rs:452:11 + --> $DIR/empty-types.rs:442:11 | LL | match ref_opt_never { | ^^^^^^^^^^^^^ pattern `&Some(_)` not covered @@ -503,6 +557,7 @@ note: `Option` defined here | = note: not covered = note: the matched value is of type `&Option` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 => {}, @@ -510,7 +565,7 @@ LL + &Some(_) => todo!() | error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:493:11 + --> $DIR/empty-types.rs:483:11 | LL | match *ref_opt_never { | ^^^^^^^^^^^^^^ pattern `Some(_)` not covered @@ -521,6 +576,7 @@ note: `Option` defined here | = note: not covered = note: the matched value is of type `Option` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 => {}, @@ -528,7 +584,7 @@ LL + Some(_) => todo!() | error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/empty-types.rs:541:11 + --> $DIR/empty-types.rs:531:11 | LL | match *ref_res_never { | ^^^^^^^^^^^^^^ pattern `Err(_)` not covered @@ -539,6 +595,7 @@ note: `Result` defined here | = note: not covered = note: the matched value is of type `Result` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 ~ Ok(_) => {}, @@ -546,7 +603,7 @@ LL + Err(_) => todo!() | error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/empty-types.rs:552:11 + --> $DIR/empty-types.rs:542:11 | LL | match *ref_res_never { | ^^^^^^^^^^^^^^ pattern `Err(_)` not covered @@ -557,6 +614,7 @@ note: `Result` defined here | = note: not covered = note: the matched value is of type `Result` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 ~ Ok(_a) => {}, @@ -564,7 +622,7 @@ LL + Err(_) => todo!() | error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:571:11 + --> $DIR/empty-types.rs:561:11 | LL | match *ref_tuple_half_never {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -578,7 +636,7 @@ LL + } | error: unreachable pattern - --> $DIR/empty-types.rs:604:9 + --> $DIR/empty-types.rs:594:9 | LL | _ => {} | ^ @@ -586,7 +644,7 @@ LL | _ => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:607:9 + --> $DIR/empty-types.rs:597:9 | LL | _x => {} | ^^ @@ -594,7 +652,7 @@ LL | _x => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:610:9 + --> $DIR/empty-types.rs:600:9 | LL | _ if false => {} | ^ @@ -602,7 +660,7 @@ LL | _ if false => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/empty-types.rs:613:9 + --> $DIR/empty-types.rs:603:9 | LL | _x if false => {} | ^^ @@ -610,12 +668,13 @@ LL | _x if false => {} = note: this pattern matches no values because `!` is uninhabited error[E0004]: non-exhaustive patterns: `&_` not covered - --> $DIR/empty-types.rs:638:11 + --> $DIR/empty-types.rs:628:11 | LL | match ref_never { | ^^^^^^^^^ pattern `&_` not covered | = note: the matched value is of type `&!` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required = note: references are always considered inhabited = note: match arms with guards don't count towards exhaustivity help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown @@ -625,7 +684,7 @@ LL + &_ => todo!() | error[E0004]: non-exhaustive patterns: `Ok(_)` not covered - --> $DIR/empty-types.rs:654:11 + --> $DIR/empty-types.rs:644:11 | LL | match *ref_result_never { | ^^^^^^^^^^^^^^^^^ pattern `Ok(_)` not covered @@ -636,6 +695,7 @@ note: `Result` defined here | = note: not covered = note: the matched value is of type `Result` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 ~ Err(_) => {}, @@ -643,7 +703,7 @@ LL + Ok(_) => todo!() | error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:674:11 + --> $DIR/empty-types.rs:664:11 | LL | match *x { | ^^ pattern `Some(_)` not covered @@ -654,13 +714,14 @@ note: `Option>` defined here | = note: not covered = note: the matched value is of type `Option>` + = note: `Result` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 49 previous errors +error: aborting due to 64 previous errors Some errors have detailed explanations: E0004, E0005. For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/empty-types.rs b/tests/ui/pattern/usefulness/empty-types.rs index cc71f67831dd..639c48cea12c 100644 --- a/tests/ui/pattern/usefulness/empty-types.rs +++ b/tests/ui/pattern/usefulness/empty-types.rs @@ -1,5 +1,4 @@ -//@ revisions: normal min_exh_pats exhaustive_patterns never_pats -// gate-test-min_exhaustive_patterns +//@ revisions: normal exhaustive_patterns never_pats // // This tests correct handling of empty types in exhaustiveness checking. // @@ -10,7 +9,6 @@ // This feature is useful to avoid `!` falling back to `()` all the time. #![feature(never_type_fallback)] #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))] -#![cfg_attr(min_exh_pats, feature(min_exhaustive_patterns))] #![cfg_attr(never_pats, feature(never_patterns))] //[never_pats]~^ WARN the feature `never_patterns` is incomplete #![allow(dead_code, unreachable_code)] @@ -68,19 +66,17 @@ fn basic(x: NeverBundle) { let tuple_half_never: (u32, !) = x.tuple_half_never; match tuple_half_never {} - //[normal,never_pats]~^ ERROR non-empty match tuple_half_never { - (_, _) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + (_, _) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } let tuple_never: (!, !) = x.tuple_never; match tuple_never {} - //[normal,never_pats]~^ ERROR non-empty match tuple_never { - _ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + _ => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match tuple_never { - (_, _) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + (_, _) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match tuple_never.0 {} match tuple_never.0 { @@ -91,44 +87,40 @@ fn basic(x: NeverBundle) { match res_u32_never {} //~^ ERROR non-exhaustive match res_u32_never { - //[normal,never_pats]~^ ERROR non-exhaustive Ok(_) => {} } match res_u32_never { Ok(_) => {} - Err(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + Err(_) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match res_u32_never { //~^ ERROR non-exhaustive Ok(0) => {} - Err(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + Err(_) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } let Ok(_x) = res_u32_never; - //[normal,never_pats]~^ ERROR refutable let Ok(_x) = res_u32_never.as_ref(); //~^ ERROR refutable // Non-obvious difference: here there's an implicit dereference in the patterns, which makes the // inner place !known_valid. `exhaustive_patterns` ignores this. let Ok(_x) = &res_u32_never; - //[normal,min_exh_pats,never_pats]~^ ERROR refutable + //[normal,never_pats]~^ ERROR refutable let result_never: Result = x.result_never; match result_never {} - //[normal,never_pats]~^ ERROR non-exhaustive match result_never { - _ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + _ => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match result_never { - //[normal,never_pats]~^ ERROR non-exhaustive - Ok(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + Ok(_) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match result_never { - Ok(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern - _ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + Ok(_) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern + _ => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match result_never { - Ok(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern - Err(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + Ok(_) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern + Err(_) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } } @@ -144,16 +136,15 @@ fn void_same_as_never(x: NeverBundle) { } let opt_void: Option = None; match opt_void { - //[normal,never_pats]~^ ERROR non-exhaustive None => {} } match opt_void { None => {} - Some(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + Some(_) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match opt_void { None => {} - _ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + _ => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } let ref_void: &Void = &x.void; @@ -163,7 +154,7 @@ fn void_same_as_never(x: NeverBundle) { } let ref_opt_void: &Option = &None; match *ref_opt_void { - //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive + //[normal,never_pats]~^ ERROR non-exhaustive None => {} } match *ref_opt_void { @@ -288,11 +279,11 @@ fn nested_validity_tracking(bundle: NeverBundle) { _ => {} //~ ERROR unreachable pattern } match tuple_never { - (_, _) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + (_, _) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match result_never { - Ok(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern - Err(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + Ok(_) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern + Err(_) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } // These should be considered !known_valid and not warn unreachable. @@ -313,13 +304,13 @@ fn invalid_empty_match(bundle: NeverBundle) { match *x {} let x: &(u32, !) = &bundle.tuple_half_never; - match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive + match *x {} //[normal,never_pats]~ ERROR non-exhaustive let x: &(!, !) = &bundle.tuple_never; - match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive + match *x {} //[normal,never_pats]~ ERROR non-exhaustive let x: &Result = &bundle.result_never; - match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive + match *x {} //[normal,never_pats]~ ERROR non-exhaustive let x: &[!; 3] = &bundle.array_3_never; - match *x {} //[normal,min_exh_pats,never_pats]~ ERROR non-exhaustive + match *x {} //[normal,never_pats]~ ERROR non-exhaustive } fn arrays_and_slices(x: NeverBundle) { @@ -327,7 +318,7 @@ fn arrays_and_slices(x: NeverBundle) { match slice_never {} //~^ ERROR non-empty match slice_never { - //[normal,min_exh_pats,never_pats]~^ ERROR not covered + //[normal,never_pats]~^ ERROR not covered [] => {} } match slice_never { @@ -336,7 +327,7 @@ fn arrays_and_slices(x: NeverBundle) { [_, _, ..] => {} } match slice_never { - //[normal,min_exh_pats]~^ ERROR `&[]`, `&[_]` and `&[_, _]` not covered + //[normal]~^ ERROR `&[]`, `&[_]` and `&[_, _]` not covered //[exhaustive_patterns]~^^ ERROR `&[]` not covered //[never_pats]~^^^ ERROR `&[]`, `&[!]` and `&[!, !]` not covered [_, _, _, ..] => {} @@ -350,7 +341,7 @@ fn arrays_and_slices(x: NeverBundle) { _x => {} } match slice_never { - //[normal,min_exh_pats]~^ ERROR `&[]` and `&[_, ..]` not covered + //[normal]~^ ERROR `&[]` and `&[_, ..]` not covered //[exhaustive_patterns]~^^ ERROR `&[]` not covered //[never_pats]~^^^ ERROR `&[]` and `&[!, ..]` not covered &[..] if false => {} @@ -364,15 +355,14 @@ fn arrays_and_slices(x: NeverBundle) { let array_3_never: [!; 3] = x.array_3_never; match array_3_never {} - //[normal,never_pats]~^ ERROR non-empty match array_3_never { - _ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + _ => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match array_3_never { - [_, _, _] => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + [_, _, _] => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match array_3_never { - [_, ..] => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + [_, ..] => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } let ref_array_3_never: &[!; 3] = &array_3_never; @@ -414,22 +404,22 @@ fn bindings(x: NeverBundle) { match opt_never { None => {} // !useful, !reachable - Some(_) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + Some(_) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match opt_never { None => {} // !useful, !reachable - Some(_a) => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + Some(_a) => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match opt_never { None => {} // !useful, !reachable - _ => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + _ => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } match opt_never { None => {} // !useful, !reachable - _a => {} //[exhaustive_patterns,min_exh_pats]~ ERROR unreachable pattern + _a => {} //[exhaustive_patterns,normal,never_pats]~ ERROR unreachable pattern } // The scrutinee is known_valid, but under the `&` isn't anymore. @@ -450,7 +440,7 @@ fn bindings(x: NeverBundle) { &_a => {} } match ref_opt_never { - //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive + //[normal,never_pats]~^ ERROR non-exhaustive &None => {} } match ref_opt_never { @@ -491,7 +481,7 @@ fn bindings(x: NeverBundle) { ref _a => {} } match *ref_opt_never { - //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive + //[normal,never_pats]~^ ERROR non-exhaustive None => {} } match *ref_opt_never { @@ -539,7 +529,7 @@ fn bindings(x: NeverBundle) { let ref_res_never: &Result = &x.result_never; match *ref_res_never { - //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive + //[normal,never_pats]~^ ERROR non-exhaustive // useful, reachable Ok(_) => {} } @@ -550,7 +540,7 @@ fn bindings(x: NeverBundle) { _ => {} } match *ref_res_never { - //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive + //[normal,never_pats]~^ ERROR non-exhaustive // useful, !reachable Ok(_a) => {} } @@ -569,7 +559,7 @@ fn bindings(x: NeverBundle) { let ref_tuple_half_never: &(u32, !) = &x.tuple_half_never; match *ref_tuple_half_never {} - //[normal,min_exh_pats,never_pats]~^ ERROR non-empty + //[normal,never_pats]~^ ERROR non-empty match *ref_tuple_half_never { // useful, reachable (_, _) => {} @@ -636,7 +626,7 @@ fn guards_and_validity(x: NeverBundle) { _a if false => {} } match ref_never { - //[normal,min_exh_pats,never_pats]~^ ERROR non-exhaustive + //[normal,never_pats]~^ ERROR non-exhaustive // useful, !reachable &_a if false => {} } @@ -652,7 +642,7 @@ fn guards_and_validity(x: NeverBundle) { Err(_) => {} } match *ref_result_never { - //[normal,min_exh_pats]~^ ERROR `Ok(_)` not covered + //[normal]~^ ERROR `Ok(_)` not covered //[never_pats]~^^ ERROR `Ok(!)` not covered // useful, reachable Ok(_) if false => {} @@ -672,7 +662,7 @@ fn diagnostics_subtlety(x: NeverBundle) { // Regression test for diagnostics: don't report `Some(Ok(_))` and `Some(Err(_))`. let x: &Option> = &None; match *x { - //[normal,min_exh_pats]~^ ERROR `Some(_)` not covered + //[normal]~^ ERROR `Some(_)` not covered //[never_pats]~^^ ERROR `Some(!)` not covered None => {} } diff --git a/tests/ui/pattern/usefulness/explain-unreachable-pats.rs b/tests/ui/pattern/usefulness/explain-unreachable-pats.rs index 98f781b6a09f..44d194055d9b 100644 --- a/tests/ui/pattern/usefulness/explain-unreachable-pats.rs +++ b/tests/ui/pattern/usefulness/explain-unreachable-pats.rs @@ -1,5 +1,4 @@ #![feature(never_type)] -#![feature(min_exhaustive_patterns)] #![deny(unreachable_patterns)] //~^ NOTE lint level is defined here diff --git a/tests/ui/pattern/usefulness/explain-unreachable-pats.stderr b/tests/ui/pattern/usefulness/explain-unreachable-pats.stderr index e2eecf4a9f38..105d4f73f660 100644 --- a/tests/ui/pattern/usefulness/explain-unreachable-pats.stderr +++ b/tests/ui/pattern/usefulness/explain-unreachable-pats.stderr @@ -1,5 +1,5 @@ error: unreachable pattern - --> $DIR/explain-unreachable-pats.rs:11:9 + --> $DIR/explain-unreachable-pats.rs:10:9 | LL | (1 | 2,) => {} | -------- matches all the values already @@ -8,19 +8,19 @@ LL | (2,) => {} | ^^^^ unreachable pattern | note: the lint level is defined here - --> $DIR/explain-unreachable-pats.rs:3:9 + --> $DIR/explain-unreachable-pats.rs:2:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/explain-unreachable-pats.rs:22:9 + --> $DIR/explain-unreachable-pats.rs:21:9 | LL | (1 | 2,) => {} | ^^^^^^^^ unreachable pattern | note: these patterns collectively make the last one unreachable - --> $DIR/explain-unreachable-pats.rs:22:9 + --> $DIR/explain-unreachable-pats.rs:21:9 | LL | (1,) => {} | ---- matches some of the same values @@ -32,7 +32,7 @@ LL | (1 | 2,) => {} | ^^^^^^^^ collectively making this unreachable error: unreachable pattern - --> $DIR/explain-unreachable-pats.rs:33:9 + --> $DIR/explain-unreachable-pats.rs:32:9 | LL | Err(_) => {} | ^^^^^^ @@ -40,7 +40,7 @@ LL | Err(_) => {} = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/explain-unreachable-pats.rs:46:9 + --> $DIR/explain-unreachable-pats.rs:45:9 | LL | (Err(_), Err(_)) => {} | ^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | (Err(_), Err(_)) => {} = note: this pattern matches no values because `Void2` is uninhabited error: unreachable pattern - --> $DIR/explain-unreachable-pats.rs:52:9 + --> $DIR/explain-unreachable-pats.rs:51:9 | LL | (Err(_), Err(_)) => {} | ^^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | (Err(_), Err(_)) => {} = note: this pattern matches no values because `Void1` is uninhabited error: unreachable pattern - --> $DIR/explain-unreachable-pats.rs:61:11 + --> $DIR/explain-unreachable-pats.rs:60:11 | LL | if let (0 | - matches all the values already @@ -65,13 +65,13 @@ LL | | 0, _) = (0, 0) {} | ^ unreachable pattern error: unreachable pattern - --> $DIR/explain-unreachable-pats.rs:71:9 + --> $DIR/explain-unreachable-pats.rs:70:9 | LL | (_, true) => {} | ^^^^^^^^^ unreachable pattern | note: these patterns collectively make the last one unreachable - --> $DIR/explain-unreachable-pats.rs:71:9 + --> $DIR/explain-unreachable-pats.rs:70:9 | LL | (true, _) => {} | --------- matches some of the same values @@ -83,7 +83,7 @@ LL | (_, true) => {} | ^^^^^^^^^ collectively making this unreachable error: unreachable pattern - --> $DIR/explain-unreachable-pats.rs:84:9 + --> $DIR/explain-unreachable-pats.rs:83:9 | LL | (true, _) => {} | --------- matches all the values already @@ -92,7 +92,7 @@ LL | (true, true) => {} | ^^^^^^^^^^^^ unreachable pattern error: unreachable pattern - --> $DIR/explain-unreachable-pats.rs:96:9 + --> $DIR/explain-unreachable-pats.rs:95:9 | LL | (_, true, 0..10) => {} | ---------------- matches all the values already diff --git a/tests/ui/pattern/usefulness/impl-trait.rs b/tests/ui/pattern/usefulness/impl-trait.rs index 1fec9a2633ee..c1cc279f74ce 100644 --- a/tests/ui/pattern/usefulness/impl-trait.rs +++ b/tests/ui/pattern/usefulness/impl-trait.rs @@ -1,5 +1,4 @@ #![feature(never_type)] -#![feature(min_exhaustive_patterns)] #![feature(type_alias_impl_trait)] #![feature(non_exhaustive_omitted_patterns_lint)] #![deny(unreachable_patterns)] diff --git a/tests/ui/pattern/usefulness/impl-trait.stderr b/tests/ui/pattern/usefulness/impl-trait.stderr index c079f5a259dd..92932e485388 100644 --- a/tests/ui/pattern/usefulness/impl-trait.stderr +++ b/tests/ui/pattern/usefulness/impl-trait.stderr @@ -1,18 +1,18 @@ error: unreachable pattern - --> $DIR/impl-trait.rs:17:13 + --> $DIR/impl-trait.rs:16:13 | LL | _ => {} | ^ | = note: this pattern matches no values because `Void` is uninhabited note: the lint level is defined here - --> $DIR/impl-trait.rs:5:9 + --> $DIR/impl-trait.rs:4:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/impl-trait.rs:31:13 + --> $DIR/impl-trait.rs:30:13 | LL | _ => {} | ^ @@ -20,7 +20,7 @@ LL | _ => {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/impl-trait.rs:45:13 + --> $DIR/impl-trait.rs:44:13 | LL | Some(_) => {} | ^^^^^^^ @@ -28,7 +28,7 @@ LL | Some(_) => {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/impl-trait.rs:49:13 + --> $DIR/impl-trait.rs:48:13 | LL | None => {} | ---- matches all the values already @@ -36,7 +36,7 @@ LL | _ => {} | ^ unreachable pattern error: unreachable pattern - --> $DIR/impl-trait.rs:59:13 + --> $DIR/impl-trait.rs:58:13 | LL | Some(_) => {} | ^^^^^^^ @@ -44,7 +44,7 @@ LL | Some(_) => {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/impl-trait.rs:63:13 + --> $DIR/impl-trait.rs:62:13 | LL | None => {} | ---- matches all the values already @@ -52,7 +52,7 @@ LL | _ => {} | ^ unreachable pattern error: unreachable pattern - --> $DIR/impl-trait.rs:76:9 + --> $DIR/impl-trait.rs:75:9 | LL | _ => {} | ^ @@ -60,7 +60,7 @@ LL | _ => {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/impl-trait.rs:86:9 + --> $DIR/impl-trait.rs:85:9 | LL | _ => {} | - matches any value @@ -68,7 +68,7 @@ LL | Some((a, b)) => {} | ^^^^^^^^^^^^ unreachable pattern error: unreachable pattern - --> $DIR/impl-trait.rs:94:13 + --> $DIR/impl-trait.rs:93:13 | LL | _ => {} | ^ @@ -76,7 +76,7 @@ LL | _ => {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/impl-trait.rs:105:9 + --> $DIR/impl-trait.rs:104:9 | LL | Some((a, b)) => {} | ------------ matches all the values already @@ -84,7 +84,7 @@ LL | Some((mut x, mut y)) => { | ^^^^^^^^^^^^^^^^^^^^ unreachable pattern error: unreachable pattern - --> $DIR/impl-trait.rs:124:13 + --> $DIR/impl-trait.rs:123:13 | LL | _ => {} | - matches any value @@ -92,7 +92,7 @@ LL | Rec { n: 0, w: Some(Rec { n: 0, w: _ }) } => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern error: unreachable pattern - --> $DIR/impl-trait.rs:138:13 + --> $DIR/impl-trait.rs:137:13 | LL | _ => {} | ^ @@ -100,7 +100,7 @@ LL | _ => {} = note: this pattern matches no values because `SecretelyVoid` is uninhabited error: unreachable pattern - --> $DIR/impl-trait.rs:151:13 + --> $DIR/impl-trait.rs:150:13 | LL | _ => {} | ^ @@ -108,7 +108,7 @@ LL | _ => {} = note: this pattern matches no values because `SecretelyDoubleVoid` is uninhabited error[E0004]: non-exhaustive patterns: type `impl Copy` is non-empty - --> $DIR/impl-trait.rs:23:11 + --> $DIR/impl-trait.rs:22:11 | LL | match return_never_rpit(x) {} | ^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL + } | error[E0004]: non-exhaustive patterns: type `T` is non-empty - --> $DIR/impl-trait.rs:37:11 + --> $DIR/impl-trait.rs:36:11 | LL | match return_never_tait(x) {} | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/pattern/usefulness/match-privately-empty.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/match-privately-empty.exhaustive_patterns.stderr index 261a4b3353f2..463e104f970c 100644 --- a/tests/ui/pattern/usefulness/match-privately-empty.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/match-privately-empty.exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered - --> $DIR/match-privately-empty.rs:15:11 + --> $DIR/match-privately-empty.rs:14:11 | LL | match private::DATA { | ^^^^^^^^^^^^^ pattern `Some(Private { misc: true, .. })` not covered diff --git a/tests/ui/pattern/usefulness/match-privately-empty.normal.stderr b/tests/ui/pattern/usefulness/match-privately-empty.normal.stderr new file mode 100644 index 000000000000..463e104f970c --- /dev/null +++ b/tests/ui/pattern/usefulness/match-privately-empty.normal.stderr @@ -0,0 +1,21 @@ +error[E0004]: non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered + --> $DIR/match-privately-empty.rs:14:11 + | +LL | match private::DATA { + | ^^^^^^^^^^^^^ pattern `Some(Private { misc: true, .. })` not covered + | +note: `Option` 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` +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 ~ Some(private::Private { misc: false, .. }) => {}, +LL + Some(Private { misc: true, .. }) => todo!() + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/match-privately-empty.rs b/tests/ui/pattern/usefulness/match-privately-empty.rs index 7e1d0dc48f2c..bfea15af1801 100644 --- a/tests/ui/pattern/usefulness/match-privately-empty.rs +++ b/tests/ui/pattern/usefulness/match-privately-empty.rs @@ -1,6 +1,5 @@ -//@ revisions: min_exhaustive_patterns exhaustive_patterns +//@ revisions: normal exhaustive_patterns #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))] -#![cfg_attr(min_exhaustive_patterns, feature(min_exhaustive_patterns))] #![feature(never_type)] mod private { diff --git a/tests/ui/pattern/usefulness/slice_of_empty.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/slice_of_empty.exhaustive_patterns.stderr index e5e581447e66..c4fcd67cfdbd 100644 --- a/tests/ui/pattern/usefulness/slice_of_empty.exhaustive_patterns.stderr +++ b/tests/ui/pattern/usefulness/slice_of_empty.exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `&[]` not covered - --> $DIR/slice_of_empty.rs:21:11 + --> $DIR/slice_of_empty.rs:20:11 | LL | match nevers { | ^^^^^^ pattern `&[]` not covered diff --git a/tests/ui/pattern/usefulness/slice_of_empty.normal.stderr b/tests/ui/pattern/usefulness/slice_of_empty.normal.stderr new file mode 100644 index 000000000000..c9afd1bfc901 --- /dev/null +++ b/tests/ui/pattern/usefulness/slice_of_empty.normal.stderr @@ -0,0 +1,30 @@ +error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered + --> $DIR/slice_of_empty.rs:9:11 + | +LL | match nevers { + | ^^^^^^ pattern `&[_, ..]` not covered + | + = note: the matched value is of type `&[!]` + = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required +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 ~ &[] => (), +LL ~ &[_, ..] => todo!(), + | + +error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered + --> $DIR/slice_of_empty.rs:20:11 + | +LL | match nevers { + | ^^^^^^ patterns `&[]` and `&[_, _, ..]` not covered + | + = note: the matched value is of type `&[!]` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms + | +LL ~ &[_] => (), +LL ~ &[] | &[_, _, ..] => todo!(), + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/slice_of_empty.rs b/tests/ui/pattern/usefulness/slice_of_empty.rs index 785fccaabf7e..e186ba5134d5 100644 --- a/tests/ui/pattern/usefulness/slice_of_empty.rs +++ b/tests/ui/pattern/usefulness/slice_of_empty.rs @@ -1,6 +1,5 @@ -//@ revisions: min_exhaustive_patterns exhaustive_patterns +//@ revisions: normal exhaustive_patterns #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))] -#![cfg_attr(min_exhaustive_patterns, feature(min_exhaustive_patterns))] #![feature(never_type)] #![deny(unreachable_patterns)] @@ -8,7 +7,7 @@ fn main() {} fn foo(nevers: &[!]) { match nevers { - //[min_exhaustive_patterns]~^ ERROR non-exhaustive patterns: `&[_, ..]` not covered + //[normal]~^ ERROR non-exhaustive patterns: `&[_, ..]` not covered &[] => (), }; @@ -20,7 +19,7 @@ fn foo(nevers: &[!]) { match nevers { //[exhaustive_patterns]~^ ERROR non-exhaustive patterns: `&[]` not covered - //[min_exhaustive_patterns]~^^ ERROR non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered + //[normal]~^^ ERROR non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered &[_] => (), }; } diff --git a/tests/ui/pattern/usefulness/uninhabited.rs b/tests/ui/pattern/usefulness/uninhabited.rs index 72e602ee8d2d..5c774b7874a6 100644 --- a/tests/ui/pattern/usefulness/uninhabited.rs +++ b/tests/ui/pattern/usefulness/uninhabited.rs @@ -5,7 +5,6 @@ // `Ty::is_inhabited_from` function. #![feature(never_type)] #![feature(never_type_fallback)] -#![feature(min_exhaustive_patterns)] #![deny(unreachable_patterns)] macro_rules! assert_empty { diff --git a/tests/ui/polymorphization/inline-tainted-body.rs b/tests/ui/polymorphization/inline-tainted-body.rs new file mode 100644 index 000000000000..13aec97e22bd --- /dev/null +++ b/tests/ui/polymorphization/inline-tainted-body.rs @@ -0,0 +1,21 @@ +//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes + +#![feature(unboxed_closures)] + +use std::sync::Arc; + +pub struct WeakOnce(); +//~^ ERROR type parameter `T` is never used + +impl WeakOnce { + extern "rust-call" fn try_get(&self) -> Option> {} + //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument + //~| ERROR mismatched types + + pub fn get(&self) -> Arc { + self.try_get() + .unwrap_or_else(|| panic!("Singleton {} not available", std::any::type_name::())) + } +} + +fn main() {} diff --git a/tests/ui/polymorphization/inline-tainted-body.stderr b/tests/ui/polymorphization/inline-tainted-body.stderr new file mode 100644 index 000000000000..5c3bd70adae0 --- /dev/null +++ b/tests/ui/polymorphization/inline-tainted-body.stderr @@ -0,0 +1,30 @@ +error[E0392]: type parameter `T` is never used + --> $DIR/inline-tainted-body.rs:7:21 + | +LL | pub struct WeakOnce(); + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/inline-tainted-body.rs:11:35 + | +LL | extern "rust-call" fn try_get(&self) -> Option> {} + | ^^^^^ + +error[E0308]: mismatched types + --> $DIR/inline-tainted-body.rs:11:45 + | +LL | extern "rust-call" fn try_get(&self) -> Option> {} + | ------- ^^^^^^^^^^^^^^ expected `Option>`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + | + = note: expected enum `Option>` + found unit type `()` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0308, E0392. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/reachable/unreachable-loop-patterns.rs b/tests/ui/reachable/unreachable-loop-patterns.rs index 4294a18ba440..d074e3a6ece4 100644 --- a/tests/ui/reachable/unreachable-loop-patterns.rs +++ b/tests/ui/reachable/unreachable-loop-patterns.rs @@ -1,6 +1,4 @@ #![feature(never_type, never_type_fallback)] -#![feature(min_exhaustive_patterns)] - #![allow(unreachable_code)] #![deny(unreachable_patterns)] diff --git a/tests/ui/reachable/unreachable-loop-patterns.stderr b/tests/ui/reachable/unreachable-loop-patterns.stderr index bdd9b5ee4115..9b7c2ba4acdc 100644 --- a/tests/ui/reachable/unreachable-loop-patterns.stderr +++ b/tests/ui/reachable/unreachable-loop-patterns.stderr @@ -1,12 +1,12 @@ error: unreachable pattern - --> $DIR/unreachable-loop-patterns.rs:18:9 + --> $DIR/unreachable-loop-patterns.rs:16:9 | LL | for _ in unimplemented!() as Void {} | ^ | = note: this pattern matches no values because `Void` is uninhabited note: the lint level is defined here - --> $DIR/unreachable-loop-patterns.rs:5:9 + --> $DIR/unreachable-loop-patterns.rs:3:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/check.rs b/tests/ui/rfcs/rfc-0000-never_patterns/check.rs index dc13dd05fa6a..77f79003edc8 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/check.rs +++ b/tests/ui/rfcs/rfc-0000-never_patterns/check.rs @@ -11,22 +11,22 @@ macro_rules! never { } fn no_arms_or_guards(x: Void) { - match None:: { + match &None:: { Some(!) => {} //~^ ERROR a never pattern is always unreachable None => {} } - match None:: { //~ ERROR: `Some(!)` not covered + match &None:: { //~ ERROR: `&Some(!)` not covered Some(!) if true, //~^ ERROR guard on a never pattern None => {} } - match None:: { //~ ERROR: `Some(!)` not covered + match &None:: { //~ ERROR: `&Some(!)` not covered Some(!) if true => {} //~^ ERROR a never pattern is always unreachable None => {} } - match None:: { + match &None:: { Some(never!()) => {} //~^ ERROR a never pattern is always unreachable None => {} diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/check.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/check.stderr index fbf7aa02ac2b..4622ea59b547 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/check.stderr +++ b/tests/ui/rfcs/rfc-0000-never_patterns/check.stderr @@ -31,40 +31,42 @@ LL | Some(never!()) => {} | this will never be executed | help: remove this expression -error[E0004]: non-exhaustive patterns: `Some(!)` not covered +error[E0004]: non-exhaustive patterns: `&Some(!)` not covered --> $DIR/check.rs:19:11 | -LL | match None:: { - | ^^^^^^^^^^^^ pattern `Some(!)` not covered +LL | match &None:: { + | ^^^^^^^^^^^^^ pattern `&Some(!)` not covered | note: `Option` 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` + = note: the matched value is of type `&Option` + = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required 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(!) +LL + &Some(!) | -error[E0004]: non-exhaustive patterns: `Some(!)` not covered +error[E0004]: non-exhaustive patterns: `&Some(!)` not covered --> $DIR/check.rs:24:11 | -LL | match None:: { - | ^^^^^^^^^^^^ pattern `Some(!)` not covered +LL | match &None:: { + | ^^^^^^^^^^^^^ pattern `&Some(!)` not covered | note: `Option` 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` + = note: the matched value is of type `&Option` + = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required 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(!) +LL + &Some(!) | error: aborting due to 6 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr index 013a8b53a550..9e2ae2846d51 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr +++ b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr @@ -1,5 +1,5 @@ error: mismatched types - --> $DIR/typeck.rs:25:9 + --> $DIR/typeck.rs:24:9 | LL | !, | ^ a never pattern must be used on an uninhabited type @@ -7,7 +7,7 @@ LL | !, = note: the matched value is of type `()` error: mismatched types - --> $DIR/typeck.rs:29:9 + --> $DIR/typeck.rs:28:9 | LL | !, | ^ a never pattern must be used on an uninhabited type @@ -15,7 +15,7 @@ LL | !, = note: the matched value is of type `(i32, bool)` error: mismatched types - --> $DIR/typeck.rs:33:13 + --> $DIR/typeck.rs:32:13 | LL | (_, !), | ^ a never pattern must be used on an uninhabited type @@ -23,7 +23,7 @@ LL | (_, !), = note: the matched value is of type `bool` error: mismatched types - --> $DIR/typeck.rs:38:14 + --> $DIR/typeck.rs:37:14 | LL | Some(!), | ^ a never pattern must be used on an uninhabited type @@ -31,7 +31,7 @@ LL | Some(!), = note: the matched value is of type `i32` error: mismatched types - --> $DIR/typeck.rs:45:9 + --> $DIR/typeck.rs:44:9 | LL | !, | ^ a never pattern must be used on an uninhabited type @@ -39,7 +39,7 @@ LL | !, = note: the matched value is of type `()` error: mismatched types - --> $DIR/typeck.rs:52:9 + --> $DIR/typeck.rs:51:9 | LL | !, | ^ a never pattern must be used on an uninhabited type @@ -47,7 +47,7 @@ LL | !, = note: the matched value is of type `Option` error: mismatched types - --> $DIR/typeck.rs:57:9 + --> $DIR/typeck.rs:56:9 | LL | !, | ^ a never pattern must be used on an uninhabited type @@ -55,7 +55,7 @@ LL | !, = note: the matched value is of type `[Void]` error: mismatched types - --> $DIR/typeck.rs:63:9 + --> $DIR/typeck.rs:62:9 | LL | !, | ^ a never pattern must be used on an uninhabited type diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs index 8300f953dc16..bf74b282f6cd 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs +++ b/tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs @@ -2,7 +2,6 @@ //@[pass] check-pass //@[fail] check-fail #![feature(never_patterns)] -#![feature(min_exhaustive_patterns)] #![allow(incomplete_features)] #[derive(Copy, Clone)] diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr index a875041d89c5..d78f4a5f6ebb 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr @@ -1,18 +1,18 @@ error: unreachable pattern - --> $DIR/unreachable.rs:17:9 + --> $DIR/unreachable.rs:16:9 | LL | Err(!), | ^^^^^^ | = note: this pattern matches no values because `Void` is uninhabited note: the lint level is defined here - --> $DIR/unreachable.rs:7:9 + --> $DIR/unreachable.rs:6:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/unreachable.rs:20:19 + --> $DIR/unreachable.rs:19:19 | LL | let (Ok(_x) | Err(!)) = res_void; | ^^^^^^ @@ -20,7 +20,7 @@ LL | let (Ok(_x) | Err(!)) = res_void; = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/unreachable.rs:22:12 + --> $DIR/unreachable.rs:21:12 | LL | if let Err(!) = res_void {} | ^^^^^^ @@ -28,7 +28,7 @@ LL | if let Err(!) = res_void {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/unreachable.rs:24:24 + --> $DIR/unreachable.rs:23:24 | LL | if let (Ok(true) | Err(!)) = res_void {} | ^^^^^^ @@ -36,7 +36,7 @@ LL | if let (Ok(true) | Err(!)) = res_void {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/unreachable.rs:26:23 + --> $DIR/unreachable.rs:25:23 | LL | for (Ok(mut _x) | Err(!)) in [res_void] {} | ^^^^^^ @@ -44,7 +44,7 @@ LL | for (Ok(mut _x) | Err(!)) in [res_void] {} = note: this pattern matches no values because `Void` is uninhabited error: unreachable pattern - --> $DIR/unreachable.rs:30:18 + --> $DIR/unreachable.rs:29:18 | LL | fn foo((Ok(_x) | Err(!)): Result) {} | ^^^^^^ diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.normal.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.normal.stderr new file mode 100644 index 000000000000..a3bf8e80ecec --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.normal.stderr @@ -0,0 +1,44 @@ +error: unreachable pattern + --> $DIR/unreachable.rs:16:9 + | +LL | Err(!), + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unreachable.rs:6:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:19:19 + | +LL | let (Ok(_x) | Err(!)) = res_void; + | ^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:21:12 + | +LL | if let Err(!) = res_void {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:23:24 + | +LL | if let (Ok(true) | Err(!)) = res_void {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:25:23 + | +LL | for (Ok(mut _x) | Err(!)) in [res_void] {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:29:18 + | +LL | fn foo((Ok(_x) | Err(!)): Result) {} + | ^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs index 4d20c67cf0f8..f68da4aa1731 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs @@ -1,8 +1,5 @@ -//@ revisions: normal exh_pats -//@[normal] check-pass #![feature(never_patterns)] #![allow(incomplete_features)] -#![cfg_attr(exh_pats, feature(min_exhaustive_patterns))] #![allow(dead_code, unreachable_code)] #![deny(unreachable_patterns)] @@ -15,17 +12,17 @@ fn main() { match res_void { Ok(_x) => {} Err(!), - //[exh_pats]~^ ERROR unreachable + //~^ ERROR unreachable } let (Ok(_x) | Err(!)) = res_void; - //[exh_pats]~^ ERROR unreachable + //~^ ERROR unreachable if let Err(!) = res_void {} - //[exh_pats]~^ ERROR unreachable + //~^ ERROR unreachable if let (Ok(true) | Err(!)) = res_void {} - //[exh_pats]~^ ERROR unreachable + //~^ ERROR unreachable for (Ok(mut _x) | Err(!)) in [res_void] {} - //[exh_pats]~^ ERROR unreachable + //~^ ERROR unreachable } fn foo((Ok(_x) | Err(!)): Result) {} -//[exh_pats]~^ ERROR unreachable +//~^ ERROR unreachable diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.stderr new file mode 100644 index 000000000000..79b640d9f419 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.stderr @@ -0,0 +1,55 @@ +error: unreachable pattern + --> $DIR/unreachable.rs:14:9 + | +LL | Err(!), + | ^^^^^^ + | + = note: this pattern matches no values because `Void` is uninhabited +note: the lint level is defined here + --> $DIR/unreachable.rs:4:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:17:19 + | +LL | let (Ok(_x) | Err(!)) = res_void; + | ^^^^^^ + | + = note: this pattern matches no values because `Void` is uninhabited + +error: unreachable pattern + --> $DIR/unreachable.rs:19:12 + | +LL | if let Err(!) = res_void {} + | ^^^^^^ + | + = note: this pattern matches no values because `Void` is uninhabited + +error: unreachable pattern + --> $DIR/unreachable.rs:21:24 + | +LL | if let (Ok(true) | Err(!)) = res_void {} + | ^^^^^^ + | + = note: this pattern matches no values because `Void` is uninhabited + +error: unreachable pattern + --> $DIR/unreachable.rs:23:23 + | +LL | for (Ok(mut _x) | Err(!)) in [res_void] {} + | ^^^^^^ + | + = note: this pattern matches no values because `Void` is uninhabited + +error: unreachable pattern + --> $DIR/unreachable.rs:27:18 + | +LL | fn foo((Ok(_x) | Err(!)): Result) {} + | ^^^^^^ + | + = note: this pattern matches no values because `Void` is uninhabited + +error: aborting due to 6 previous errors + diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs index 8f090fe886a0..d81896eba195 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs @@ -1,3 +1,4 @@ +//@ check-pass #![feature(never_type)] #[non_exhaustive] @@ -28,24 +29,24 @@ pub struct IndirectUninhabitedVariants(UninhabitedVariants); struct A; // This test checks that an empty match on a non-exhaustive uninhabited type through a level of -// indirection from the defining crate will not compile without `#![feature(exhaustive_patterns)]`. +// indirection from the defining crate compiles. fn cannot_empty_match_on_empty_enum_to_anything(x: IndirectUninhabitedEnum) -> A { - match x {} //~ ERROR non-exhaustive patterns + match x {} } fn cannot_empty_match_on_empty_struct_to_anything(x: IndirectUninhabitedStruct) -> A { - match x {} //~ ERROR non-exhaustive patterns + match x {} } fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: IndirectUninhabitedTupleStruct) -> A { - match x {} //~ ERROR non-exhaustive patterns + match x {} } fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything( x: IndirectUninhabitedVariants, ) -> A { - match x {} //~ ERROR non-exhaustive patterns + match x {} } fn main() {} diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr deleted file mode 100644 index c12190541403..000000000000 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr +++ /dev/null @@ -1,79 +0,0 @@ -error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedEnum` is non-empty - --> $DIR/indirect_match_same_crate.rs:34:11 - | -LL | match x {} - | ^ - | -note: `IndirectUninhabitedEnum` defined here - --> $DIR/indirect_match_same_crate.rs:20:12 - | -LL | pub struct IndirectUninhabitedEnum(UninhabitedEnum); - | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: the matched value is of type `IndirectUninhabitedEnum` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedStruct` is non-empty - --> $DIR/indirect_match_same_crate.rs:38:11 - | -LL | match x {} - | ^ - | -note: `IndirectUninhabitedStruct` defined here - --> $DIR/indirect_match_same_crate.rs:22:12 - | -LL | pub struct IndirectUninhabitedStruct(UninhabitedStruct); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the matched value is of type `IndirectUninhabitedStruct` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedTupleStruct` is non-empty - --> $DIR/indirect_match_same_crate.rs:42:11 - | -LL | match x {} - | ^ - | -note: `IndirectUninhabitedTupleStruct` defined here - --> $DIR/indirect_match_same_crate.rs:24:12 - | -LL | pub struct IndirectUninhabitedTupleStruct(UninhabitedTupleStruct); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the matched value is of type `IndirectUninhabitedTupleStruct` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedVariants` is non-empty - --> $DIR/indirect_match_same_crate.rs:48:11 - | -LL | match x {} - | ^ - | -note: `IndirectUninhabitedVariants` defined here - --> $DIR/indirect_match_same_crate.rs:26:12 - | -LL | pub struct IndirectUninhabitedVariants(UninhabitedVariants); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the matched value is of type `IndirectUninhabitedVariants` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match x { -LL + _ => todo!(), -LL ~ } - | - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs index c40a2676e84c..dd9a570522a2 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs @@ -1,6 +1,5 @@ //@ aux-build:uninhabited.rs #![deny(unreachable_patterns)] -#![feature(min_exhaustive_patterns)] #![feature(never_type)] extern crate uninhabited; diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr index ef97c1fa17f3..745b196a0e3a 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedEnum` is non-empty - --> $DIR/indirect_match_with_exhaustive_patterns.rs:23:11 + --> $DIR/indirect_match_with_exhaustive_patterns.rs:22:11 | LL | match x {} | ^ @@ -18,7 +18,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedStruct` is non-empty - --> $DIR/indirect_match_with_exhaustive_patterns.rs:27:11 + --> $DIR/indirect_match_with_exhaustive_patterns.rs:26:11 | LL | match x {} | ^ @@ -37,7 +37,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedTupleStruct` is non-empty - --> $DIR/indirect_match_with_exhaustive_patterns.rs:31:11 + --> $DIR/indirect_match_with_exhaustive_patterns.rs:30:11 | LL | match x {} | ^ @@ -56,7 +56,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedVariants` is non-empty - --> $DIR/indirect_match_with_exhaustive_patterns.rs:37:11 + --> $DIR/indirect_match_with_exhaustive_patterns.rs:36:11 | LL | match x {} | ^ diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs index efaec0ebdbe3..32f5f504136a 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs @@ -1,7 +1,6 @@ //@ check-pass #![deny(unreachable_patterns)] -#![feature(min_exhaustive_patterns)] #![feature(never_type)] #[non_exhaustive] diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs index ebbdfba15f3a..04f7fe26b5ae 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs @@ -1,3 +1,4 @@ +//@ check-pass #![feature(never_type)] #[non_exhaustive] @@ -27,15 +28,15 @@ fn cannot_empty_match_on_empty_enum_to_anything(x: UninhabitedEnum) -> A { } fn cannot_empty_match_on_empty_struct_to_anything(x: UninhabitedStruct) -> A { - match x {} //~ ERROR non-exhaustive patterns + match x {} } fn cannot_empty_match_on_empty_tuple_struct_to_anything(x: UninhabitedTupleStruct) -> A { - match x {} //~ ERROR non-exhaustive patterns + match x {} } fn cannot_empty_match_on_enum_with_empty_variants_struct_to_anything(x: UninhabitedVariants) -> A { - match x {} //~ ERROR non-exhaustive patterns + match x {} } fn main() {} diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr deleted file mode 100644 index 7a12aca8520d..000000000000 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr +++ /dev/null @@ -1,64 +0,0 @@ -error[E0004]: non-exhaustive patterns: type `UninhabitedStruct` is non-empty - --> $DIR/match_same_crate.rs:30:11 - | -LL | match x {} - | ^ - | -note: `UninhabitedStruct` defined here - --> $DIR/match_same_crate.rs:8:12 - | -LL | pub struct UninhabitedStruct { - | ^^^^^^^^^^^^^^^^^ - = note: the matched value is of type `UninhabitedStruct` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: type `UninhabitedTupleStruct` is non-empty - --> $DIR/match_same_crate.rs:34:11 - | -LL | match x {} - | ^ - | -note: `UninhabitedTupleStruct` defined here - --> $DIR/match_same_crate.rs:13:12 - | -LL | pub struct UninhabitedTupleStruct(!); - | ^^^^^^^^^^^^^^^^^^^^^^ - = note: the matched value is of type `UninhabitedTupleStruct` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered - --> $DIR/match_same_crate.rs:38:11 - | -LL | match x {} - | ^ patterns `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered - | -note: `UninhabitedVariants` defined here - --> $DIR/match_same_crate.rs:15:10 - | -LL | pub enum UninhabitedVariants { - | ^^^^^^^^^^^^^^^^^^^ -LL | #[non_exhaustive] Tuple(!), - | ----- not covered -LL | #[non_exhaustive] Struct { x: ! } - | ------ not covered - = note: the matched value is of type `UninhabitedVariants` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ match x { -LL + UninhabitedVariants::Tuple(_) | UninhabitedVariants::Struct { .. } => todo!(), -LL ~ } - | - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs index 69b15fca0b72..108cac7099eb 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs @@ -1,6 +1,5 @@ //@ aux-build:uninhabited.rs #![deny(unreachable_patterns)] -#![feature(min_exhaustive_patterns)] #![feature(never_type)] extern crate uninhabited; diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr index 19e2546b0da8..0c8b14ab69df 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: type `UninhabitedEnum` is non-empty - --> $DIR/match_with_exhaustive_patterns.rs:22:11 + --> $DIR/match_with_exhaustive_patterns.rs:21:11 | LL | match x {} | ^ @@ -18,7 +18,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `UninhabitedStruct` is non-empty - --> $DIR/match_with_exhaustive_patterns.rs:26:11 + --> $DIR/match_with_exhaustive_patterns.rs:25:11 | LL | match x {} | ^ @@ -37,7 +37,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: type `UninhabitedTupleStruct` is non-empty - --> $DIR/match_with_exhaustive_patterns.rs:30:11 + --> $DIR/match_with_exhaustive_patterns.rs:29:11 | LL | match x {} | ^ @@ -56,7 +56,7 @@ LL ~ } | error[E0004]: non-exhaustive patterns: `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered - --> $DIR/match_with_exhaustive_patterns.rs:34:11 + --> $DIR/match_with_exhaustive_patterns.rs:33:11 | LL | match x {} | ^ patterns `UninhabitedVariants::Tuple(_)` and `UninhabitedVariants::Struct { .. }` not covered diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs index bbc5d03d6126..468703c78e06 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs @@ -1,7 +1,6 @@ //@ check-pass #![deny(unreachable_patterns)] -#![feature(min_exhaustive_patterns)] #![feature(never_type)] #[non_exhaustive] diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns.rs index 0007614988cd..be55ad51578b 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns.rs @@ -1,7 +1,6 @@ //@ aux-build:uninhabited.rs //@ build-pass (FIXME(62277): could be check-pass?) #![deny(unreachable_patterns)] -#![feature(min_exhaustive_patterns)] extern crate uninhabited; diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs index 898be87cccab..1194d7b858d6 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs @@ -1,5 +1,4 @@ #![deny(unreachable_patterns)] -#![feature(min_exhaustive_patterns)] #![feature(never_type)] #[non_exhaustive] diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr index d5192a70ed55..c399bb9083fb 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr @@ -1,5 +1,5 @@ error: unreachable pattern - --> $DIR/patterns_same_crate.rs:52:9 + --> $DIR/patterns_same_crate.rs:51:9 | LL | Some(_x) => (), | ^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/patterns_same_crate.rs:57:9 + --> $DIR/patterns_same_crate.rs:56:9 | LL | Some(_x) => (), | ^^^^^^^^ @@ -20,7 +20,7 @@ LL | Some(_x) => (), = note: this pattern matches no values because `UninhabitedVariants` is uninhabited error: unreachable pattern - --> $DIR/patterns_same_crate.rs:61:15 + --> $DIR/patterns_same_crate.rs:60:15 | LL | while let PartiallyInhabitedVariants::Struct { x } = partially_inhabited_variant() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | while let PartiallyInhabitedVariants::Struct { x } = partially_inhabite = note: this pattern matches no values because `!` is uninhabited error: unreachable pattern - --> $DIR/patterns_same_crate.rs:65:15 + --> $DIR/patterns_same_crate.rs:64:15 | LL | while let Some(_x) = uninhabited_struct() { | ^^^^^^^^ @@ -36,7 +36,7 @@ LL | while let Some(_x) = uninhabited_struct() { = note: this pattern matches no values because `UninhabitedStruct` is uninhabited error: unreachable pattern - --> $DIR/patterns_same_crate.rs:68:15 + --> $DIR/patterns_same_crate.rs:67:15 | LL | while let Some(_x) = uninhabited_tuple_struct() { | ^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr index c2227f8e8478..1ddf05b40a60 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/safe-calls.stderr @@ -4,8 +4,8 @@ error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and req LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: sse and sse2 - = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target feature: sse2 + = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:29:5 @@ -13,8 +13,7 @@ error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx, sse, sse2, sse3, sse4.1, sse4.2, ssse3, and bmi2 - = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:31:5 @@ -22,8 +21,7 @@ error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsa LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx, sse, sse2, sse3, sse4.1, sse4.2, ssse3, and bmi2 - = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:38:5 @@ -31,7 +29,7 @@ error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and LL | avx_bmi2(); | ^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx, sse3, sse4.1, sse4.2, ssse3, and bmi2 + = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:40:5 @@ -39,7 +37,7 @@ error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsa LL | Quux.avx_bmi2(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: avx, sse3, sse4.1, sse4.2, ssse3, and bmi2 + = help: in order for the call to be safe, the context requires the following additional target features: avx and bmi2 error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:47:5 @@ -63,8 +61,8 @@ error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and req LL | const _: () = sse2(); | ^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: sse and sse2 - = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target feature: sse2 + = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsafe and requires unsafe function or block --> $DIR/safe-calls.rs:64:15 @@ -72,8 +70,8 @@ error[E0133]: call to function `sse2_and_fxsr` with `#[target_feature]` is unsaf LL | const _: () = sse2_and_fxsr(); | ^^^^^^^^^^^^^^^ call to function with `#[target_feature]` | - = help: in order for the call to be safe, the context requires the following additional target features: sse, sse2, and fxsr - = note: the fxsr, sse, and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target features: sse2 and fxsr + = note: the fxsr and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe block --> $DIR/safe-calls.rs:69:5 @@ -82,8 +80,8 @@ LL | sse2(); | ^^^^^^ call to function with `#[target_feature]` | = note: for more information, see issue #71668 - = help: in order for the call to be safe, the context requires the following additional target features: sse and sse2 - = note: the sse and sse2 target features being enabled in the build configuration does not remove the requirement to list them in `#[target_feature]` + = help: in order for the call to be safe, the context requires the following additional target feature: sse2 + = note: the sse2 target feature being enabled in the build configuration does not remove the requirement to list it in `#[target_feature]` note: an unsafe function restricts its caller, but its body is safe by default --> $DIR/safe-calls.rs:68:1 | diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.precise.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.precise.stderr index 9d1ca5dbf2c0..b451555ec3cc 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.precise.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.precise.stderr @@ -40,7 +40,7 @@ error: `~const` can only be applied to `#[const_trait]` traits LL | const fn a(_: T) {} | ^^^^^^^^ -error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `foo` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const-drop.rs:54:5 | LL | #[const_trait] @@ -49,7 +49,7 @@ LL | pub trait SomeTrait { LL | fn foo(); | - expected 0 const parameters -error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `foo` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const-drop.rs:54:5 | LL | #[const_trait] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.stock.stderr index 2f93f9a6743b..296614f7fd07 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.stock.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.stock.stderr @@ -40,7 +40,7 @@ error: `~const` can only be applied to `#[const_trait]` traits LL | const fn a(_: T) {} | ^^^^^^^^ -error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `foo` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const-drop.rs:54:5 | LL | #[const_trait] @@ -49,7 +49,7 @@ LL | pub trait SomeTrait { LL | fn foo(); | - expected 0 const parameters -error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `foo` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const-drop.rs:54:5 | LL | #[const_trait] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr index b4c4cf0a8905..7643697874f2 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr @@ -1,4 +1,4 @@ -error[E0049]: method `bar` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `bar` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const-default-bound-non-const-specialized-bound.rs:16:1 | LL | #[const_trait] @@ -16,7 +16,7 @@ LL | | T: Foo, //FIXME ~ ERROR missing `~const` qualifier LL | | T: Specialize, | |__________________^ -error[E0049]: method `baz` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `baz` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const-default-bound-non-const-specialized-bound.rs:36:1 | LL | #[const_trait] @@ -25,7 +25,7 @@ LL | trait Baz { LL | fn baz(); | - expected 0 const parameters -error[E0049]: method `baz` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `baz` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const-default-bound-non-const-specialized-bound.rs:36:1 | LL | #[const_trait] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.stderr index cabf201405f5..9b2ae8d739c8 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.stderr @@ -1,4 +1,4 @@ -error[E0049]: method `value` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `value` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const-default-const-specialized.rs:10:1 | LL | #[const_trait] @@ -7,7 +7,7 @@ LL | trait Value { LL | fn value() -> u32; | - expected 0 const parameters -error[E0049]: method `value` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `value` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const-default-const-specialized.rs:10:1 | LL | #[const_trait] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/default-keyword.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/default-keyword.stderr index 52c8708f2c88..18a25045f4b0 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/default-keyword.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/default-keyword.stderr @@ -1,4 +1,4 @@ -error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `foo` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/default-keyword.rs:7:1 | LL | #[const_trait] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.stderr index 1aa34637ca48..ecdc7b930e60 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.stderr @@ -1,4 +1,4 @@ -error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `foo` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/issue-95186-specialize-on-tilde-const.rs:14:1 | LL | #[const_trait] @@ -7,7 +7,7 @@ LL | trait Foo { LL | fn foo(); | - expected 0 const parameters -error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `foo` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/issue-95186-specialize-on-tilde-const.rs:14:1 | LL | #[const_trait] @@ -18,7 +18,7 @@ LL | fn foo(); | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0049]: method `bar` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `bar` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/issue-95186-specialize-on-tilde-const.rs:30:1 | LL | #[const_trait] @@ -27,7 +27,7 @@ LL | trait Bar { LL | fn bar() {} | - expected 0 const parameters -error[E0049]: method `bar` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `bar` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/issue-95186-specialize-on-tilde-const.rs:30:1 | LL | #[const_trait] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.stderr index 0e0f391b0865..6679bb465378 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.stderr @@ -1,4 +1,4 @@ -error[E0049]: method `bar` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `bar` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/issue-95187-same-trait-bound-different-constness.rs:18:1 | LL | #[const_trait] @@ -7,7 +7,7 @@ LL | trait Bar { LL | fn bar(); | - expected 0 const parameters -error[E0049]: method `bar` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `bar` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/issue-95187-same-trait-bound-different-constness.rs:18:1 | LL | #[const_trait] @@ -18,7 +18,7 @@ LL | fn bar(); | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0049]: method `baz` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `baz` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/issue-95187-same-trait-bound-different-constness.rs:38:1 | LL | #[const_trait] @@ -27,7 +27,7 @@ LL | trait Baz { LL | fn baz(); | - expected 0 const parameters -error[E0049]: method `baz` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `baz` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/issue-95187-same-trait-bound-different-constness.rs:38:1 | LL | #[const_trait] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr index d49beb932591..7f363922947f 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.stderr @@ -1,4 +1,4 @@ -error[E0049]: method `value` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `value` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/non-const-default-const-specialized.rs:9:1 | LL | #[const_trait] @@ -7,7 +7,7 @@ LL | trait Value { LL | fn value() -> u32; | - expected 0 const parameters -error[E0049]: method `value` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `value` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/non-const-default-const-specialized.rs:9:1 | LL | #[const_trait] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr index d082cd6de609..bf273f349b4b 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr @@ -1,4 +1,4 @@ -error[E0049]: method `a` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `a` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/specializing-constness-2.rs:9:1 | LL | #[const_trait] @@ -7,7 +7,7 @@ LL | pub trait A { LL | fn a() -> u32; | - expected 0 const parameters -error[E0049]: method `a` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `a` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/specializing-constness-2.rs:9:1 | LL | #[const_trait] diff --git a/tests/ui/rust-2018/auxiliary/edition-lint-infer-outlives-macro.rs b/tests/ui/rust-2018/edition-lint-inter-outlives/auxiliary/edition-lint-infer-outlives-macro.rs similarity index 100% rename from tests/ui/rust-2018/auxiliary/edition-lint-infer-outlives-macro.rs rename to tests/ui/rust-2018/edition-lint-inter-outlives/auxiliary/edition-lint-infer-outlives-macro.rs diff --git a/tests/ui/rust-2018/edition-lint-infer-outlives-macro.fixed b/tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives-macro.fixed similarity index 100% rename from tests/ui/rust-2018/edition-lint-infer-outlives-macro.fixed rename to tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives-macro.fixed diff --git a/tests/ui/rust-2018/edition-lint-infer-outlives-macro.rs b/tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives-macro.rs similarity index 100% rename from tests/ui/rust-2018/edition-lint-infer-outlives-macro.rs rename to tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives-macro.rs diff --git a/tests/ui/rust-2018/edition-lint-infer-outlives-macro.stderr b/tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives-macro.stderr similarity index 100% rename from tests/ui/rust-2018/edition-lint-infer-outlives-macro.stderr rename to tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives-macro.stderr diff --git a/tests/ui/rust-2018/edition-lint-infer-outlives-multispan.rs b/tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives-multispan.rs similarity index 100% rename from tests/ui/rust-2018/edition-lint-infer-outlives-multispan.rs rename to tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives-multispan.rs diff --git a/tests/ui/rust-2018/edition-lint-infer-outlives-multispan.stderr b/tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives-multispan.stderr similarity index 100% rename from tests/ui/rust-2018/edition-lint-infer-outlives-multispan.stderr rename to tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives-multispan.stderr diff --git a/tests/ui/rust-2018/edition-lint-infer-outlives.fixed b/tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives.fixed similarity index 100% rename from tests/ui/rust-2018/edition-lint-infer-outlives.fixed rename to tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives.fixed diff --git a/tests/ui/rust-2018/edition-lint-infer-outlives.rs b/tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives.rs similarity index 100% rename from tests/ui/rust-2018/edition-lint-infer-outlives.rs rename to tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives.rs diff --git a/tests/ui/rust-2018/edition-lint-infer-outlives.stderr b/tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives.stderr similarity index 100% rename from tests/ui/rust-2018/edition-lint-infer-outlives.stderr rename to tests/ui/rust-2018/edition-lint-inter-outlives/edition-lint-infer-outlives.stderr diff --git a/tests/ui/rust-2018/edition-lint-inter-outlives/explicit-outlives-recursive-119228.fixed b/tests/ui/rust-2018/edition-lint-inter-outlives/explicit-outlives-recursive-119228.fixed new file mode 100644 index 000000000000..7b9fac8f408a --- /dev/null +++ b/tests/ui/rust-2018/edition-lint-inter-outlives/explicit-outlives-recursive-119228.fixed @@ -0,0 +1,41 @@ +//@ run-rustfix +//@ check-pass +#![deny(explicit_outlives_requirements)] + +pub trait TypeCx { + type Ty; +} + +pub struct Pat { + pub ty: Cx::Ty, +} + +// Simple recursive case: no warning +pub struct MyTypeContextSimpleRecursive<'thir, 'tcx: 'thir> { + pub pat: Pat>, +} +impl<'thir, 'tcx: 'thir> TypeCx for MyTypeContextSimpleRecursive<'thir, 'tcx> { + type Ty = (); +} + +// Non-recursive case: we want a warning +pub struct MyTypeContextNotRecursive<'thir, 'tcx: 'thir> { + pub tcx: &'tcx (), + pub thir: &'thir (), +} +impl<'thir, 'tcx: 'thir> TypeCx for MyTypeContextNotRecursive<'thir, 'tcx> { + type Ty = (); +} + + +// Mixed-recursive case: we want a warning +pub struct MyTypeContextMixedRecursive<'thir, 'tcx: 'thir> { + pub pat: Pat>, + pub tcx: &'tcx (), + pub thir: &'thir (), +} +impl<'thir, 'tcx: 'thir> TypeCx for MyTypeContextMixedRecursive<'thir, 'tcx> { + type Ty = (); +} + +fn main() {} diff --git a/tests/ui/rust-2018/edition-lint-inter-outlives/explicit-outlives-recursive-119228.rs b/tests/ui/rust-2018/edition-lint-inter-outlives/explicit-outlives-recursive-119228.rs new file mode 100644 index 000000000000..7b9fac8f408a --- /dev/null +++ b/tests/ui/rust-2018/edition-lint-inter-outlives/explicit-outlives-recursive-119228.rs @@ -0,0 +1,41 @@ +//@ run-rustfix +//@ check-pass +#![deny(explicit_outlives_requirements)] + +pub trait TypeCx { + type Ty; +} + +pub struct Pat { + pub ty: Cx::Ty, +} + +// Simple recursive case: no warning +pub struct MyTypeContextSimpleRecursive<'thir, 'tcx: 'thir> { + pub pat: Pat>, +} +impl<'thir, 'tcx: 'thir> TypeCx for MyTypeContextSimpleRecursive<'thir, 'tcx> { + type Ty = (); +} + +// Non-recursive case: we want a warning +pub struct MyTypeContextNotRecursive<'thir, 'tcx: 'thir> { + pub tcx: &'tcx (), + pub thir: &'thir (), +} +impl<'thir, 'tcx: 'thir> TypeCx for MyTypeContextNotRecursive<'thir, 'tcx> { + type Ty = (); +} + + +// Mixed-recursive case: we want a warning +pub struct MyTypeContextMixedRecursive<'thir, 'tcx: 'thir> { + pub pat: Pat>, + pub tcx: &'tcx (), + pub thir: &'thir (), +} +impl<'thir, 'tcx: 'thir> TypeCx for MyTypeContextMixedRecursive<'thir, 'tcx> { + type Ty = (); +} + +fn main() {} diff --git a/tests/ui/specialization/const_trait_impl.stderr b/tests/ui/specialization/const_trait_impl.stderr index e39138983c6e..643f1de3e8d7 100644 --- a/tests/ui/specialization/const_trait_impl.stderr +++ b/tests/ui/specialization/const_trait_impl.stderr @@ -1,4 +1,4 @@ -error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `foo` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const_trait_impl.rs:6:1 | LL | #[const_trait] @@ -7,7 +7,7 @@ LL | pub unsafe trait Sup { LL | fn foo() -> u32; | - expected 0 const parameters -error[E0049]: method `foo` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `foo` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const_trait_impl.rs:6:1 | LL | #[const_trait] @@ -36,7 +36,7 @@ error: `~const` can only be applied to `#[const_trait]` traits LL | impl const A for T { | ^^^^^^^ -error[E0049]: method `a` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `a` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const_trait_impl.rs:29:1 | LL | #[const_trait] @@ -45,7 +45,7 @@ LL | pub trait A { LL | fn a() -> u32; | - expected 0 const parameters -error[E0049]: method `a` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `a` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const_trait_impl.rs:29:1 | LL | #[const_trait] @@ -56,7 +56,7 @@ LL | fn a() -> u32; | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0049]: method `a` has 1 const parameter but its trait declaration has 0 const parameters +error[E0049]: associated function `a` has 1 const parameter but its trait declaration has 0 const parameters --> $DIR/const_trait_impl.rs:29:1 | LL | #[const_trait] diff --git a/tests/ui/statics/unsizing-wfcheck-issue-127299.rs b/tests/ui/statics/unsizing-wfcheck-issue-127299.rs new file mode 100644 index 000000000000..cd15be54ec78 --- /dev/null +++ b/tests/ui/statics/unsizing-wfcheck-issue-127299.rs @@ -0,0 +1,17 @@ +// This ensures we don't ICE in situations like rust-lang/rust#127299. + +trait Qux { + fn bar() -> i32; +} + +pub struct Lint { + pub desc: &'static dyn Qux, + //~^ ERROR cannot be made into an object +} + +static FOO: &Lint = &Lint { desc: "desc" }; +//~^ ERROR cannot be shared between threads safely +//~| ERROR cannot be made into an object +//~| ERROR cannot be made into an object + +fn main() {} diff --git a/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr new file mode 100644 index 000000000000..49e8d87f3547 --- /dev/null +++ b/tests/ui/statics/unsizing-wfcheck-issue-127299.stderr @@ -0,0 +1,87 @@ +error[E0038]: the trait `Qux` cannot be made into an object + --> $DIR/unsizing-wfcheck-issue-127299.rs:8:24 + | +LL | pub desc: &'static dyn Qux, + | ^^^^^^^ `Qux` 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 + --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 + | +LL | trait Qux { + | --- this trait cannot be made into an object... +LL | fn bar() -> i32; + | ^^^ ...because associated function `bar` has no `self` parameter +help: consider turning `bar` into a method by giving it a `&self` argument + | +LL | fn bar(&self) -> i32; + | +++++ +help: alternatively, consider constraining `bar` so it does not apply to trait objects + | +LL | fn bar() -> i32 where Self: Sized; + | +++++++++++++++++ + +error[E0277]: `(dyn Qux + 'static)` cannot be shared between threads safely + --> $DIR/unsizing-wfcheck-issue-127299.rs:12:13 + | +LL | static FOO: &Lint = &Lint { desc: "desc" }; + | ^^^^^ `(dyn Qux + 'static)` cannot be shared between threads safely + | + = help: within `&'static Lint`, the trait `Sync` is not implemented for `(dyn Qux + 'static)`, which is required by `&'static Lint: Sync` + = note: required because it appears within the type `&'static (dyn Qux + 'static)` +note: required because it appears within the type `Lint` + --> $DIR/unsizing-wfcheck-issue-127299.rs:7:12 + | +LL | pub struct Lint { + | ^^^^ + = note: required because it appears within the type `&'static Lint` + = note: shared static variables must have a type that implements `Sync` + +error[E0038]: the trait `Qux` cannot be made into an object + --> $DIR/unsizing-wfcheck-issue-127299.rs:12:35 + | +LL | static FOO: &Lint = &Lint { desc: "desc" }; + | ^^^^^^ `Qux` 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 + --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 + | +LL | trait Qux { + | --- this trait cannot be made into an object... +LL | fn bar() -> i32; + | ^^^ ...because associated function `bar` has no `self` parameter + = note: required for the cast from `&'static str` to `&'static (dyn Qux + 'static)` +help: consider turning `bar` into a method by giving it a `&self` argument + | +LL | fn bar(&self) -> i32; + | +++++ +help: alternatively, consider constraining `bar` so it does not apply to trait objects + | +LL | fn bar() -> i32 where Self: Sized; + | +++++++++++++++++ + +error[E0038]: the trait `Qux` cannot be made into an object + --> $DIR/unsizing-wfcheck-issue-127299.rs:12:35 + | +LL | static FOO: &Lint = &Lint { desc: "desc" }; + | ^^^^^^ `Qux` 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 + --> $DIR/unsizing-wfcheck-issue-127299.rs:4:8 + | +LL | trait Qux { + | --- this trait cannot be made into an object... +LL | fn bar() -> i32; + | ^^^ ...because associated function `bar` has no `self` parameter +help: consider turning `bar` into a method by giving it a `&self` argument + | +LL | fn bar(&self) -> i32; + | +++++ +help: alternatively, consider constraining `bar` so it does not apply to trait objects + | +LL | fn bar() -> i32 where Self: Sized; + | +++++++++++++++++ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0038, E0277. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/structs/field-implied-unsizing-wfcheck.rs b/tests/ui/structs/field-implied-unsizing-wfcheck.rs new file mode 100644 index 000000000000..a46c9b5a68b4 --- /dev/null +++ b/tests/ui/structs/field-implied-unsizing-wfcheck.rs @@ -0,0 +1,32 @@ +struct FooStruct { + nested: &'static Bar, + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time +} + +struct FooTuple(&'static Bar); +//~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + +enum FooEnum1 { + Struct { nested: &'static Bar }, + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time +} + +enum FooEnum2 { + Tuple(&'static Bar), + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time +} + +struct Bar(T); + +fn main() { + // Ensure there's an error at the construction site, for error tainting purposes. + + FooStruct { nested: &Bar(4) }; + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + FooTuple(&Bar(4)); + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + FooEnum1::Struct { nested: &Bar(4) }; + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + FooEnum2::Tuple(&Bar(4)); + //~^ ERROR the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time +} diff --git a/tests/ui/structs/field-implied-unsizing-wfcheck.stderr b/tests/ui/structs/field-implied-unsizing-wfcheck.stderr new file mode 100644 index 000000000000..47d486ffa331 --- /dev/null +++ b/tests/ui/structs/field-implied-unsizing-wfcheck.stderr @@ -0,0 +1,163 @@ +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:2:13 + | +LL | nested: &'static Bar, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:6:17 + | +LL | struct FooTuple(&'static Bar); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:10:22 + | +LL | Struct { nested: &'static Bar }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:15:11 + | +LL | Tuple(&'static Bar), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:24:25 + | +LL | FooStruct { nested: &Bar(4) }; + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:26:14 + | +LL | FooTuple(&Bar(4)); + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:28:32 + | +LL | FooEnum1::Struct { nested: &Bar(4) }; + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time + --> $DIR/field-implied-unsizing-wfcheck.rs:30:21 + | +LL | FooEnum2::Tuple(&Bar(4)); + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` +note: required by an implicit `Sized` bound in `Bar` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` +help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` + --> $DIR/field-implied-unsizing-wfcheck.rs:19:12 + | +LL | struct Bar(T); + | ^ - ...if indirection were used here: `Box` + | | + | this could be changed to `T: ?Sized`... + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0277`. 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 index aeeec3aca340..3a1f685f16b7 100644 --- 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 @@ -2,18 +2,13 @@ error[E0700]: hidden type for `impl Iterator` captures lifetime that --> $DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:7:5 | LL | fn bar(src: &crate::Foo) -> impl Iterator { - | ---------- ------------------------- opaque type defined here - | | - | hidden type `FilterMap, {closure@$DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:9:21: 9:24}>` captures the anonymous lifetime defined here + | ------------------------- opaque type defined here LL | / [0].into_iter() LL | | LL | | .filter_map(|_| foo(src)) | |_________________________________^ | -help: to declare that `impl Iterator` captures `'_`, you can introduce a named lifetime parameter `'a` - | -LL | fn bar<'a>(src: &'a crate::Foo<'a>) -> impl Iterator + 'a { - | ++++ ++ ++++ ++++ + = note: hidden type `FilterMap, {closure@$DIR/explicit-lifetime-suggestion-in-proper-span-issue-121267.rs:9:21: 9:24}>` captures lifetime `'_` error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr index a2a78ddc7059..db16fff826fa 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr @@ -44,15 +44,13 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct AABB { LL | pub loc: Vector2, - | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `K`, which is required by `Vector2: Clone` + | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `K` | -note: required for `Vector2` to implement `Clone` - --> $DIR/missing-bound-in-derive-copy-impl-2.rs:4:23 +note: required by a bound in `Vector2` + --> $DIR/missing-bound-in-derive-copy-impl-2.rs:5:31 | -LL | #[derive(Debug, Copy, Clone)] - | ^^^^^ LL | pub struct Vector2 { - | ---- unsatisfied trait bound introduced in this `derive` macro + | ^^^^ required by this bound in `Vector2` = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider further restricting this bound | diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr index 2ae0871b815e..cf383b5c8ff0 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr @@ -95,15 +95,13 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct AABB { LL | pub loc: Vector2, - | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `K`, which is required by `Vector2: Clone` + | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `K` | -note: required for `Vector2` to implement `Clone` - --> $DIR/missing-bound-in-derive-copy-impl.rs:3:23 +note: required by a bound in `Vector2` + --> $DIR/missing-bound-in-derive-copy-impl.rs:4:31 | -LL | #[derive(Debug, Copy, Clone)] - | ^^^^^ LL | pub struct Vector2 { - | ---- unsatisfied trait bound introduced in this `derive` macro + | ^^^^ required by this bound in `Vector2` = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider restricting type parameter `K` | diff --git a/tests/ui/try-trait/try-operator-custom.rs b/tests/ui/try-trait/try-operator-custom.rs index ab0772dd228b..936c0b0689ad 100644 --- a/tests/ui/try-trait/try-operator-custom.rs +++ b/tests/ui/try-trait/try-operator-custom.rs @@ -31,7 +31,6 @@ impl Try for MyResult { impl FromResidual> for MyResult where V: Into { fn from_residual(x: MyResult) -> Self { match x { - MyResult::Awesome(u) => match u {}, MyResult::Terrible(e) => MyResult::Terrible(e.into()), } } @@ -42,7 +41,6 @@ type ResultResidual = Result; impl FromResidual> for MyResult where V: Into { fn from_residual(x: ResultResidual) -> Self { match x { - Ok(v) => match v {} Err(e) => MyResult::Terrible(e.into()), } } @@ -51,7 +49,6 @@ impl FromResidual> for MyResult where V: Into FromResidual> for Result where V: Into { fn from_residual(x: MyResult) -> Self { match x { - MyResult::Awesome(u) => match u {}, MyResult::Terrible(e) => Err(e.into()), } } diff --git a/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr b/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr index 6f74b89d2247..03e91b52a2aa 100644 --- a/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr +++ b/tests/ui/type/pattern_types/feature-gate-pattern_types.stderr @@ -4,6 +4,7 @@ error[E0658]: use of unstable library feature 'core_pattern_type' LL | type NonNullU32 = pattern_type!(u32 is 1..); | ^^^^^^^^^^^^ | + = note: see issue #123646 for more information = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -13,6 +14,7 @@ error[E0658]: use of unstable library feature 'core_pattern_type' LL | type Percent = pattern_type!(u32 is 0..=100); | ^^^^^^^^^^^^ | + = note: see issue #123646 for more information = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -22,6 +24,7 @@ error[E0658]: use of unstable library feature 'core_pattern_type' LL | type Negative = pattern_type!(i32 is ..=0); | ^^^^^^^^^^^^ | + = note: see issue #123646 for more information = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -31,6 +34,7 @@ error[E0658]: use of unstable library feature 'core_pattern_type' LL | type Positive = pattern_type!(i32 is 0..); | ^^^^^^^^^^^^ | + = note: see issue #123646 for more information = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date @@ -40,6 +44,7 @@ error[E0658]: use of unstable library feature 'core_pattern_type' LL | type Always = pattern_type!(Option is Some(_)); | ^^^^^^^^^^^^ | + = note: see issue #123646 for more information = help: add `#![feature(core_pattern_type)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/typeck/issue-36708.stderr b/tests/ui/typeck/issue-36708.stderr index 3589796b6aac..0aca575320f2 100644 --- a/tests/ui/typeck/issue-36708.stderr +++ b/tests/ui/typeck/issue-36708.stderr @@ -1,4 +1,4 @@ -error[E0049]: method `foo` has 1 type parameter but its trait declaration has 0 type parameters +error[E0049]: associated function `foo` has 1 type parameter but its trait declaration has 0 type parameters --> $DIR/issue-36708.rs:8:12 | LL | fn foo() {} diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.rs b/tests/ui/typeck/suggest-arg-comma-delete-ice.rs new file mode 100644 index 000000000000..48d02e13eca0 --- /dev/null +++ b/tests/ui/typeck/suggest-arg-comma-delete-ice.rs @@ -0,0 +1,19 @@ +//! Previously, we tried to remove extra arg commas when providing extra arg removal suggestions. +//! One of the edge cases is having to account for an arg that has a closing delimiter `)` +//! following it. However, the previous suggestion code assumed that the delimiter is in fact +//! exactly the 1-byte `)` character. This assumption was proven incorrect, because we recover +//! from Unicode-confusable delimiters in the parser, which means that the ending delimiter could be +//! a multi-byte codepoint that looks *like* a `)`. Subtracing 1 byte could land us in the middle of +//! a codepoint, triggering a codepoint boundary assertion. +//! +//! issue: rust-lang/rust#128717 + +fn main() { + // The following example has been modified from #128717 to remove irrelevant Unicode as they do + // not otherwise partake in the right delimiter calculation causing the codepoint boundary + // assertion. + main(rahh); + //~^ ERROR unknown start of token + //~| ERROR this function takes 0 arguments but 1 argument was supplied + //~| ERROR cannot find value `rahh` in this scope +} diff --git a/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr b/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr new file mode 100644 index 000000000000..53608391f3c8 --- /dev/null +++ b/tests/ui/typeck/suggest-arg-comma-delete-ice.stderr @@ -0,0 +1,38 @@ +error: unknown start of token: \u{ff09} + --> $DIR/suggest-arg-comma-delete-ice.rs:15:14 + | +LL | main(rahh); + | ^^ + | +help: Unicode character ')' (Fullwidth Right Parenthesis) looks like ')' (Right Parenthesis), but it is not + | +LL | main(rahh); + | ~ + +error[E0425]: cannot find value `rahh` in this scope + --> $DIR/suggest-arg-comma-delete-ice.rs:15:10 + | +LL | main(rahh); + | ^^^^ not found in this scope + +error[E0061]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/suggest-arg-comma-delete-ice.rs:15:5 + | +LL | main(rahh); + | ^^^^ ---- unexpected argument + | +note: function defined here + --> $DIR/suggest-arg-comma-delete-ice.rs:11:4 + | +LL | fn main() { + | ^^^^ +help: remove the extra argument + | +LL - main(rahh); +LL + main(); + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0061, E0425. +For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/uninhabited/exhaustive-wo-nevertype-issue-51221.rs b/tests/ui/uninhabited/exhaustive-wo-nevertype-issue-51221.rs index 3130fb3fe9e6..722d9b4ed292 100644 --- a/tests/ui/uninhabited/exhaustive-wo-nevertype-issue-51221.rs +++ b/tests/ui/uninhabited/exhaustive-wo-nevertype-issue-51221.rs @@ -1,7 +1,5 @@ //@ check-pass -#![feature(min_exhaustive_patterns)] - enum Void {} fn main() { let a: Option = None; diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.exhaustive_patterns.stderr b/tests/ui/uninhabited/uninhabited-irrefutable.exhaustive_patterns.stderr index bc1a9fa41915..50f33607c06f 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.exhaustive_patterns.stderr +++ b/tests/ui/uninhabited/uninhabited-irrefutable.exhaustive_patterns.stderr @@ -1,5 +1,5 @@ error[E0005]: refutable pattern in local binding - --> $DIR/uninhabited-irrefutable.rs:31:9 + --> $DIR/uninhabited-irrefutable.rs:30:9 | LL | let Foo::D(_y, _z) = x; | ^^^^^^^^^^^^^^ pattern `Foo::A(_)` not covered @@ -7,7 +7,7 @@ LL | let Foo::D(_y, _z) = x; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `Foo` defined here - --> $DIR/uninhabited-irrefutable.rs:20:6 + --> $DIR/uninhabited-irrefutable.rs:19:6 | LL | enum Foo { | ^^^ diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.normal.stderr b/tests/ui/uninhabited/uninhabited-irrefutable.normal.stderr new file mode 100644 index 000000000000..50f33607c06f --- /dev/null +++ b/tests/ui/uninhabited/uninhabited-irrefutable.normal.stderr @@ -0,0 +1,26 @@ +error[E0005]: refutable pattern in local binding + --> $DIR/uninhabited-irrefutable.rs:30:9 + | +LL | let Foo::D(_y, _z) = x; + | ^^^^^^^^^^^^^^ pattern `Foo::A(_)` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html +note: `Foo` defined here + --> $DIR/uninhabited-irrefutable.rs:19:6 + | +LL | enum Foo { + | ^^^ +LL | +LL | A(foo::SecretlyEmpty), + | - not covered + = note: pattern `Foo::A(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future + = note: the matched value is of type `Foo` +help: you might want to use `let else` to handle the variant that isn't matched + | +LL | let Foo::D(_y, _z) = x else { todo!() }; + | ++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0005`. diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.rs b/tests/ui/uninhabited/uninhabited-irrefutable.rs index c1f4e5f8e27f..cbaa98960033 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.rs +++ b/tests/ui/uninhabited/uninhabited-irrefutable.rs @@ -1,6 +1,5 @@ -//@ revisions: min_exhaustive_patterns exhaustive_patterns +//@ revisions: normal exhaustive_patterns #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))] -#![cfg_attr(min_exhaustive_patterns, feature(min_exhaustive_patterns))] #![feature(never_type)] mod foo { diff --git a/tests/ui/uninhabited/uninhabited-matches-feature-gated.rs b/tests/ui/uninhabited/uninhabited-matches-feature-gated.rs index e804afcf9ed9..1b158dd48e98 100644 --- a/tests/ui/uninhabited/uninhabited-matches-feature-gated.rs +++ b/tests/ui/uninhabited/uninhabited-matches-feature-gated.rs @@ -15,10 +15,10 @@ fn main() { let _ = match x {}; //~ ERROR non-exhaustive let x: (Void,) = unsafe { zeroed() }; - let _ = match x {}; //~ ERROR non-exhaustive + let _ = match x {}; let x: [Void; 1] = unsafe { zeroed() }; - let _ = match x {}; //~ ERROR non-exhaustive + let _ = match x {}; let x: &[Void] = unsafe { zeroed() }; let _ = match x { //~ ERROR non-exhaustive @@ -29,11 +29,10 @@ fn main() { let _ = match x {}; // okay let x: Result = Ok(23); - let _ = match x { //~ ERROR non-exhaustive + let _ = match x { Ok(x) => x, }; let x: Result = Ok(23); let Ok(x) = x; - //~^ ERROR refutable } diff --git a/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr index 466d7f2eadb9..2cd3c9375d0b 100644 --- a/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -36,34 +36,6 @@ LL + _ => todo!(), LL ~ }; | -error[E0004]: non-exhaustive patterns: type `(Void,)` is non-empty - --> $DIR/uninhabited-matches-feature-gated.rs:18:19 - | -LL | let _ = match x {}; - | ^ - | - = note: the matched value is of type `(Void,)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ let _ = match x { -LL + _ => todo!(), -LL ~ }; - | - -error[E0004]: non-exhaustive patterns: type `[Void; 1]` is non-empty - --> $DIR/uninhabited-matches-feature-gated.rs:21:19 - | -LL | let _ = match x {}; - | ^ - | - = note: the matched value is of type `[Void; 1]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ let _ = match x { -LL + _ => todo!(), -LL ~ }; - | - error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered --> $DIR/uninhabited-matches-feature-gated.rs:24:19 | @@ -71,45 +43,13 @@ LL | let _ = match x { | ^ pattern `&[_, ..]` not covered | = note: the matched value is of type `&[Void]` + = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required 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 ~ &[] => (), LL ~ &[_, ..] => todo!(), | -error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/uninhabited-matches-feature-gated.rs:32:19 - | -LL | let _ = match x { - | ^ pattern `Err(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -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 ~ Ok(x) => x, -LL ~ Err(_) => todo!(), - | +error: aborting due to 3 previous errors -error[E0005]: refutable pattern in local binding - --> $DIR/uninhabited-matches-feature-gated.rs:37:9 - | -LL | let Ok(x) = x; - | ^^^^^ pattern `Err(_)` not covered - | - = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant - = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html - = note: the matched value is of type `Result` -help: you might want to use `let else` to handle the variant that isn't matched - | -LL | let Ok(x) = x else { todo!() }; - | ++++++++++++++++ - -error: aborting due to 7 previous errors - -Some errors have detailed explanations: E0004, E0005. -For more information about an error, try `rustc --explain E0004`. +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/uninhabited/uninhabited-patterns.rs b/tests/ui/uninhabited/uninhabited-patterns.rs index ae12c0fc4af6..988383e691b4 100644 --- a/tests/ui/uninhabited/uninhabited-patterns.rs +++ b/tests/ui/uninhabited/uninhabited-patterns.rs @@ -1,6 +1,5 @@ #![feature(box_patterns)] #![feature(never_type)] -#![feature(min_exhaustive_patterns)] #![deny(unreachable_patterns)] mod foo { diff --git a/tests/ui/uninhabited/uninhabited-patterns.stderr b/tests/ui/uninhabited/uninhabited-patterns.stderr index ca62386d7ef0..4e4aaa93f804 100644 --- a/tests/ui/uninhabited/uninhabited-patterns.stderr +++ b/tests/ui/uninhabited/uninhabited-patterns.stderr @@ -1,18 +1,18 @@ error: unreachable pattern - --> $DIR/uninhabited-patterns.rs:30:9 + --> $DIR/uninhabited-patterns.rs:29:9 | LL | Ok(box _) => (), | ^^^^^^^^^ | = note: this pattern matches no values because `NotSoSecretlyEmpty` is uninhabited note: the lint level is defined here - --> $DIR/uninhabited-patterns.rs:4:9 + --> $DIR/uninhabited-patterns.rs:3:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/uninhabited-patterns.rs:39:9 + --> $DIR/uninhabited-patterns.rs:38:9 | LL | Err(Ok(_y)) => (), | ^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | Err(Ok(_y)) => (), = note: this pattern matches no values because `NotSoSecretlyEmpty` is uninhabited error: unreachable pattern - --> $DIR/uninhabited-patterns.rs:42:15 + --> $DIR/uninhabited-patterns.rs:41:15 | LL | while let Some(_y) = foo() { | ^^^^^^^^