Skip to content

Commit

Permalink
Rollup merge of rust-lang#123170 - compiler-errors:const-statics, r=lcnr
Browse files Browse the repository at this point in the history
Replace regions in const canonical vars' types with `'static` in next-solver canonicalizer

We shouldn't ever have non-static regions in consts on stable (or really any regions at all, lol).

The test I committed is less minimal than, e.g., rust-lang#123155 -- however, I believe that it actually portrays the underlying issue here a bit better than that one.

In the linked issue, we end up emitting a normalizes-to predicate for a const placeholder because we don't actually unify `false` and `""`. In the test I committed, we emit a normalizes-to predicate as a part of actually solving a negative coherence goal.

Fixes rust-lang#123155
Fixes rust-lang#118783

r? lcnr
  • Loading branch information
matthiaskrgr authored Mar 30, 2024
2 parents 5977d63 + 6439c7f commit 93f1443
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 73 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ impl<'tcx> rustc_type_ir::new::Region<TyCtxt<'tcx>> for Region<'tcx> {
fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self {
Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::BrAnon })
}

fn new_static(tcx: TyCtxt<'tcx>) -> Self {
tcx.lifetimes.re_static
}
}

/// Region utilities
Expand Down
89 changes: 58 additions & 31 deletions compiler/rustc_next_trait_solver/src/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
Region::new_anon_bound(self.interner(), self.binder_index, var)
}

fn fold_ty(&mut self, t: I::Ty) -> I::Ty
where
I::Ty: TypeSuperFoldable<I>,
{
fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
let kind = match t.kind() {
ty::Infer(i) => match i {
ty::TyVar(vid) => {
Expand Down Expand Up @@ -378,47 +375,48 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
Ty::new_anon_bound(self.interner(), self.binder_index, var)
}

fn fold_const(&mut self, c: I::Const) -> I::Const
where
I::Const: TypeSuperFoldable<I>,
{
fn fold_const(&mut self, c: I::Const) -> I::Const {
// We could canonicalize all consts with static types, but the only ones we
// *really* need to worry about are the ones that we end up putting into `CanonicalVarKind`
// since canonical vars can't reference other canonical vars.
let ty = c
.ty()
.fold_with(&mut RegionsToStatic { interner: self.interner(), binder: ty::INNERMOST });
let kind = match c.kind() {
ty::ConstKind::Infer(i) => {
// FIXME: we should fold the ty too eventually
match i {
ty::InferConst::Var(vid) => {
assert_eq!(
self.infcx.root_ct_var(vid),
vid,
"region vid should have been resolved fully before canonicalization"
);
assert_eq!(
self.infcx.probe_ct_var(vid),
None,
"region vid should have been resolved fully before canonicalization"
);
CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap(), c.ty())
}
ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect,
ty::InferConst::Fresh(_) => todo!(),
ty::ConstKind::Infer(i) => match i {
ty::InferConst::Var(vid) => {
assert_eq!(
self.infcx.root_ct_var(vid),
vid,
"region vid should have been resolved fully before canonicalization"
);
assert_eq!(
self.infcx.probe_ct_var(vid),
None,
"region vid should have been resolved fully before canonicalization"
);
CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap(), ty)
}
}
ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect,
ty::InferConst::Fresh(_) => todo!(),
},
ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst(
PlaceholderLike::new(placeholder.universe(), self.variables.len().into()),
c.ty(),
ty,
),
CanonicalizeMode::Response { .. } => {
CanonicalVarKind::PlaceholderConst(placeholder, c.ty())
CanonicalVarKind::PlaceholderConst(placeholder, ty)
}
},
ty::ConstKind::Param(_) => match self.canonicalize_mode {
CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst(
PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()),
c.ty(),
ty,
),
CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"),
},
// FIXME: See comment above -- we could fold the region separately or something.
ty::ConstKind::Bound(_, _)
| ty::ConstKind::Unevaluated(_)
| ty::ConstKind::Value(_)
Expand All @@ -435,6 +433,35 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
}),
);

Const::new_anon_bound(self.interner(), self.binder_index, var, c.ty())
Const::new_anon_bound(self.interner(), self.binder_index, var, ty)
}
}

struct RegionsToStatic<I> {
interner: I,
binder: ty::DebruijnIndex,
}

impl<I: Interner> TypeFolder<I> for RegionsToStatic<I> {
fn interner(&self) -> I {
self.interner
}

fn fold_binder<T>(&mut self, t: I::Binder<T>) -> I::Binder<T>
where
T: TypeFoldable<I>,
I::Binder<T>: TypeSuperFoldable<I>,
{
self.binder.shift_in(1);
let t = t.fold_with(self);
self.binder.shift_out(1);
t
}

fn fold_region(&mut self, r: I::Region) -> I::Region {
match r.kind() {
ty::ReBound(db, _) if self.binder > db => r,
_ => Region::new_static(self.interner()),
}
}
}
51 changes: 11 additions & 40 deletions compiler/rustc_type_ir/src/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,31 +136,21 @@ pub trait TypeFolder<I: Interner>: FallibleTypeFolder<I, Error = Never> {
t.super_fold_with(self)
}

fn fold_ty(&mut self, t: I::Ty) -> I::Ty
where
I::Ty: TypeSuperFoldable<I>,
{
fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
t.super_fold_with(self)
}

// The default region folder is a no-op because `Region` is non-recursive
// and has no `super_fold_with` method to call. That also explains the
// lack of `I::Region: TypeSuperFoldable<I>` bound on this method.
// and has no `super_fold_with` method to call.
fn fold_region(&mut self, r: I::Region) -> I::Region {
r
}

fn fold_const(&mut self, c: I::Const) -> I::Const
where
I::Const: TypeSuperFoldable<I>,
{
fn fold_const(&mut self, c: I::Const) -> I::Const {
c.super_fold_with(self)
}

fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate
where
I::Predicate: TypeSuperFoldable<I>,
{
fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
p.super_fold_with(self)
}
}
Expand All @@ -185,31 +175,21 @@ pub trait FallibleTypeFolder<I: Interner>: Sized {
t.try_super_fold_with(self)
}

fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Self::Error>
where
I::Ty: TypeSuperFoldable<I>,
{
fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Self::Error> {
t.try_super_fold_with(self)
}

// The default region folder is a no-op because `Region` is non-recursive
// and has no `super_fold_with` method to call. That also explains the
// lack of `I::Region: TypeSuperFoldable<I>` bound on this method.
// and has no `super_fold_with` method to call.
fn try_fold_region(&mut self, r: I::Region) -> Result<I::Region, Self::Error> {
Ok(r)
}

fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Self::Error>
where
I::Const: TypeSuperFoldable<I>,
{
fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Self::Error> {
c.try_super_fold_with(self)
}

fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error>
where
I::Predicate: TypeSuperFoldable<I>,
{
fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error> {
p.try_super_fold_with(self)
}
}
Expand All @@ -234,28 +214,19 @@ where
Ok(self.fold_binder(t))
}

fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Never>
where
I::Ty: TypeSuperFoldable<I>,
{
fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Never> {
Ok(self.fold_ty(t))
}

fn try_fold_region(&mut self, r: I::Region) -> Result<I::Region, Never> {
Ok(self.fold_region(r))
}

fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Never>
where
I::Const: TypeSuperFoldable<I>,
{
fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Never> {
Ok(self.fold_const(c))
}

fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Never>
where
I::Predicate: TypeSuperFoldable<I>,
{
fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Never> {
Ok(self.fold_predicate(p))
}
}
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ use smallvec::SmallVec;
use std::fmt::Debug;
use std::hash::Hash;

use crate::fold::TypeSuperFoldable;
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
use crate::{
new, BoundVar, BoundVars, CanonicalVarInfo, ConstKind, DebugWithInfcx, RegionKind, TyKind,
UniverseIndex,
};

pub trait Interner: Sized {
pub trait Interner: Sized + Copy {
type DefId: Copy + Debug + Hash + Eq;
type AdtDef: Copy + Debug + Hash + Eq;

Expand All @@ -34,6 +35,7 @@ pub trait Interner: Sized {
+ Into<Self::GenericArg>
+ IntoKind<Kind = TyKind<Self>>
+ TypeSuperVisitable<Self>
+ TypeSuperFoldable<Self>
+ Flags
+ new::Ty<Self>;
type Tys: Copy + Debug + Hash + Eq + IntoIterator<Item = Self::Ty>;
Expand All @@ -57,6 +59,7 @@ pub trait Interner: Sized {
+ IntoKind<Kind = ConstKind<Self>>
+ ConstTy<Self>
+ TypeSuperVisitable<Self>
+ TypeSuperFoldable<Self>
+ Flags
+ new::Const<Self>;
type AliasConst: Copy + DebugWithInfcx<Self> + Hash + Eq;
Expand All @@ -82,7 +85,13 @@ pub trait Interner: Sized {
type PlaceholderRegion: Copy + Debug + Hash + Eq + PlaceholderLike;

// Predicates
type Predicate: Copy + Debug + Hash + Eq + TypeSuperVisitable<Self> + Flags;
type Predicate: Copy
+ Debug
+ Hash
+ Eq
+ TypeSuperVisitable<Self>
+ TypeSuperFoldable<Self>
+ Flags;
type TraitPredicate: Copy + Debug + Hash + Eq;
type RegionOutlivesPredicate: Copy + Debug + Hash + Eq;
type TypeOutlivesPredicate: Copy + Debug + Hash + Eq;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_type_ir/src/new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pub trait Ty<I: Interner<Ty = Self>> {

pub trait Region<I: Interner<Region = Self>> {
fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self;

fn new_static(interner: I) -> Self;
}

pub trait Const<I: Interner<Const = Self>> {
Expand Down
23 changes: 23 additions & 0 deletions tests/ui/coherence/negative-coherence/regions-in-canonical.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@ check-pass

#![feature(adt_const_params)]
//~^ WARN the feature `adt_const_params` is incomplete
#![feature(with_negative_coherence, negative_impls)]

pub trait A<const K: &'static str> {}
pub trait C {}


struct W<T>(T);

// Negative coherence:
// Proving `W<!T>: !A<"">` requires proving `CONST alias-eq ""`, which requires proving
// `CONST normalizes-to (?1c: &str)`. The type's region is uniquified, so it ends up being
// put in to the canonical vars list with an infer region => ICE.
impl<T> C for T where T: A<""> {}
impl<T> C for W<T> {}

impl<T> !A<CONST> for W<T> {}
const CONST: &str = "";

fn main() {}
11 changes: 11 additions & 0 deletions tests/ui/coherence/negative-coherence/regions-in-canonical.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/regions-in-canonical.rs:3:12
|
LL | #![feature(adt_const_params)]
| ^^^^^^^^^^^^^^^^
|
= note: see issue #95174 <https://github.com/rust-lang/rust/issues/95174> for more information
= note: `#[warn(incomplete_features)]` on by default

warning: 1 warning emitted

0 comments on commit 93f1443

Please sign in to comment.