Skip to content

Commit 068161e

Browse files
committed
Auto merge of rust-lang#107940 - BoxyUwU:const_ty_assertion_use_semantic_equality, r=compiler-errors
use semantic equality for const param type equality assertion Fixes rust-lang#107898 See added test for what caused this ICE --- The current in assertion in `relate.rs` is rather inadequate when keeping in mind future expansions to const generics: - it will ICE when there are infer vars in a projection in a const param ty - it will spurriously return false when either ty has infer vars because of using `==` instead of `infcx.at(..).eq` - i am also unsure if it would be possible with `adt_const_params` to craft a situation where the const param type is not wf causing `normalize_erasing_regions` to `bug!` when we would have emitted a diagnostic. This impl feels pretty Not Great to me although i am not sure what a better idea would be. - We have to have the logic behind a query because neither `relate.rs` or `combine.rs` have access to trait solving machinery (without evaluating nested obligations this assert will become _far_ less useful under lazy norm, which consts are already doing) - `relate.rs` does not have access to canonicalization machinery which is necessary in order to have types potentially containing infer vars in query arguments. We could possible add a method to `TypeRelation` to do this assertion rather than a query but to avoid implementing the same logic over and over we'd probably end up with the logic in a free function somewhere in `rustc_trait_selection` _anyway_ so I don't think that would be much better. We could also just remove this assertion, it should not actually be necessary for it to be present. It has caught some bugs in the past though so if possible I would like to keep it. r? `@compiler-errors`
2 parents 0416b1a + 57ad73a commit 068161e

File tree

7 files changed

+240
-22
lines changed

7 files changed

+240
-22
lines changed

compiler/rustc_infer/src/infer/combine.rs

+30
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ 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};
37+
use rustc_middle::traits::query::NoSolution;
3638
use rustc_middle::traits::ObligationCause;
3739
use rustc_middle::ty::error::{ExpectedFound, TypeError};
3840
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
@@ -152,6 +154,34 @@ impl<'tcx> InferCtxt<'tcx> {
152154
let a = self.shallow_resolve(a);
153155
let b = self.shallow_resolve(b);
154156

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

compiler/rustc_middle/src/query/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -2173,4 +2173,11 @@ rustc_queries! {
21732173
desc { "traits in scope for documentation links for a module" }
21742174
separate_provide_extern
21752175
}
2176+
2177+
/// Used in `super_combine_consts` to ICE if the type of the two consts are definitely not going to end up being
2178+
/// equal to eachother. This might return `Ok` even if the types are unequal, but will never return `Err` if
2179+
/// the types might be equal.
2180+
query check_tys_might_be_eq(arg: Canonical<'tcx, (ty::ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>) -> Result<(), NoSolution> {
2181+
desc { "check whether two const param are definitely not equal to eachother"}
2182+
}
21762183
}

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

compiler/rustc_trait_selection/src/traits/misc.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
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};
9+
use rustc_infer::traits::query::NoSolution;
810
use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
9-
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
11+
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitable};
12+
use rustc_span::DUMMY_SP;
1013

1114
use super::outlives_bounds::InferCtxtExt;
1215

@@ -131,3 +134,19 @@ pub fn type_allowed_to_implement_copy<'tcx>(
131134

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

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_tys_might_be_eq: misc::check_tys_might_be_eq,
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)