diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index c2bf075444f..964222307bc 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -31,8 +31,10 @@ 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::query::NoSolution; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; @@ -152,6 +154,34 @@ pub fn super_combine_consts( 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(NoSolution) = self.tcx.check_tys_might_be_eq(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)), diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 71c9058a696..f02e3a9aa29 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2173,4 +2173,11 @@ 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_tys_might_be_eq(arg: Canonical<'tcx, (ty::ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>) -> Result<(), NoSolution> { + desc { "check whether two const param are definitely not equal to eachother"} + } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 8b08ea9a58c..33b509ec490 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -9,7 +9,6 @@ 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 diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index a41a601f2db..de730773794 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -1,12 +1,15 @@ //! 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::traits::query::NoSolution; 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 +134,19 @@ pub fn type_allowed_to_implement_copy<'tcx>( Ok(()) } + +pub fn check_tys_might_be_eq<'tcx>( + tcx: TyCtxt<'tcx>, + canonical: Canonical<'tcx, (ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>, +) -> Result<(), NoSolution> { + 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(NoSolution) } else { Ok(()) } +} diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 067e63de911..41ee8cd9d3f 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -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_tys_might_be_eq: misc::check_tys_might_be_eq, is_impossible_method, ..*providers }; diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_ty_with_infer_1.rs b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_ty_with_infer_1.rs new file mode 100644 index 00000000000..1e248411830 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_ty_with_infer_1.rs @@ -0,0 +1,30 @@ +// check-pass +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +// issue #107899 +// We end up relating `Const(ty: size_of, kind: Value(Branch([])))` with +// `Const(ty: size_of, 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 { + fn f(self); + fn g(self); +} + +struct Y; + +impl X for Y +where + [(); size_of::()]: Sized, +{ + fn f(self) { + self.g(); + } + fn g(self) {} +} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_ty_with_infer_2.rs b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_ty_with_infer_2.rs new file mode 100644 index 00000000000..91a8a7c4a01 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_ty_with_infer_2.rs @@ -0,0 +1,151 @@ +// check-pass +#![feature(inline_const, generic_const_exprs)] +#![allow(incomplete_features)] +use std::marker::PhantomData; + +pub struct Equal(); +pub trait True {} +impl True for Equal {} + +// replacement for generativity +pub struct Id<'id>(PhantomData &'id ()>); +pub struct Guard<'id>(Id<'id>); +fn make_guard<'id>(i: &'id Id<'id>) -> Guard<'id> { + Guard(Id(PhantomData)) +} + +impl<'id> Into> for Guard<'id> { + fn into(self) -> Id<'id> { + self.0 + } +} + +pub struct Arena<'life> { + bytes: *mut [u8], + //bitmap: RefCell, + _token: PhantomData>, +} + +#[repr(transparent)] +pub struct Item<'life, T> { + data: T, + _phantom: PhantomData>, +} + +#[repr(transparent)] +pub struct Token<'life, 'borrow, 'compact, 'reborrow, T> +where + 'life: 'reborrow, + T: Tokenize<'life, 'borrow, 'compact, 'reborrow>, +{ + //ptr: *mut ::Tokenized, + ptr: core::ptr::NonNull, + _phantom: PhantomData>, + _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::() }, { core::mem::size_of::() }>: 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>); +struct TokenFoo<'life, 'borrow, 'compact, 'reborrow>( + Option>, +); +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() {}