diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 993f04fe1c161..f1a22fd6af982 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -836,7 +836,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && !visitor .errors .iter() - .map(|(sp, _)| *sp) + .map(|error| error.span) .any(|sp| span < sp && !sp.contains(span)) }) { show_assign_sugg = true; @@ -865,8 +865,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { err.span_label(span, format!("{path} {used} here but it {isnt_initialized}")); let mut shown = false; - for (sp, label) in visitor.errors { - if sp < span && !sp.overlaps(span) { + let mut shown_condition_value = false; + for error in visitor.errors { + if error.span < span && !error.span.overlaps(span) { // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention // match arms coming after the primary span because they aren't relevant: // ``` @@ -880,7 +881,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // _ => {} // We don't want to point to this. // }; // ``` - err.span_label(sp, label); + shown_condition_value |= error.kind.describes_condition_value(); + err.span_label(error.span, error.label); shown = true; } } @@ -893,6 +895,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } err.span_label(decl_span, "binding declared here but left uninitialized"); + if shown_condition_value { + err.note( + "when checking initialization, the compiler describes possible control-flow paths \ + without evaluating whether branch conditions can actually have the values shown", + ); + } if show_assign_sugg { struct LetVisitor { decl_span: Span, @@ -4680,7 +4688,31 @@ struct ConditionVisitor<'tcx> { tcx: TyCtxt<'tcx>, spans: Vec, name: String, - errors: Vec<(Span, String)>, + errors: Vec, +} + +struct ConditionError { + span: Span, + label: String, + kind: ConditionErrorKind, +} + +impl ConditionError { + fn new(span: Span, kind: ConditionErrorKind, label: String) -> Self { + Self { span, label, kind } + } +} + +#[derive(Clone, Copy)] +enum ConditionErrorKind { + ConditionValue, + Other, +} + +impl ConditionErrorKind { + fn describes_condition_value(self) -> bool { + matches!(self, Self::ConditionValue) + } } impl<'v, 'tcx> Visitor<'v> for ConditionVisitor<'tcx> { @@ -4690,15 +4722,17 @@ impl<'v, 'tcx> Visitor<'v> for ConditionVisitor<'tcx> { // `if` expressions with no `else` that initialize the binding might be missing an // `else` arm. if ReferencedStatementsVisitor(&self.spans).visit_expr(body).is_break() { - self.errors.push(( + self.errors.push(ConditionError::new( cond.span, + ConditionErrorKind::ConditionValue, format!( "if this `if` condition is `false`, {} is not initialized", self.name, ), )); - self.errors.push(( + self.errors.push(ConditionError::new( ex.span.shrink_to_hi(), + ConditionErrorKind::Other, format!("an `else` arm might be missing here, initializing {}", self.name), )); } @@ -4712,8 +4746,9 @@ impl<'v, 'tcx> Visitor<'v> for ConditionVisitor<'tcx> { (true, true) | (false, false) => {} (true, false) => { if other.span.is_desugaring(DesugaringKind::WhileLoop) { - self.errors.push(( + self.errors.push(ConditionError::new( cond.span, + ConditionErrorKind::ConditionValue, format!( "if this condition isn't met and the `while` loop runs 0 \ times, {} is not initialized", @@ -4721,8 +4756,9 @@ impl<'v, 'tcx> Visitor<'v> for ConditionVisitor<'tcx> { ), )); } else { - self.errors.push(( + self.errors.push(ConditionError::new( body.span.shrink_to_hi().until(other.span), + ConditionErrorKind::ConditionValue, format!( "if the `if` condition is `false` and this `else` arm is \ executed, {} is not initialized", @@ -4732,8 +4768,9 @@ impl<'v, 'tcx> Visitor<'v> for ConditionVisitor<'tcx> { } } (false, true) => { - self.errors.push(( + self.errors.push(ConditionError::new( cond.span, + ConditionErrorKind::ConditionValue, format!( "if this condition is `true`, {} is not initialized", self.name @@ -4753,8 +4790,9 @@ impl<'v, 'tcx> Visitor<'v> for ConditionVisitor<'tcx> { for (arm, seen) in arms.iter().zip(results) { if !seen { if loop_desugar == hir::MatchSource::ForLoopDesugar { - self.errors.push(( + self.errors.push(ConditionError::new( e.span, + ConditionErrorKind::Other, format!( "if the `for` loop runs 0 times, {} is not initialized", self.name @@ -4767,8 +4805,9 @@ impl<'v, 'tcx> Visitor<'v> for ConditionVisitor<'tcx> { ) { continue; } - self.errors.push(( + self.errors.push(ConditionError::new( arm.pat.span.to(guard.span), + ConditionErrorKind::ConditionValue, format!( "if this pattern and condition are matched, {} is not \ initialized", @@ -4782,8 +4821,9 @@ impl<'v, 'tcx> Visitor<'v> for ConditionVisitor<'tcx> { ) { continue; } - self.errors.push(( + self.errors.push(ConditionError::new( arm.pat.span, + ConditionErrorKind::Other, format!( "if this pattern is matched, {} is not initialized", self.name diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index fd0ef8500c6c3..d8d749cd35c4b 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -10,10 +10,10 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::errors::{ - EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, - EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition, - EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, - EiiStaticMultipleImplementations, EiiStaticMutable, + EiiAttributeNotSupported, EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, + EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, + EiiSharedMacroInStatementPosition, EiiSharedMacroTarget, EiiStaticArgumentRequired, + EiiStaticDefault, EiiStaticMultipleImplementations, EiiStaticMutable, }; /// ```rust @@ -128,6 +128,8 @@ fn eii_( let attrs_from_decl = filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path); + let (macro_attrs, foreign_item_attrs, default_func_attrs) = + split_attrs(ecx, item_span, attrs_from_decl); let Ok(macro_name) = name_for_impl_macro(ecx, foreign_item_name, &meta_item) else { // we don't need to wrap in Annotatable::Stmt conditionally since @@ -148,6 +150,7 @@ fn eii_( eii_attr_span, item_span, foreign_item_name, + default_func_attrs, )) } @@ -157,7 +160,7 @@ fn eii_( item_span, kind, vis, - &attrs_from_decl, + foreign_item_attrs, )); module_items.push(generate_attribute_macro_to_implement( ecx, @@ -165,7 +168,7 @@ fn eii_( macro_name, foreign_item_name, impl_unsafe, - &attrs_from_decl, + macro_attrs, )); // we don't need to wrap in Annotatable::Stmt conditionally since @@ -173,6 +176,49 @@ fn eii_( module_items.into_iter().map(Annotatable::Item).collect() } +fn split_attrs( + ecx: &mut ExtCtxt<'_>, + span: Span, + attrs: ThinVec, +) -> (ThinVec, ThinVec, ThinVec) { + let mut macro_attributes = ThinVec::new(); + let mut foreign_item_attributes = ThinVec::new(); + let mut default_attributes = ThinVec::new(); + + for attr in attrs { + match attr.name() { + // Inline only matters for the default function being inlined into callsites + Some(sym::inline) => default_attributes.push(attr), + // If an eii is marked a lang item, that's because we want to call its declaration, so + // mark the foreign item as the lang item + Some(sym::lang) => foreign_item_attributes.push(attr), + // Deprecating an eii means deprecating the macro and the foreign item + Some(sym::deprecated) => { + foreign_item_attributes.push(attr.clone()); + macro_attributes.push(attr); + } + // The stability of an EII affects the usage of the macro and calling the foreign item + Some(sym::stable) | Some(sym::unstable) => { + foreign_item_attributes.push(attr.clone()); + macro_attributes.push(attr); + } + // Doc attributes should be forwarded to the macro and the foreign item, since those are + // the two items you interact with as a user. + // FIXME: idk yet how EIIs show up in docs, might want to customize + _ if attr.is_doc_comment() => { + foreign_item_attributes.push(attr.clone()); + macro_attributes.push(attr); + } + Some(sym::eii) => unreachable!("should already be filtered out"), + _ => { + ecx.dcx().emit_err(EiiAttributeNotSupported { span, attr_span: attr.span() }); + } + } + } + + (macro_attributes, foreign_item_attributes, default_attributes) +} + /// Decide on the name of the macro that can be used to implement the EII. /// This is either an explicitly given name, or the name of the item in the /// declaration of the EII. @@ -228,10 +274,8 @@ fn generate_default_func_impl( eii_attr_span: Span, item_span: Span, foreign_item_name: Ident, + attrs: ThinVec, ) -> Box { - // FIXME: re-add some original attrs - let attrs = ThinVec::new(); - let mut default_func = func.clone(); default_func.eii_impls.push(EiiImpl { node_id: DUMMY_NODE_ID, @@ -289,10 +333,9 @@ fn generate_foreign_item( item_span: Span, item_kind: &ItemKind, vis: Visibility, - attrs_from_decl: &[Attribute], + attrs_from_decl: ThinVec, ) -> Box { - let mut foreign_item_attrs = ThinVec::new(); - foreign_item_attrs.extend_from_slice(attrs_from_decl); + let mut foreign_item_attrs = attrs_from_decl; // Add the rustc_eii_foreign_item on the foreign item. Usually, foreign items are mangled. // This attribute makes sure that we later know that this foreign item's symbol should not be. @@ -381,13 +424,9 @@ fn generate_attribute_macro_to_implement( macro_name: Ident, foreign_item_name: Ident, impl_unsafe: bool, - attrs_from_decl: &[Attribute], + attrs_from_decl: ThinVec, ) -> Box { - let mut macro_attrs = ThinVec::new(); - - // To avoid e.g. `error: attribute macro has missing stability attribute` - // errors for eii's in std. - macro_attrs.extend_from_slice(attrs_from_decl); + let mut macro_attrs = attrs_from_decl; // Avoid "missing stability attribute" errors for eiis in std. See #146993. macro_attrs.push(ecx.attr_name_value_str(sym::rustc_macro_transparency, sym::semiopaque, span)); diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index c64d6871269a6..929c88402b1aa 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1186,6 +1186,15 @@ pub(crate) struct EiiMacroExpectedMaxOneArgument { pub name: String, } +#[derive(Diagnostic)] +#[diag("only a small subset of attributes are supported on externally implementable items")] +pub(crate) struct EiiAttributeNotSupported { + #[primary_span] + pub span: Span, + #[note("this attribute is not supported")] + pub attr_span: Span, +} + #[derive(Diagnostic)] #[diag("named argument `{$named_arg_name}` is not used by name")] pub(crate) struct NamedArgumentUsedPositionally { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 98f2a6a2603e7..800683f43fe8c 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -454,13 +454,42 @@ impl<'tcx> SizeSkeleton<'tcx> { } ty::Adt(def, args) => { - // Only newtypes and enums w/ nullable pointer optimization. + // Only newtypes and enums w/ nullable pointer optimization (NPO). if def.is_union() || def.variants().is_empty() || def.variants().len() > 2 { return Err(err); } + // Only default repr types. + { + // We can ignore the seed and some particular flags that can never affect the + // layout of newtypes / NPO types, but we have to check everything else. + // If you are adding a new field to `ReprOptions`, make sure to extend the check + // below so that we bail out if it is not at its default value! + let ReprOptions { int, align, pack, flags, scalable, field_shuffle_seed: _ } = + def.repr(); + let mut ignored_flags = ReprFlags::IS_TRANSPARENT + | ReprFlags::IS_LINEAR + | ReprFlags::RANDOMIZE_LAYOUT; + if def.is_struct() { + // `repr(C)` is only okay for structs, not for enums. + // Below, the *only* thing we do for structs is propagating + // `SizeSkeleton::Pointer`. We do *not* assume that `repr(C)` preserved + // ZST-ness (which might stop being true eventually). + ignored_flags |= ReprFlags::IS_C; + } + if int.is_some() + || align.is_some() + || pack.is_some() + || flags.difference(ignored_flags) != ReprFlags::default() + || scalable.is_some() + { + return Err(err); + } + } // Get a zero-sized variant or a pointer newtype. - let zero_or_ptr_variant = |i| { + // Returns `Ok(None)` for 1-ZST types, `Ok(Some)` if (ignoring all 1-ZST fields) + // there's just a single pointer, and `Err` otherwise. + let zero_or_ptr_variant = |i| -> Result>, _> { let i = VariantIdx::from_usize(i); let fields = def.variant(i).fields.iter().map(|field| { SizeSkeleton::compute_inner( @@ -494,7 +523,8 @@ impl<'tcx> SizeSkeleton<'tcx> { }; let v0 = zero_or_ptr_variant(0)?; - // Newtype. + // Single-variant case: Check if this is a newtype around a pointer. + // Such types are themselves pointer-sized. if def.variants().len() == 1 { if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 { return Ok(SizeSkeleton::Pointer { non_zero, tail }); @@ -504,7 +534,9 @@ impl<'tcx> SizeSkeleton<'tcx> { } let v1 = zero_or_ptr_variant(1)?; - // Nullable pointer enum optimization. + // 2-variant case: Check if one variant is a *non-zero* pointer and the other a + // 1-ZST. Such types are eligible to for the nullable pointer enum optimization, so + // they are themselves pointer-sized. match (v0, v1) { (Some(SizeSkeleton::Pointer { non_zero: true, tail }), None) | (None, Some(SizeSkeleton::Pointer { non_zero: true, tail })) => { diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index e9f9da9c8cd91..d158f55a3111c 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -890,6 +890,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { { continue; } + // Ignore return value plumbing. After a call returning a non-`!` + // uninhabited type, a tail expression can be unreachable while + // still being needed to satisfy the surrounding return type. + StatementKind::Assign((place, _)) if place.as_local() == Some(RETURN_PLACE) => { + continue; + } StatementKind::StorageLive(_) | StatementKind::StorageDead(_) => { continue; } diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 7957bacda5677..7d8c6bdca8800 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -63,6 +63,7 @@ impl TypingModeErasedStatus for MayBeErased {} feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] +#[cfg_attr(feature = "nightly", rustc_must_match_exhaustively)] pub enum TypingMode { /// When checking whether impls overlap, we check whether any obligations /// are guaranteed to never hold when unifying the impls. This requires us diff --git a/tests/run-make/README.md b/tests/run-make/README.md index 5cdd2fdc523cf..c200c7e466aec 100644 --- a/tests/run-make/README.md +++ b/tests/run-make/README.md @@ -22,5 +22,39 @@ The setup for the `rmake.rs` can be summarized as a 3-stage process: and copy non-`rmake.rs` input support files over to `rmake_out/`. The support library is made available as an [*extern prelude*][extern_prelude]. 3. Finally, we run the recipe binary and set `rmake_out/` as the working directory. +## External dependencies + +`compiletest` passes tool paths and target-specific flags to `rmake.rs` through +environment variables. Prefer using the helpers in [`run_make_support`] over +reading these variables directly. The helpers keep command construction +consistent across hosts and targets, and avoid relying on whichever tools happen +to be first in `PATH`. + +Commonly used helpers include: + +- `rustc()` and `rustdoc()` for the compiler and rustdoc under test, from + `RUSTC` and `RUSTDOC`. +- `cc()` and `cxx()` for the target C and C++ compilers, from `CC`/`CXX` plus + `CC_DEFAULT_FLAGS`/`CXX_DEFAULT_FLAGS`. +- `gcc()` for tests that specifically need `gcc`; unlike `cc()`, this assumes a + suitable `gcc` is available in `PATH` and does not add `CC_DEFAULT_FLAGS`. +- `llvm_ar()`, `llvm_nm()`, `llvm_objdump()`, `llvm_readobj()`, and similar + LLVM tools from `LLVM_BIN_DIR`; `llvm_filecheck()` uses `LLVM_FILECHECK`. +- `python_command()` for the Python interpreter selected by bootstrap, from + `PYTHON`. +- `clang()` for tests that explicitly require clang-based testing, from `CLANG`. +- `cargo()` for in-tree cargo, from `CARGO`; this is only available in the + `run-make-cargo` and `build-std` suites, not in plain `run-make`. +- `htmldocck()` for the in-tree `src/etc/htmldocck.py` script, invoked through + `python_command()`. + +Some of these variables are only set when the configured builder can provide the +corresponding tool. Tests that require optional tools or LLVM target components +should declare the matching compiletest directive, such as +`//@ needs-force-clang-based-tests` or `//@ needs-llvm-components: x86`, instead +of failing later in the recipe. If a test needs a tool that is not represented by +`run_make_support::external_deps`, add a small helper there rather than open +coding the lookup in individual tests. + [`run_make_support`]: ../../src/tools/run-make-support [extern_prelude]: https://doc.rust-lang.org/reference/names/preludes.html#extern-prelude diff --git a/tests/ui/async-await/no-non-guaranteed-initialization.stderr b/tests/ui/async-await/no-non-guaranteed-initialization.stderr index 7adccdba2f87c..4e62a307e4b0b 100644 --- a/tests/ui/async-await/no-non-guaranteed-initialization.stderr +++ b/tests/ui/async-await/no-non-guaranteed-initialization.stderr @@ -10,6 +10,8 @@ LL | } | - an `else` arm might be missing here, initializing `y` LL | y | ^ `y` used here but it is possibly-uninitialized + | + = note: when checking initialization, the compiler describes possible control-flow paths without evaluating whether branch conditions can actually have the values shown error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-if-no-else.stderr b/tests/ui/borrowck/borrowck-if-no-else.stderr index f1fad2d36dc5e..48a526b744b03 100644 --- a/tests/ui/borrowck/borrowck-if-no-else.stderr +++ b/tests/ui/borrowck/borrowck-if-no-else.stderr @@ -8,6 +8,8 @@ LL | let x: isize; if 1 > 2 { x = 10; } | binding declared here but left uninitialized LL | foo(x); | ^ `x` used here but it is possibly-uninitialized + | + = note: when checking initialization, the compiler describes possible control-flow paths without evaluating whether branch conditions can actually have the values shown error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-if-with-else.stderr b/tests/ui/borrowck/borrowck-if-with-else.stderr index c246e41487fc3..be3b33a09751c 100644 --- a/tests/ui/borrowck/borrowck-if-with-else.stderr +++ b/tests/ui/borrowck/borrowck-if-with-else.stderr @@ -8,6 +8,8 @@ LL | if 1 > 2 { ... LL | foo(x); | ^ `x` used here but it is possibly-uninitialized + | + = note: when checking initialization, the compiler describes possible control-flow paths without evaluating whether branch conditions can actually have the values shown error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-while-break.stderr b/tests/ui/borrowck/borrowck-while-break.stderr index 68333ce0a75d3..c4af9276b6009 100644 --- a/tests/ui/borrowck/borrowck-while-break.stderr +++ b/tests/ui/borrowck/borrowck-while-break.stderr @@ -8,6 +8,8 @@ LL | while cond { ... LL | println!("{}", v); | ^ `v` used here but it is possibly-uninitialized + | + = note: when checking initialization, the compiler describes possible control-flow paths without evaluating whether branch conditions can actually have the values shown error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/borrowck-while.stderr b/tests/ui/borrowck/borrowck-while.stderr index d560b9c02fb68..ba703dd62c103 100644 --- a/tests/ui/borrowck/borrowck-while.stderr +++ b/tests/ui/borrowck/borrowck-while.stderr @@ -7,6 +7,8 @@ LL | while 1 == 1 { x = 10; } | ------ if this condition isn't met and the `while` loop runs 0 times, `x` is not initialized LL | return x; | ^ `x` used here but it is possibly-uninitialized + | + = note: when checking initialization, the compiler describes possible control-flow paths without evaluating whether branch conditions can actually have the values shown error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/branch-condition-flow-issue-156494.rs b/tests/ui/borrowck/branch-condition-flow-issue-156494.rs new file mode 100644 index 0000000000000..d2f0404ab9ce6 --- /dev/null +++ b/tests/ui/borrowck/branch-condition-flow-issue-156494.rs @@ -0,0 +1,55 @@ +//@ edition: 2024 + +fn main() { + let v1: Vec = vec![1, 2, 3]; + let mut iter = v1.iter(); + + let mut first_encounter = true; + let mut token: i32; + let mut following_token: i32; + loop { + if first_encounter { + if let Some(token) = iter.next() { + match iter.next() { + Some(temp) => { + following_token = *temp; + first_encounter = false; + println!("{} followed by {}", token, following_token); + } + _ => { + println!("{} is last", token); + } + } + } else { + break; + } + } else { + token = following_token; //~ ERROR used binding `following_token` isn't initialized + first_encounter = true; + match iter.next() { + Some(temp) => { + following_token = *temp; + first_encounter = false; + println!("{} followed by {}", token, following_token); + } + _ => { + println!("{} is last", token); + } + } + } + } +} + +fn guarded_match(value: i32, condition: bool) { + let y; + match value { + 0 => { + y = 1; + } + _ if condition => {} + _ => { + y = 2; + } + } + let _z = y; //~ ERROR used binding `y` is possibly-uninitialized +} diff --git a/tests/ui/borrowck/branch-condition-flow-issue-156494.stderr b/tests/ui/borrowck/branch-condition-flow-issue-156494.stderr new file mode 100644 index 0000000000000..04fb77a72a6f7 --- /dev/null +++ b/tests/ui/borrowck/branch-condition-flow-issue-156494.stderr @@ -0,0 +1,38 @@ +error[E0381]: used binding `following_token` isn't initialized + --> $DIR/branch-condition-flow-issue-156494.rs:27:21 + | +LL | let mut following_token: i32; + | ------------------- binding declared here but left uninitialized +... +LL | _ => { + | - if this pattern is matched, `following_token` is not initialized +... +LL | } else { + | ------ if the `if` condition is `false` and this `else` arm is executed, `following_token` is not initialized +... +LL | token = following_token; + | ^^^^^^^^^^^^^^^ `following_token` used here but it isn't initialized + | + = note: when checking initialization, the compiler describes possible control-flow paths without evaluating whether branch conditions can actually have the values shown +help: consider assigning a value + | +LL | let mut following_token: i32 = 42; + | ++++ + +error[E0381]: used binding `y` is possibly-uninitialized + --> $DIR/branch-condition-flow-issue-156494.rs:54:14 + | +LL | let y; + | - binding declared here but left uninitialized +... +LL | _ if condition => {} + | -------------- if this pattern and condition are matched, `y` is not initialized +... +LL | let _z = y; + | ^ `y` used here but it is possibly-uninitialized + | + = note: when checking initialization, the compiler describes possible control-flow paths without evaluating whether branch conditions can actually have the values shown + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0381`. diff --git a/tests/ui/eii/attrs.rs b/tests/ui/eii/attrs.rs new file mode 100644 index 0000000000000..ec6259863e9b0 --- /dev/null +++ b/tests/ui/eii/attrs.rs @@ -0,0 +1,22 @@ +#![feature(extern_item_impls)] +#![deny(deprecated)] //~ NOTE: + +// makes no sense on functions, nor on the macro generated (it's a macrov2). +#[macro_export] //~ NOTE: this attribute is not supported +// makes sense, as long as we only forward it onto the function, +// so we allow and this shouln't cause errors for being on a "wrong target". +#[inline] +// makes sense, should be allowed, and forwarded on both the function and the macro +#[deprecated = "foo"] +#[eii] +fn example() {} +//~^ ERROR only a small subset of attributes are supported on externally implementable items + +// check that both are deprecated vvvv +#[example] +//~^ ERROR use of deprecated macro +fn explicit_impl() {} +fn main() { + example() + //~^ ERROR use of deprecated function +} diff --git a/tests/ui/eii/attrs.stderr b/tests/ui/eii/attrs.stderr new file mode 100644 index 0000000000000..a8d7cbbee1828 --- /dev/null +++ b/tests/ui/eii/attrs.stderr @@ -0,0 +1,32 @@ +error: only a small subset of attributes are supported on externally implementable items + --> $DIR/attrs.rs:12:1 + | +LL | fn example() {} + | ^^^^^^^^^^^^ + | +note: this attribute is not supported + --> $DIR/attrs.rs:5:1 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ + +error: use of deprecated macro `example`: foo + --> $DIR/attrs.rs:16:3 + | +LL | #[example] + | ^^^^^^^ + | +note: the lint level is defined here + --> $DIR/attrs.rs:2:9 + | +LL | #![deny(deprecated)] + | ^^^^^^^^^^ + +error: use of deprecated function `example`: foo + --> $DIR/attrs.rs:20:5 + | +LL | example() + | ^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/eii/ice_contract_attr_on_eii_generated_item.rs b/tests/ui/eii/ice_contract_attr_on_eii_generated_item.rs deleted file mode 100644 index fce142f6dc08b..0000000000000 --- a/tests/ui/eii/ice_contract_attr_on_eii_generated_item.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ compile-flags: --crate-type rlib - -#![feature(extern_item_impls)] -#![feature(contracts)] -#![allow(incomplete_features)] - -#[eii] -#[core::contracts::ensures] -//~^ ERROR contract annotations is only supported in functions with bodies -//~| ERROR contract annotations can only be used on functions -fn implementation(); diff --git a/tests/ui/eii/ice_contract_attr_on_eii_generated_item.stderr b/tests/ui/eii/ice_contract_attr_on_eii_generated_item.stderr deleted file mode 100644 index 3335346e55eec..0000000000000 --- a/tests/ui/eii/ice_contract_attr_on_eii_generated_item.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: contract annotations is only supported in functions with bodies - --> $DIR/ice_contract_attr_on_eii_generated_item.rs:8:1 - | -LL | #[core::contracts::ensures] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: contract annotations can only be used on functions - --> $DIR/ice_contract_attr_on_eii_generated_item.rs:8:1 - | -LL | #[core::contracts::ensures] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/transmute/raw-ptr-non-null.rs b/tests/ui/transmute/raw-ptr-non-null.rs deleted file mode 100644 index af518095b3fbd..0000000000000 --- a/tests/ui/transmute/raw-ptr-non-null.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! After the use of pattern types inside `NonNull`, -//! transmuting between a niche optimized enum wrapping a -//! generic `NonNull` and raw pointers stopped working. -//@ check-pass - -use std::ptr::NonNull; -pub const fn is_null<'a, T: ?Sized>(ptr: *const T) -> bool { - unsafe { matches!(core::mem::transmute::<*const T, Option>>(ptr), None) } -} - -fn main() {} diff --git a/tests/ui/transmute/transmute-different-sizes.rs b/tests/ui/transmute/transmute-different-sizes.rs index 40197a6c53f00..4f1f758677145 100644 --- a/tests/ui/transmute/transmute-different-sizes.rs +++ b/tests/ui/transmute/transmute-different-sizes.rs @@ -48,4 +48,36 @@ pub unsafe fn shouldnt_work2(from: *mut T) -> PtrAndEmptyArray { //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types } +#[repr(align(16))] +struct OverAlignWrap(T); + +fn shouldnt_work3(x: OverAlignWrap<*const T>) -> *const T { + unsafe { transmute(x) } + //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types +} + +// With `repr(C)`, this type has a disriminant, so it does not have the same size as `T`. +#[repr(C)] +enum NotANewtype { + SingleVariant(T), +} + +fn shouldnt_work4(x: &T) -> NotANewtype<&T> { + unsafe { transmute(x) } + //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types +} + +// With `repr(C)`, this type has a disriminant; NPO does not kick in. +// Therefore this is always bigger than `T`. +#[repr(C)] +enum NotNullPointerOptimized { + Some(T), + None +} + +fn shouldnt_work5(x: &T) -> NotNullPointerOptimized<&T> { + unsafe { transmute(x) } + //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types +} + fn main() {} diff --git a/tests/ui/transmute/transmute-different-sizes.stderr b/tests/ui/transmute/transmute-different-sizes.stderr index ea3a017c16e6f..fe611f7ba18d1 100644 --- a/tests/ui/transmute/transmute-different-sizes.stderr +++ b/tests/ui/transmute/transmute-different-sizes.stderr @@ -43,6 +43,33 @@ LL | std::mem::transmute(from) = note: source type: `*mut T` (pointer to `T`) = note: target type: `PtrAndEmptyArray` (size can vary because of ::Metadata) -error: aborting due to 5 previous errors +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:55:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `OverAlignWrap<*const T>` (size can vary because of ::Metadata) + = note: target type: `*const T` (pointer to `T`) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:66:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `NotANewtype<&T>` (size can vary because of ::Metadata) + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/transmute-different-sizes.rs:79:14 + | +LL | unsafe { transmute(x) } + | ^^^^^^^^^ + | + = note: source type: `&T` (pointer to `T`) + = note: target type: `NotNullPointerOptimized<&T>` (size can vary because of ::Metadata) + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/transmute/transmute-fat-pointers.rs b/tests/ui/transmute/transmute-fat-pointers.rs index f095b80dc2da8..c03d0d34feb1b 100644 --- a/tests/ui/transmute/transmute-fat-pointers.rs +++ b/tests/ui/transmute/transmute-fat-pointers.rs @@ -5,6 +5,7 @@ #![allow(dead_code)] use std::mem::transmute; +use std::ptr::NonNull; fn a(x: &[T]) -> &U { unsafe { transmute(x) } //~ ERROR cannot transmute between types of different sizes @@ -15,11 +16,11 @@ fn b(x: &T) -> &U { } fn c(x: &T) -> &U { - unsafe { transmute(x) } + unsafe { transmute(x) } // Ok! } fn d(x: &[T]) -> &[U] { - unsafe { transmute(x) } + unsafe { transmute(x) } // Ok! } fn e(x: &T) -> &U { @@ -30,4 +31,36 @@ fn f(x: &T) -> &U { unsafe { transmute(x) } //~ ERROR cannot transmute between types of different sizes } +// We can transmute even between pointers of unknown size as long as the metadata of +// input and output type are the same. This even accounts for null pointer optimizations. +fn g1(x: &T) -> Option<&T> { + unsafe { transmute(x) } // Ok! +} + +fn g2(x: Option>) -> NonNull { + unsafe { transmute(x) } // Ok! +} + +fn g3(x: *const T) -> Option> { + unsafe { transmute(x) } // Ok! +} + +// Make sure we can see through all the layers of `Box`. +fn h(x: Box) -> &'static T { + unsafe { transmute(x) } // Ok! +} + +// Make sure we can see through newtype wrappers. +struct Wrapper1(T); + +#[repr(C)] +struct Wrapper2(T); + +fn i1(x: &T) -> Wrapper1<&T> { + unsafe { transmute(x) } // Ok! +} +fn i2(x: &T) -> Wrapper2<&T> { + unsafe { transmute(x) } // Ok! +} + fn main() { } diff --git a/tests/ui/transmute/transmute-fat-pointers.stderr b/tests/ui/transmute/transmute-fat-pointers.stderr index e8335fcbed9d0..56a4f7f33d152 100644 --- a/tests/ui/transmute/transmute-fat-pointers.stderr +++ b/tests/ui/transmute/transmute-fat-pointers.stderr @@ -1,5 +1,5 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:10:14 + --> $DIR/transmute-fat-pointers.rs:11:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe { transmute(x) } = note: target type: `&U` (pointer to `U`) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:14:14 + --> $DIR/transmute-fat-pointers.rs:15:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | unsafe { transmute(x) } = note: target type: `&U` (pointer to `U`) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:26:14 + --> $DIR/transmute-fat-pointers.rs:27:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ @@ -26,7 +26,7 @@ LL | unsafe { transmute(x) } = note: target type: `&U` (N bits) error[E0512]: cannot transmute between types of different sizes, or dependently-sized types - --> $DIR/transmute-fat-pointers.rs:30:14 + --> $DIR/transmute-fat-pointers.rs:31:14 | LL | unsafe { transmute(x) } | ^^^^^^^^^ diff --git a/tests/ui/uninhabited/unreachable.rs b/tests/ui/uninhabited/unreachable.rs index e4a5ba406a72e..10e77d27370ef 100644 --- a/tests/ui/uninhabited/unreachable.rs +++ b/tests/ui/uninhabited/unreachable.rs @@ -4,6 +4,8 @@ //@ check-pass #![deny(unreachable_code)] +use std::process::ExitCode; + enum Never {} fn make_never() -> Never { @@ -28,6 +30,14 @@ fn branchy() { } } +// Regression test for https://github.com/rust-lang/rust/issues/152559. +// The final expression is unreachable at runtime, but it cannot be removed +// because it supplies the function's required return type. +fn required_return_value() -> ExitCode { + make_never(); + ExitCode::FAILURE +} + fn main() { func(); block();