Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2354,6 +2354,93 @@ impl Expr<'_> {
}
}

/// Returns true if mir building would consider this expression a constant for the purposes of
/// lowering `[CONST; 0]` as `[]`.
pub fn is_constant_for_zero_repeat(&self) -> bool {
// NB: This needs to be kept in sync with `rustc_mir_build::builder::expr::category::Category::of`

match self.kind {
ExprKind::ConstBlock(_) | ExprKind::Lit(_) => true,
ExprKind::Cast(inner, _) | ExprKind::Type(inner, _) => {
inner.is_constant_for_zero_repeat()
}

ExprKind::Path(qpath) => {
match qpath {
QPath::Resolved(_, Path { res: Res::Def(def_kind, _), .. }) => match def_kind {
DefKind::Const | DefKind::ConstParam | DefKind::AssocConst => true,

DefKind::Mod
| DefKind::Struct
| DefKind::Union
| DefKind::Enum
| DefKind::Variant
| DefKind::Trait
| DefKind::TyAlias
| DefKind::ForeignTy
| DefKind::TraitAlias
| DefKind::AssocTy
| DefKind::TyParam
| DefKind::Fn
| DefKind::Static { .. }
| DefKind::Ctor(..)
| DefKind::AssocFn
| DefKind::Macro(_)
| DefKind::ExternCrate
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
| DefKind::OpaqueTy
| DefKind::Field
| DefKind::LifetimeParam
| DefKind::GlobalAsm
| DefKind::Impl { .. }
| DefKind::Closure
| DefKind::SyntheticCoroutineBody
| DefKind::TestBinderConstraints => false,
},

// A path that resolved to something other than a definition cannot be a const
QPath::Resolved(..) => false,

// A type relative expression can only be a const
QPath::TypeRelative(..) => true,
}
}

ExprKind::Array(_)
| ExprKind::Call(..)
| ExprKind::MethodCall(..)
| ExprKind::Use(..)
| ExprKind::Tup(_)
| ExprKind::Binary(..)
| ExprKind::Unary(..)
| ExprKind::DropTemps(_)
| ExprKind::Let(_)
| ExprKind::If(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
| ExprKind::Closure(_)
| ExprKind::Block(..)
| ExprKind::Assign(..)
| ExprKind::AssignOp(..)
| ExprKind::Field(..)
| ExprKind::Index(..)
| ExprKind::AddrOf(..)
| ExprKind::Break(..)
| ExprKind::Continue(_)
| ExprKind::Ret(_)
| ExprKind::Become(_)
| ExprKind::InlineAsm(_)
| ExprKind::OffsetOf(..)
| ExprKind::Struct(..)
| ExprKind::Repeat(..)
| ExprKind::Yield(..)
| ExprKind::UnsafeBinderCast(..)
| ExprKind::Err(_) => false,
}
}

/// If this is a desugared range expression,
/// returns the span of the range without desugaring context.
pub fn range_span(&self) -> Option<Span> {
Expand Down
24 changes: 24 additions & 0 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1739,11 +1739,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
),
);

let count_is_zero = {
if let Ok(count) = tcx.try_normalize_erasing_regions(
self.typing_env(self.param_env),
Unnormalized::new_wip(count),
) {
count.try_to_value().and_then(|c| c.try_to_target_usize(tcx)) == Some(0)
} else {
false
}
};

let uty = match expected {
ExpectHasType(uty) => uty.builtin_index(),
_ => None,
};

let old_diverges = self.diverges.get();

let (element_ty, t) = match uty {
Some(uty) => {
self.check_expr_coercible_to_type(element, uty, None);
Expand All @@ -1756,6 +1769,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};

// FIXME: remove this workaround for an unsoundness (#165323) once it's no longer needed.
//
// MIR building lowers `[CONST; 0]` to `[]`, making `CONST` not evaluated.
//
// This is a problem, because it means if `CONST` is `const { panic!() }`, the side effect
// of panic / failing compilation never happens. In such cases we need to ignore divergence
// from the `CONST`, as otherwise we can end up executing code that was assumed unreachable.
if count_is_zero && element.is_constant_for_zero_repeat() {
self.diverges.set(old_diverges);
}

if let Err(guar) = element_ty.error_reported() {
return Ty::new_error(tcx, guar);
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_mir_build/src/builder/expr/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ impl Category {
/// Determines the category for a given expression. Note that scope
/// and paren expressions have no category.
pub(crate) fn of(ek: &ExprKind<'_>) -> Option<Category> {
// NB: this needs to be kept in sync with `hir::Expr::is_constant_for_zero_repeat`

match *ek {
ExprKind::Scope { .. } => None,

Expand Down
12 changes: 12 additions & 0 deletions tests/ui/repeat-expr/zero_diverging_consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Check that repeating a diverging constant 0 times doesn't lead to unsoundness.
//
// Issue: <https://github.com/rust-lang/rust/issues/165323>

fn ex_nihilo() -> u32 {
//~^ error: mismatched types
[const { panic!() } as !; 0];
}

fn main() {
dbg!(ex_nihilo());
}
11 changes: 11 additions & 0 deletions tests/ui/repeat-expr/zero_diverging_consts.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0308]: mismatched types
--> $DIR/zero_diverging_consts.rs:5:19
|
LL | fn ex_nihilo() -> u32 {
| --------- ^^^ expected `u32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0308`.
20 changes: 20 additions & 0 deletions tests/ui/repeat-expr/zero_diverging_consts_runnable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Check that repeating a diverging constant 0 times doesn't lead to unsoundness.
//
// Issue: <https://github.com/rust-lang/rust/issues/165323>
//
//@ run-fail
//@ check-run-results
//@ exec-env:RUST_BACKTRACE=0

fn get(v: &[u32], i: usize) -> u32 {
if i >= v.len() {
let _: [!; 0] = [const { panic!() }; 0];
}
v[i]
}

fn main() {
let v = vec![1, 2, 3];
let i = std::hint::black_box(4); // 4, hidden from the optimizer
println!("v[{i}] = {}", get(&v, i));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

thread 'main' ($TID) panicked at $DIR/zero_diverging_consts_runnable.rs:13:5:
index out of bounds: the len is 3 but the index is 4
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
21 changes: 21 additions & 0 deletions tests/ui/repeat-expr/zero_diverging_type_relative_consts_consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Check that repeating a diverging constant 0 times doesn't lead to unsoundness.
//
// Issue: <https://github.com/rust-lang/rust/issues/165323>

trait T {
const V: !;
}

struct Panic;
impl T for Panic {
const V: ! = panic!();
}

fn ex_nihilo() -> u32 {
//~^ error: mismatched types
[Panic::V; 0];
}

fn main() {
dbg!(ex_nihilo());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0308]: mismatched types
--> $DIR/zero_diverging_type_relative_consts_consts.rs:14:19
|
LL | fn ex_nihilo() -> u32 {
| --------- ^^^ expected `u32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0308`.
Loading