make relate
's const ty assertion use semantic equality
This commit is contained in:
parent
8dabf5da9e
commit
a85b0101e6
7 changed files with 237 additions and 22 deletions
|
@ -31,6 +31,7 @@ use super::{InferCtxt, MiscVariable, TypeTrace};
|
|||
use crate::traits::{Obligation, PredicateObligations};
|
||||
use rustc_data_structures::sso::SsoHashMap;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::infer::canonical::OriginalQueryValues;
|
||||
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
|
||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
|
@ -152,6 +153,33 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
let a = self.shallow_resolve(a);
|
||||
let b = self.shallow_resolve(b);
|
||||
|
||||
// We should never have to relate the `ty` field on `Const` as it is checked elsewhere that consts have the
|
||||
// correct type for the generic param they are an argument for. However there have been a number of cases
|
||||
// historically where asserting that the types are equal has found bugs in the compiler so this is valuable
|
||||
// to check even if it is a bit nasty impl wise :(
|
||||
//
|
||||
// This probe is probably not strictly necessary but it seems better to be safe and not accidentally find
|
||||
// ourselves with a check to find bugs being required for code to compile because it made inference progress.
|
||||
self.probe(|_| {
|
||||
if a.ty() == b.ty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't have access to trait solving machinery in `rustc_infer` so the logic for determining if the
|
||||
// two const param's types are able to be equal has to go through a canonical query with the actual logic
|
||||
// in `rustc_trait_selection`.
|
||||
let canonical = self.canonicalize_query(
|
||||
(relation.param_env(), a.ty(), b.ty()),
|
||||
&mut OriginalQueryValues::default(),
|
||||
);
|
||||
if let Err(()) = self.tcx.check_const_param_definitely_unequal(canonical) {
|
||||
self.tcx.sess.delay_span_bug(
|
||||
DUMMY_SP,
|
||||
&format!("cannot relate consts of different types (a={:?}, b={:?})", a, b,),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
match (a.kind(), b.kind()) {
|
||||
(
|
||||
ty::ConstKind::Infer(InferConst::Var(a_vid)),
|
||||
|
|
|
@ -2168,4 +2168,11 @@ rustc_queries! {
|
|||
desc { "traits in scope for documentation links for a module" }
|
||||
separate_provide_extern
|
||||
}
|
||||
|
||||
/// Used in `super_combine_consts` to ICE if the type of the two consts are definitely not going to end up being
|
||||
/// equal to eachother. This might return `Ok` even if the types are unequal, but will never return `Err` if
|
||||
/// the types might be equal.
|
||||
query check_const_param_definitely_unequal(arg: Canonical<'tcx, (ty::ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>) -> Result<(), ()> {
|
||||
desc { "check whether two const param are definitely not equal to eachother"}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ use crate::ty::{self, Expr, ImplSubject, Term, TermKind, Ty, TyCtxt, TypeFoldabl
|
|||
use crate::ty::{GenericArg, GenericArgKind, SubstsRef};
|
||||
use rustc_hir as ast;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_target::spec::abi;
|
||||
use std::iter;
|
||||
|
||||
|
@ -594,25 +593,6 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
|
|||
debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
|
||||
let tcx = relation.tcx();
|
||||
|
||||
let a_ty;
|
||||
let b_ty;
|
||||
if relation.tcx().features().adt_const_params {
|
||||
a_ty = tcx.normalize_erasing_regions(relation.param_env(), a.ty());
|
||||
b_ty = tcx.normalize_erasing_regions(relation.param_env(), b.ty());
|
||||
} else {
|
||||
a_ty = tcx.erase_regions(a.ty());
|
||||
b_ty = tcx.erase_regions(b.ty());
|
||||
}
|
||||
if a_ty != b_ty {
|
||||
relation.tcx().sess.delay_span_bug(
|
||||
DUMMY_SP,
|
||||
&format!(
|
||||
"cannot relate constants ({:?}, {:?}) of different types: {} != {}",
|
||||
a, b, a_ty, b_ty
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// HACK(const_generics): We still need to eagerly evaluate consts when
|
||||
// relating them because during `normalize_param_env_or_error`,
|
||||
// we may relate an evaluated constant in a obligation against
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
|
||||
|
||||
use crate::traits::{self, ObligationCause};
|
||||
use crate::traits::{self, ObligationCause, ObligationCtxt};
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::canonical::Canonical;
|
||||
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
|
||||
use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
|
||||
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitable};
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
use super::outlives_bounds::InferCtxtExt;
|
||||
|
||||
|
@ -131,3 +133,19 @@ pub fn type_allowed_to_implement_copy<'tcx>(
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_const_param_definitely_unequal<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
canonical: Canonical<'tcx, (ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>,
|
||||
) -> Result<(), ()> {
|
||||
let (infcx, (param_env, ty_a, ty_b), _) =
|
||||
tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
|
||||
let result = ocx.eq(&ObligationCause::dummy(), param_env, ty_a, ty_b);
|
||||
// use `select_where_possible` instead of `select_all_or_error` so that
|
||||
// we don't get errors from obligations being ambiguous.
|
||||
let errors = ocx.select_where_possible();
|
||||
|
||||
if errors.len() > 0 || result.is_err() { Err(()) } else { Ok(()) }
|
||||
}
|
||||
|
|
|
@ -554,6 +554,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
|
|||
specialization_graph_of: specialize::specialization_graph_provider,
|
||||
specializes: specialize::specializes,
|
||||
subst_and_check_impossible_predicates,
|
||||
check_const_param_definitely_unequal: misc::check_const_param_definitely_unequal,
|
||||
is_impossible_method,
|
||||
..*providers
|
||||
};
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// check-pass
|
||||
#![feature(generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
// issue #107899
|
||||
// We end up relating `Const(ty: size_of<?0>, kind: Value(Branch([])))` with
|
||||
// `Const(ty: size_of<T>, kind: Value(Branch([])))` which if you were to `==`
|
||||
// the `ty` fields would return `false` and ICE. This test checks that we use
|
||||
// actual semantic equality that takes into account aliases and infer vars.
|
||||
|
||||
use std::mem::size_of;
|
||||
|
||||
trait X<T> {
|
||||
fn f(self);
|
||||
fn g(self);
|
||||
}
|
||||
|
||||
struct Y;
|
||||
|
||||
impl<T> X<T> for Y
|
||||
where
|
||||
[(); size_of::<T>()]: Sized,
|
||||
{
|
||||
fn f(self) {
|
||||
self.g();
|
||||
}
|
||||
fn g(self) {}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,151 @@
|
|||
// check-pass
|
||||
#![feature(inline_const, generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct Equal<const T: usize, const R: usize>();
|
||||
pub trait True {}
|
||||
impl<const T: usize> True for Equal<T, T> {}
|
||||
|
||||
// replacement for generativity
|
||||
pub struct Id<'id>(PhantomData<fn(&'id ()) -> &'id ()>);
|
||||
pub struct Guard<'id>(Id<'id>);
|
||||
fn make_guard<'id>(i: &'id Id<'id>) -> Guard<'id> {
|
||||
Guard(Id(PhantomData))
|
||||
}
|
||||
|
||||
impl<'id> Into<Id<'id>> for Guard<'id> {
|
||||
fn into(self) -> Id<'id> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Arena<'life> {
|
||||
bytes: *mut [u8],
|
||||
//bitmap: RefCell<RoaringBitmap>,
|
||||
_token: PhantomData<Id<'life>>,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Item<'life, T> {
|
||||
data: T,
|
||||
_phantom: PhantomData<Id<'life>>,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Token<'life, 'borrow, 'compact, 'reborrow, T>
|
||||
where
|
||||
'life: 'reborrow,
|
||||
T: Tokenize<'life, 'borrow, 'compact, 'reborrow>,
|
||||
{
|
||||
//ptr: *mut <T as Tokenize>::Tokenized,
|
||||
ptr: core::ptr::NonNull<T::Tokenized>,
|
||||
_phantom: PhantomData<Id<'life>>,
|
||||
_compact: PhantomData<&'borrow Guard<'compact>>,
|
||||
_result: PhantomData<&'reborrow T::Untokenized>,
|
||||
}
|
||||
|
||||
impl<'life> Arena<'life> {
|
||||
pub fn tokenize<'before, 'compact, 'borrow, 'reborrow, T, U>(
|
||||
&self,
|
||||
guard: &'borrow Guard<'compact>,
|
||||
item: Item<'life, &'before mut T>,
|
||||
) -> Token<'life, 'borrow, 'compact, 'reborrow, U>
|
||||
where
|
||||
T: Tokenize<'life, 'borrow, 'compact, 'reborrow, Untokenized = U>,
|
||||
T::Untokenized: Tokenize<'life, 'borrow, 'compact, 'reborrow>,
|
||||
Equal<{ core::mem::size_of::<T>() }, { core::mem::size_of::<U>() }>: True,
|
||||
'compact: 'borrow,
|
||||
'life: 'reborrow,
|
||||
'life: 'compact,
|
||||
'life: 'borrow,
|
||||
// 'borrow: 'before ??
|
||||
{
|
||||
let dst = item.data as *mut T as *mut T::Tokenized;
|
||||
Token {
|
||||
ptr: core::ptr::NonNull::new(dst as *mut _).unwrap(),
|
||||
_phantom: PhantomData,
|
||||
_compact: PhantomData,
|
||||
_result: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Tokenize<'life, 'borrow, 'compact, 'reborrow>
|
||||
where
|
||||
'compact: 'borrow,
|
||||
'life: 'reborrow,
|
||||
'life: 'borrow,
|
||||
'life: 'compact,
|
||||
{
|
||||
type Tokenized;
|
||||
type Untokenized;
|
||||
const TO: fn(&Arena<'life>, &'borrow Guard<'compact>, Self) -> Self::Tokenized;
|
||||
const FROM: fn(&'reborrow Arena<'life>, Self::Tokenized) -> Self::Untokenized;
|
||||
}
|
||||
|
||||
macro_rules! tokenize {
|
||||
($to:expr, $from:expr) => {
|
||||
const TO: fn(&Arena<'life>, &'borrow Guard<'compact>, Self) -> Self::Tokenized = $to;
|
||||
const FROM: fn(&'reborrow Arena<'life>, Self::Tokenized) -> Self::Untokenized = $from;
|
||||
};
|
||||
}
|
||||
|
||||
struct Foo<'life, 'borrow>(Option<Item<'life, &'borrow mut Bar>>);
|
||||
struct TokenFoo<'life, 'borrow, 'compact, 'reborrow>(
|
||||
Option<Token<'life, 'borrow, 'compact, 'reborrow, Bar>>,
|
||||
);
|
||||
struct Bar(u8);
|
||||
|
||||
impl<'life, 'before, 'borrow, 'compact, 'reborrow> Tokenize<'life, 'borrow, 'compact, 'reborrow>
|
||||
for Foo<'life, 'before>
|
||||
where
|
||||
'compact: 'borrow,
|
||||
'life: 'reborrow,
|
||||
'life: 'borrow,
|
||||
'life: 'compact,
|
||||
{
|
||||
type Tokenized = TokenFoo<'life, 'borrow, 'compact, 'reborrow>;
|
||||
type Untokenized = Foo<'life, 'reborrow>;
|
||||
tokenize!(foo_to, foo_from);
|
||||
}
|
||||
|
||||
impl<'life, 'borrow, 'compact, 'reborrow> Tokenize<'life, 'borrow, 'compact, 'reborrow> for Bar
|
||||
where
|
||||
'compact: 'borrow,
|
||||
'life: 'reborrow,
|
||||
'life: 'borrow,
|
||||
'life: 'compact,
|
||||
{
|
||||
type Tokenized = Bar;
|
||||
type Untokenized = Bar;
|
||||
tokenize!(bar_to, bar_from);
|
||||
}
|
||||
|
||||
fn bar_to<'life, 'borrow, 'compact>(
|
||||
arena: &Arena<'life>,
|
||||
guard: &'borrow Guard<'compact>,
|
||||
s: Bar,
|
||||
) -> Bar {
|
||||
s
|
||||
}
|
||||
fn bar_from<'life, 'reborrow>(arena: &'reborrow Arena<'life>, s: Bar) -> Bar {
|
||||
s
|
||||
}
|
||||
|
||||
fn foo_to<'life, 'borrow, 'compact, 'reborrow, 'before>(
|
||||
arena: &'before Arena<'life>,
|
||||
guard: &'borrow Guard<'compact>,
|
||||
s: Foo<'life, 'before>,
|
||||
) -> TokenFoo<'life, 'borrow, 'compact, 'reborrow> {
|
||||
let Foo(bar) = s;
|
||||
TokenFoo(bar.map(|bar| arena.tokenize(guard, bar)))
|
||||
}
|
||||
fn foo_from<'life, 'borrow, 'compact, 'reborrow>(
|
||||
arena: &'reborrow Arena<'life>,
|
||||
s: TokenFoo<'life, 'borrow, 'compact, 'reborrow>,
|
||||
) -> Foo<'life, 'reborrow> {
|
||||
Foo(s.0.map(|bar| panic!()))
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue