Rollup merge of #145627 - compiler-errors:const-supertrait-dyn-compat, r=fee1-dead

Unconditionally-const supertraits are considered not dyn compatible

Let's save some space in the design of const traits by making `dyn Trait` where `trait Trait: const Super` not dyn compatible.

Such a trait cannot satisfy `dyn Trait: Trait`; we could in the future make this dyn compatible but *NOT* implement `Trait`, but that's a bit weird and seems like it needs to be independently justified moving forward.

Fixes https://github.com/rust-lang/rust/issues/145198

r? fee1-dead
This commit is contained in:
Jacob Pratt
2025-08-21 01:12:21 -04:00
committed by GitHub
4 changed files with 75 additions and 11 deletions

View File

@@ -760,6 +760,9 @@ pub enum DynCompatibilityViolation {
// Supertrait has a non-lifetime `for<T>` binder.
SupertraitNonLifetimeBinder(SmallVec<[Span; 1]>),
// Trait has a `const Trait` supertrait.
SupertraitConst(SmallVec<[Span; 1]>),
/// Method has something illegal.
Method(Symbol, MethodViolationCode, Span),
@@ -785,6 +788,9 @@ impl DynCompatibilityViolation {
DynCompatibilityViolation::SupertraitNonLifetimeBinder(_) => {
"where clause cannot reference non-lifetime `for<...>` variables".into()
}
DynCompatibilityViolation::SupertraitConst(_) => {
"it cannot have a `const` supertrait".into()
}
DynCompatibilityViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => {
format!("associated function `{name}` has no `self` parameter").into()
}
@@ -842,7 +848,8 @@ impl DynCompatibilityViolation {
match self {
DynCompatibilityViolation::SizedSelf(_)
| DynCompatibilityViolation::SupertraitSelf(_)
| DynCompatibilityViolation::SupertraitNonLifetimeBinder(..) => {
| DynCompatibilityViolation::SupertraitNonLifetimeBinder(..)
| DynCompatibilityViolation::SupertraitConst(_) => {
DynCompatibilityViolationSolution::None
}
DynCompatibilityViolation::Method(
@@ -873,15 +880,17 @@ impl DynCompatibilityViolation {
match self {
DynCompatibilityViolation::SupertraitSelf(spans)
| DynCompatibilityViolation::SizedSelf(spans)
| DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans) => spans.clone(),
| DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans)
| DynCompatibilityViolation::SupertraitConst(spans) => spans.clone(),
DynCompatibilityViolation::AssocConst(_, span)
| DynCompatibilityViolation::GAT(_, span)
| DynCompatibilityViolation::Method(_, _, span)
if *span != DUMMY_SP =>
{
| DynCompatibilityViolation::Method(_, _, span) => {
if *span != DUMMY_SP {
smallvec![*span]
} else {
smallvec![]
}
}
_ => smallvec![],
}
}
}

View File

@@ -106,6 +106,10 @@ fn dyn_compatibility_violations_for_trait(
if !spans.is_empty() {
violations.push(DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans));
}
let spans = super_predicates_are_unconditionally_const(tcx, trait_def_id);
if !spans.is_empty() {
violations.push(DynCompatibilityViolation::SupertraitConst(spans));
}
violations
}
@@ -247,16 +251,31 @@ fn super_predicates_have_non_lifetime_binders(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
) -> SmallVec<[Span; 1]> {
// If non_lifetime_binders is disabled, then exit early
if !tcx.features().non_lifetime_binders() {
return SmallVec::new();
}
tcx.explicit_super_predicates_of(trait_def_id)
.iter_identity_copied()
.filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(span))
.collect()
}
/// Checks for `const Trait` supertraits. We're okay with `[const] Trait`,
/// supertraits since for a non-const instantiation of that trait, the
/// conditionally-const supertrait is also not required to be const.
fn super_predicates_are_unconditionally_const(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
) -> SmallVec<[Span; 1]> {
tcx.explicit_super_predicates_of(trait_def_id)
.iter_identity_copied()
.filter_map(|(pred, span)| {
if let ty::ClauseKind::HostEffect(_) = pred.kind().skip_binder() {
Some(span)
} else {
None
}
})
.collect()
}
fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
tcx.generics_require_sized_self(trait_def_id)
}

View File

@@ -0,0 +1,18 @@
#![feature(const_trait_impl)]
const trait Super {}
// Not ok
const trait Unconditionally: const Super {}
fn test() {
let _: &dyn Unconditionally;
//~^ ERROR the trait `Unconditionally` is not dyn compatible
}
// Okay
const trait Conditionally: [const] Super {}
fn test2() {
let _: &dyn Conditionally;
}
fn main() {}

View File

@@ -0,0 +1,18 @@
error[E0038]: the trait `Unconditionally` is not dyn compatible
--> $DIR/const-supertraits-dyn-compat.rs:8:17
|
LL | let _: &dyn Unconditionally;
| ^^^^^^^^^^^^^^^ `Unconditionally` is not dyn compatible
|
note: for a trait to be dyn compatible it needs to allow building a vtable
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
--> $DIR/const-supertraits-dyn-compat.rs:6:30
|
LL | const trait Unconditionally: const Super {}
| --------------- ^^^^^^^^^^^ ...because it cannot have a `const` supertrait
| |
| this trait is not dyn compatible...
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0038`.