Skip to content

Commit a85b010

Browse files
committed
make relate's const ty assertion use semantic equality
1 parent 8dabf5d commit a85b010

File tree

7 files changed

+237
-22
lines changed

7 files changed

+237
-22
lines changed

Diff for: compiler/rustc_infer/src/infer/combine.rs

+28
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use super::{InferCtxt, MiscVariable, TypeTrace};
3131
use crate::traits::{Obligation, PredicateObligations};
3232
use rustc_data_structures::sso::SsoHashMap;
3333
use rustc_hir::def_id::DefId;
34+
use rustc_middle::infer::canonical::OriginalQueryValues;
3435
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
3536
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
3637
use rustc_middle::traits::ObligationCause;
@@ -152,6 +153,33 @@ impl<'tcx> InferCtxt<'tcx> {
152153
let a = self.shallow_resolve(a);
153154
let b = self.shallow_resolve(b);
154155

156+
// We should never have to relate the `ty` field on `Const` as it is checked elsewhere that consts have the
157+
// correct type for the generic param they are an argument for. However there have been a number of cases
158+
// historically where asserting that the types are equal has found bugs in the compiler so this is valuable
159+
// to check even if it is a bit nasty impl wise :(
160+
//
161+
// This probe is probably not strictly necessary but it seems better to be safe and not accidentally find
162+
// ourselves with a check to find bugs being required for code to compile because it made inference progress.
163+
self.probe(|_| {
164+
if a.ty() == b.ty() {
165+
return;
166+
}
167+
168+
// We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the
169+
// two const param's types are able to be equal has to go through a canonical query with the actual logic
170+
// in `rustc_trait_selection`.
171+
let canonical = self.canonicalize_query(
172+
(relation.param_env(), a.ty(), b.ty()),
173+
&mut OriginalQueryValues::default(),
174+
);
175+
if let Err(()) = self.tcx.check_const_param_definitely_unequal(canonical) {
176+
self.tcx.sess.delay_span_bug(
177+
DUMMY_SP,
178+
&format!("cannot relate consts of different types (a={:?}, b={:?})", a, b,),
179+
);
180+
}
181+
});
182+
155183
match (a.kind(), b.kind()) {
156184
(
157185
ty::ConstKind::Infer(InferConst::Var(a_vid)),

Diff for: compiler/rustc_middle/src/query/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -2168,4 +2168,11 @@ rustc_queries! {
21682168
desc { "traits in scope for documentation links for a module" }
21692169
separate_provide_extern
21702170
}
2171+
2172+
/// Used in `super_combine_consts` to ICE if the type of the two consts are definitely not going to end up being
2173+
/// equal to eachother. This might return `Ok` even if the types are unequal, but will never return `Err` if
2174+
/// the types might be equal.
2175+
query check_const_param_definitely_unequal(arg: Canonical<'tcx, (ty::ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>) -> Result<(), ()> {
2176+
desc { "check whether two const param are definitely not equal to eachother"}
2177+
}
21712178
}

Diff for: compiler/rustc_middle/src/ty/relate.rs

-20
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use crate::ty::{self, Expr, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldabl
99
use crate::ty::{GenericArg, GenericArgKind, SubstsRef};
1010
use rustc_hir as ast;
1111
use rustc_hir::def_id::DefId;
12-
use rustc_span::DUMMY_SP;
1312
use rustc_target::spec::abi;
1413
use std::iter;
1514

@@ -594,25 +593,6 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
594593
debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
595594
let tcx = relation.tcx();
596595

597-
let a_ty;
598-
let b_ty;
599-
if relation.tcx().features().adt_const_params {
600-
a_ty = tcx.normalize_erasing_regions(relation.param_env(), a.ty());
601-
b_ty = tcx.normalize_erasing_regions(relation.param_env(), b.ty());
602-
} else {
603-
a_ty = tcx.erase_regions(a.ty());
604-
b_ty = tcx.erase_regions(b.ty());
605-
}
606-
if a_ty != b_ty {
607-
relation.tcx().sess.delay_span_bug(
608-
DUMMY_SP,
609-
&format!(
610-
"cannot relate constants ({:?}, {:?}) of different types: {} != {}",
611-
a, b, a_ty, b_ty
612-
),
613-
);
614-
}
615-
616596
// HACK(const_generics): We still need to eagerly evaluate consts when
617597
// relating them because during `normalize_param_env_or_error`,
618598
// we may relate an evaluated constant in a obligation against

Diff for: compiler/rustc_trait_selection/src/traits/misc.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
22
3-
use crate::traits::{self, ObligationCause};
3+
use crate::traits::{self, ObligationCause, ObligationCtxt};
44

55
use rustc_data_structures::fx::FxIndexSet;
66
use rustc_hir as hir;
7+
use rustc_infer::infer::canonical::Canonical;
78
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
89
use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
9-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
10+
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitable};
11+
use rustc_span::DUMMY_SP;
1012

1113
use super::outlives_bounds::InferCtxtExt;
1214

@@ -131,3 +133,19 @@ pub fn type_allowed_to_implement_copy<'tcx>(
131133

132134
Ok(())
133135
}
136+
137+
pub fn check_const_param_definitely_unequal<'tcx>(
138+
tcx: TyCtxt<'tcx>,
139+
canonical: Canonical<'tcx, (ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>,
140+
) -> Result<(), ()> {
141+
let (infcx, (param_env, ty_a, ty_b), _) =
142+
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical);
143+
let ocx = ObligationCtxt::new(&infcx);
144+
145+
let result = ocx.eq(&ObligationCause::dummy(), param_env, ty_a, ty_b);
146+
// use `select_where_possible` instead of `select_all_or_error` so that
147+
// we don't get errors from obligations being ambiguous.
148+
let errors = ocx.select_where_possible();
149+
150+
if errors.len() > 0 || result.is_err() { Err(()) } else { Ok(()) }
151+
}

Diff for: compiler/rustc_trait_selection/src/traits/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
554554
specialization_graph_of: specialize::specialization_graph_provider,
555555
specializes: specialize::specializes,
556556
subst_and_check_impossible_predicates,
557+
check_const_param_definitely_unequal: misc::check_const_param_definitely_unequal,
557558
is_impossible_method,
558559
..*providers
559560
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// check-pass
2+
#![feature(generic_const_exprs)]
3+
#![allow(incomplete_features)]
4+
5+
// issue #107899
6+
// We end up relating `Const(ty: size_of<?0>, kind: Value(Branch([])))` with
7+
// `Const(ty: size_of<T>, kind: Value(Branch([])))` which if you were to `==`
8+
// the `ty` fields would return `false` and ICE. This test checks that we use
9+
// actual semantic equality that takes into account aliases and infer vars.
10+
11+
use std::mem::size_of;
12+
13+
trait X<T> {
14+
fn f(self);
15+
fn g(self);
16+
}
17+
18+
struct Y;
19+
20+
impl<T> X<T> for Y
21+
where
22+
[(); size_of::<T>()]: Sized,
23+
{
24+
fn f(self) {
25+
self.g();
26+
}
27+
fn g(self) {}
28+
}
29+
30+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// check-pass
2+
#![feature(inline_const, generic_const_exprs)]
3+
#![allow(incomplete_features)]
4+
use std::marker::PhantomData;
5+
6+
pub struct Equal<const T: usize, const R: usize>();
7+
pub trait True {}
8+
impl<const T: usize> True for Equal<T, T> {}
9+
10+
// replacement for generativity
11+
pub struct Id<'id>(PhantomData<fn(&'id ()) -> &'id ()>);
12+
pub struct Guard<'id>(Id<'id>);
13+
fn make_guard<'id>(i: &'id Id<'id>) -> Guard<'id> {
14+
Guard(Id(PhantomData))
15+
}
16+
17+
impl<'id> Into<Id<'id>> for Guard<'id> {
18+
fn into(self) -> Id<'id> {
19+
self.0
20+
}
21+
}
22+
23+
pub struct Arena<'life> {
24+
bytes: *mut [u8],
25+
//bitmap: RefCell<RoaringBitmap>,
26+
_token: PhantomData<Id<'life>>,
27+
}
28+
29+
#[repr(transparent)]
30+
pub struct Item<'life, T> {
31+
data: T,
32+
_phantom: PhantomData<Id<'life>>,
33+
}
34+
35+
#[repr(transparent)]
36+
pub struct Token<'life, 'borrow, 'compact, 'reborrow, T>
37+
where
38+
'life: 'reborrow,
39+
T: Tokenize<'life, 'borrow, 'compact, 'reborrow>,
40+
{
41+
//ptr: *mut <T as Tokenize>::Tokenized,
42+
ptr: core::ptr::NonNull<T::Tokenized>,
43+
_phantom: PhantomData<Id<'life>>,
44+
_compact: PhantomData<&'borrow Guard<'compact>>,
45+
_result: PhantomData<&'reborrow T::Untokenized>,
46+
}
47+
48+
impl<'life> Arena<'life> {
49+
pub fn tokenize<'before, 'compact, 'borrow, 'reborrow, T, U>(
50+
&self,
51+
guard: &'borrow Guard<'compact>,
52+
item: Item<'life, &'before mut T>,
53+
) -> Token<'life, 'borrow, 'compact, 'reborrow, U>
54+
where
55+
T: Tokenize<'life, 'borrow, 'compact, 'reborrow, Untokenized = U>,
56+
T::Untokenized: Tokenize<'life, 'borrow, 'compact, 'reborrow>,
57+
Equal<{ core::mem::size_of::<T>() }, { core::mem::size_of::<U>() }>: True,
58+
'compact: 'borrow,
59+
'life: 'reborrow,
60+
'life: 'compact,
61+
'life: 'borrow,
62+
// 'borrow: 'before ??
63+
{
64+
let dst = item.data as *mut T as *mut T::Tokenized;
65+
Token {
66+
ptr: core::ptr::NonNull::new(dst as *mut _).unwrap(),
67+
_phantom: PhantomData,
68+
_compact: PhantomData,
69+
_result: PhantomData,
70+
}
71+
}
72+
}
73+
74+
pub trait Tokenize<'life, 'borrow, 'compact, 'reborrow>
75+
where
76+
'compact: 'borrow,
77+
'life: 'reborrow,
78+
'life: 'borrow,
79+
'life: 'compact,
80+
{
81+
type Tokenized;
82+
type Untokenized;
83+
const TO: fn(&Arena<'life>, &'borrow Guard<'compact>, Self) -> Self::Tokenized;
84+
const FROM: fn(&'reborrow Arena<'life>, Self::Tokenized) -> Self::Untokenized;
85+
}
86+
87+
macro_rules! tokenize {
88+
($to:expr, $from:expr) => {
89+
const TO: fn(&Arena<'life>, &'borrow Guard<'compact>, Self) -> Self::Tokenized = $to;
90+
const FROM: fn(&'reborrow Arena<'life>, Self::Tokenized) -> Self::Untokenized = $from;
91+
};
92+
}
93+
94+
struct Foo<'life, 'borrow>(Option<Item<'life, &'borrow mut Bar>>);
95+
struct TokenFoo<'life, 'borrow, 'compact, 'reborrow>(
96+
Option<Token<'life, 'borrow, 'compact, 'reborrow, Bar>>,
97+
);
98+
struct Bar(u8);
99+
100+
impl<'life, 'before, 'borrow, 'compact, 'reborrow> Tokenize<'life, 'borrow, 'compact, 'reborrow>
101+
for Foo<'life, 'before>
102+
where
103+
'compact: 'borrow,
104+
'life: 'reborrow,
105+
'life: 'borrow,
106+
'life: 'compact,
107+
{
108+
type Tokenized = TokenFoo<'life, 'borrow, 'compact, 'reborrow>;
109+
type Untokenized = Foo<'life, 'reborrow>;
110+
tokenize!(foo_to, foo_from);
111+
}
112+
113+
impl<'life, 'borrow, 'compact, 'reborrow> Tokenize<'life, 'borrow, 'compact, 'reborrow> for Bar
114+
where
115+
'compact: 'borrow,
116+
'life: 'reborrow,
117+
'life: 'borrow,
118+
'life: 'compact,
119+
{
120+
type Tokenized = Bar;
121+
type Untokenized = Bar;
122+
tokenize!(bar_to, bar_from);
123+
}
124+
125+
fn bar_to<'life, 'borrow, 'compact>(
126+
arena: &Arena<'life>,
127+
guard: &'borrow Guard<'compact>,
128+
s: Bar,
129+
) -> Bar {
130+
s
131+
}
132+
fn bar_from<'life, 'reborrow>(arena: &'reborrow Arena<'life>, s: Bar) -> Bar {
133+
s
134+
}
135+
136+
fn foo_to<'life, 'borrow, 'compact, 'reborrow, 'before>(
137+
arena: &'before Arena<'life>,
138+
guard: &'borrow Guard<'compact>,
139+
s: Foo<'life, 'before>,
140+
) -> TokenFoo<'life, 'borrow, 'compact, 'reborrow> {
141+
let Foo(bar) = s;
142+
TokenFoo(bar.map(|bar| arena.tokenize(guard, bar)))
143+
}
144+
fn foo_from<'life, 'borrow, 'compact, 'reborrow>(
145+
arena: &'reborrow Arena<'life>,
146+
s: TokenFoo<'life, 'borrow, 'compact, 'reborrow>,
147+
) -> Foo<'life, 'reborrow> {
148+
Foo(s.0.map(|bar| panic!()))
149+
}
150+
151+
fn main() {}

0 commit comments

Comments
 (0)