From a85b0101e6a1d7d2a7a51d0e5472c1a1215b3031 Mon Sep 17 00:00:00 2001 From: Boxy Date: Sat, 11 Feb 2023 23:05:11 +0000 Subject: [PATCH 1/2] make `relate`'s const ty assertion use semantic equality --- compiler/rustc_infer/src/infer/combine.rs | 28 ++++ compiler/rustc_middle/src/query/mod.rs | 7 + compiler/rustc_middle/src/ty/relate.rs | 20 --- .../rustc_trait_selection/src/traits/misc.rs | 22 ++- .../rustc_trait_selection/src/traits/mod.rs | 1 + .../const_kind_expr/relate_ty_with_infer_1.rs | 30 ++++ .../const_kind_expr/relate_ty_with_infer_2.rs | 151 ++++++++++++++++++ 7 files changed, 237 insertions(+), 22 deletions(-) create mode 100644 tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_ty_with_infer_1.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/const_kind_expr/relate_ty_with_infer_2.rs diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 4da2a67414498..76834c3b36841 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -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)), diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d37d6b37a37c3..d8f7614c56f42 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -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"} + } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 890dabde1f73d..da6b9e3ed23d9 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -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 diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index a41a601f2db07..39654258dcda9 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -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(()) } +} diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 83458017e00f0..f34d55531e767 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_const_param_definitely_unequal: misc::check_const_param_definitely_unequal, 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 0000000000000..1e248411830cd --- /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 0000000000000..91a8a7c4a0121 --- /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() {} From 57ad73aa27dcfc1341e62738b3b79690a51beccd Mon Sep 17 00:00:00 2001 From: Boxy Date: Sun, 12 Feb 2023 19:32:07 +0000 Subject: [PATCH 2/2] rename query and use `NoSolution` --- compiler/rustc_infer/src/infer/combine.rs | 4 +++- compiler/rustc_middle/src/query/mod.rs | 2 +- compiler/rustc_trait_selection/src/traits/misc.rs | 7 ++++--- compiler/rustc_trait_selection/src/traits/mod.rs | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 76834c3b36841..49567c341d4d8 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -34,6 +34,7 @@ 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}; @@ -172,7 +173,8 @@ impl<'tcx> InferCtxt<'tcx> { (relation.param_env(), a.ty(), b.ty()), &mut OriginalQueryValues::default(), ); - if let Err(()) = self.tcx.check_const_param_definitely_unequal(canonical) { + + 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,), diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d8f7614c56f42..b0660c3d62b87 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -2172,7 +2172,7 @@ rustc_queries! { /// 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<(), ()> { + 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_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 39654258dcda9..de730773794b6 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -6,6 +6,7 @@ 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, ParamEnv, Ty, TyCtxt, TypeVisitable}; use rustc_span::DUMMY_SP; @@ -134,10 +135,10 @@ pub fn type_allowed_to_implement_copy<'tcx>( Ok(()) } -pub fn check_const_param_definitely_unequal<'tcx>( +pub fn check_tys_might_be_eq<'tcx>( tcx: TyCtxt<'tcx>, canonical: Canonical<'tcx, (ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>, -) -> Result<(), ()> { +) -> Result<(), NoSolution> { let (infcx, (param_env, ty_a, ty_b), _) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical); let ocx = ObligationCtxt::new(&infcx); @@ -147,5 +148,5 @@ pub fn check_const_param_definitely_unequal<'tcx>( // 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(()) } + 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 f34d55531e767..227119ac7ac9f 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -554,7 +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, + check_tys_might_be_eq: misc::check_tys_might_be_eq, is_impossible_method, ..*providers };