diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 53bc9eb7e466c..54cd8cc3efe75 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -12,7 +12,7 @@ use std::borrow::Cow; use std::hash::{Hash, Hasher}; use std::sync::Arc; -use rustc_errors::{Applicability, Diag, EmissionGuarantee}; +use rustc_errors::{Applicability, Diag, EmissionGuarantee, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::def_id::DefId; @@ -996,4 +996,7 @@ pub enum CodegenObligationError { /// but was included during typeck due to the trivial_bounds feature. Unimplemented, FulfillmentError, + /// The selected impl has unconstrained generic parameters. This will emit an error + /// during impl WF checking. + UnconstrainedParam(ErrorGuaranteed), } diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index e5276e6d51585..cc329ca33288c 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -80,16 +80,14 @@ pub(crate) fn codegen_select_candidate<'tcx>( // but never resolved, causing the return value of a query to contain inference // vars. We do not have a concept for this and will in fact ICE in stable hashing // of the return value. So bail out instead. - match impl_source { - ImplSource::UserDefined(impl_) => { - tcx.dcx().span_delayed_bug( - tcx.def_span(impl_.impl_def_id), - "this impl has unconstrained generic parameters", - ); - } + let guar = match impl_source { + ImplSource::UserDefined(impl_) => tcx.dcx().span_delayed_bug( + tcx.def_span(impl_.impl_def_id), + "this impl has unconstrained generic parameters", + ), _ => unreachable!(), - } - return Err(CodegenObligationError::FulfillmentError); + }; + return Err(CodegenObligationError::UnconstrainedParam(guar)); } Ok(&*tcx.arena.alloc(impl_source)) diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 8c6e3c86bf5c8..d059d6dcd139c 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -112,6 +112,7 @@ fn resolve_associated_item<'tcx>( | CodegenObligationError::Unimplemented | CodegenObligationError::FulfillmentError, ) => return Ok(None), + Err(CodegenObligationError::UnconstrainedParam(guar)) => return Err(guar), }; // Now that we know which impl is being used, we can dispatch to diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index b6a14d147cad4..c8b3c8a796f3a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -147,12 +147,25 @@ fn extract_const_value<'tcx>( ) -> Result, &'tcx LayoutError<'tcx>> { match ct.kind() { ty::ConstKind::Value(cv) => Ok(cv), - ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) => { + ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) => { if !ct.has_param() { bug!("failed to normalize const, but it is not generic: {ct:?}"); } Err(error(cx, LayoutError::TooGeneric(ty))) } + ty::ConstKind::Unevaluated(_) => { + let err = if ct.has_param() { + LayoutError::TooGeneric(ty) + } else { + // This case is reachable with unsatisfiable predicates and GCE (which will + // cause anon consts to inherit the unsatisfiable predicates). For example + // if we have an unsatisfiable `u8: Trait` bound, then it's not a compile + // error to mention `[u8; ::CONST]`, but we can't compute its + // layout. + LayoutError::Unknown(ty) + }; + Err(error(cx, err)) + } ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) diff --git a/tests/ui/layout/gce-rigid-const-in-array-len.rs b/tests/ui/layout/gce-rigid-const-in-array-len.rs new file mode 100644 index 0000000000000..8e57907a2c5ec --- /dev/null +++ b/tests/ui/layout/gce-rigid-const-in-array-len.rs @@ -0,0 +1,27 @@ +//! With `feature(generic_const_exprs)`, anon consts (e.g. length in array types) will +//! inherit their parent's predicates. When combined with `feature(trivial_bounds)`, it +//! is possible to have an unevaluated constant that is rigid, but not generic. +//! +//! This is what happens below: `u8: A` does not hold in the global environment, but +//! with trivial bounds + GCE it it possible that `::B` can appear in an array +//! length without causing a compile error. This constant is *rigid* (i.e. it cannot be +//! normalized further), but it is *not generic* (i.e. it does not depend on any generic +//! parameters). +//! +//! This test ensures that we do not ICE in layout computation when encountering such a +//! constant. + +#![feature(rustc_attrs)] +#![feature(generic_const_exprs)] //~ WARNING: the feature `generic_const_exprs` is incomplete +#![feature(trivial_bounds)] + +#![crate_type = "lib"] + +trait A { + const B: usize; +} + +#[rustc_layout(debug)] +struct S([u8; ::B]) //~ ERROR: the type `[u8; ::B]` has an unknown layout +where + u8: A; diff --git a/tests/ui/layout/gce-rigid-const-in-array-len.stderr b/tests/ui/layout/gce-rigid-const-in-array-len.stderr new file mode 100644 index 0000000000000..6149debdfe88d --- /dev/null +++ b/tests/ui/layout/gce-rigid-const-in-array-len.stderr @@ -0,0 +1,17 @@ +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/gce-rigid-const-in-array-len.rs:15:12 + | +LL | #![feature(generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #76560 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: the type `[u8; ::B]` has an unknown layout + --> $DIR/gce-rigid-const-in-array-len.rs:25:1 + | +LL | struct S([u8; ::B]) + | ^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/layout/unconstrained-param-ice-137308.rs b/tests/ui/layout/unconstrained-param-ice-137308.rs new file mode 100644 index 0000000000000..c9b1e0a4b9ec3 --- /dev/null +++ b/tests/ui/layout/unconstrained-param-ice-137308.rs @@ -0,0 +1,18 @@ +//! Regression test for . +//! +//! This used to ICE in layout computation, because `::B` fails to normalize +//! due to the unconstrained param on the impl. + +#![feature(rustc_attrs)] +#![crate_type = "lib"] + +trait A { + const B: usize; +} + +impl A for u8 { //~ ERROR: the type parameter `C` is not constrained + const B: usize = 42; +} + +#[rustc_layout(debug)] +struct S([u8; ::B]); //~ ERROR: the type has an unknown layout diff --git a/tests/ui/layout/unconstrained-param-ice-137308.stderr b/tests/ui/layout/unconstrained-param-ice-137308.stderr new file mode 100644 index 0000000000000..615c131eb9045 --- /dev/null +++ b/tests/ui/layout/unconstrained-param-ice-137308.stderr @@ -0,0 +1,15 @@ +error[E0207]: the type parameter `C` is not constrained by the impl trait, self type, or predicates + --> $DIR/unconstrained-param-ice-137308.rs:13:6 + | +LL | impl A for u8 { + | ^ unconstrained type parameter + +error: the type has an unknown layout + --> $DIR/unconstrained-param-ice-137308.rs:18:1 + | +LL | struct S([u8; ::B]); + | ^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0207`.