diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 65cced536603a..6816dd7b5f3e9 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1769,6 +1769,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let maybe_uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { ty::ConstKind::Alias(_, alias_const) => match alias_const.kind { + ty::AliasConstKind::EvidenceProjection { .. } => { + bug!("evidence projection in a MIR constant operand") + } ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::InherentSelf { def_id } | ty::AliasConstKind::InherentImpl { def_id } diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index b46bd86f0497a..61c0089b8864a 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -63,6 +63,11 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { | ty::Coroutine(def_id, args) => self.print_def_path(def_id, args), ty::Foreign(def_id) => self.print_def_path(def_id, &[]), + ty::Alias( + _, + alias @ ty::AliasTy { kind: ty::EvidenceProjection { projection }, .. }, + ) => self.print_def_path(projection.item_def_id, alias.full_args(self.tcx)), + ty::FnDef(def_id, args) => self.print_def_path(def_id, args.no_bound_vars().unwrap()), ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) => { bug!("type_name: unexpected free alias") diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index ea64b9c225c02..516a4f04a504d 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2702,7 +2702,7 @@ fn param_env_with_gat_bounds<'tcx>( .into() } GenericParamDefKind::Const { .. } => { - let bound_var = ty::BoundVariableKind::Const; + let bound_var = ty::BoundVariableKind::Const(None); bound_vars.push(bound_var); ty::Const::new_bound( tcx, diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 4e3aa41942845..a9f0bf2e08b9e 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -206,7 +206,11 @@ impl<'tcx> InherentCollect<'tcx> { ty::Alias( _, ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. }, + kind: + ty::Projection { .. } + | ty::EvidenceProjection { .. } + | ty::Inherent { .. } + | ty::Opaque { .. }, .. }, ) diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index f67ed5e44d20e..27b9c44b6b84c 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -207,7 +207,7 @@ pub(crate) fn orphan_check_impl( // type This = T; // } // impl AutoTrait for ::This {} - ty::Projection { .. } => "associated type", + ty::Projection { .. } | ty::EvidenceProjection { .. } => "associated type", // type Foo = (impl Sized, bool) // impl AutoTrait for Foo {} ty::Free { .. } => "type alias", diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index b3fbdc03e8478..91d39fc6a48f7 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -334,7 +334,7 @@ impl<'tcx> TypeFolder> for MapAndCompressBoundVars<'tcx> { mapped.expect_const() } else { let var = ty::BoundVar::from_usize(self.still_bound_vars.len()); - self.still_bound_vars.push(ty::BoundVariableKind::Const); + self.still_bound_vars.push(ty::BoundVariableKind::Const(None)); let mapped = ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst::new(var)); self.mapping.insert(old_bound.var, mapped.into()); diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 143cc164ea7a4..a293eac861691 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -296,7 +296,7 @@ fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableK ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id)) } GenericParamKind::Type { .. } => ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id)), - GenericParamKind::Const { .. } => ty::BoundVariableKind::Const, + GenericParamKind::Const { .. } => ty::BoundVariableKind::Const(None), } } @@ -313,7 +313,7 @@ fn generic_param_def_as_bound_arg<'tcx>( ty::GenericParamDefKind::Type { .. } => { ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(param.def_id)) } - ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const, + ty::GenericParamDefKind::Const { .. } => ty::BoundVariableKind::Const(None), } } diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 36777359076c3..000d9f83e044f 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -66,7 +66,11 @@ impl<'tcx> TypeVisitor> for ParameterCollector { ty::Alias( _, ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. }, + kind: + ty::Projection { .. } + | ty::EvidenceProjection { .. } + | ty::Inherent { .. } + | ty::Opaque { .. }, .. }, ) if !self.include_nonconstraining => { diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index a2e3883b0684e..be400c3123a41 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -271,6 +271,14 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_invariant_args(current, args, variance); } + ty::Alias(_, alias @ ty::AliasTy { kind: ty::EvidenceProjection { .. }, .. }) => { + self.add_constraints_from_invariant_args( + current, + alias.full_args(self.tcx()), + variance, + ); + } + ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) => { let ty = self.tcx().expand_free_alias_tys(ty); self.add_constraints_from_ty(current, ty, variance); diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 0e3292fc0c497..7655670280861 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -994,10 +994,12 @@ impl<'tcx> InferCtxt<'tcx> { ) -> ty::Term<'tcx> { match alias_term.kind { ty::AliasTermKind::ProjectionTy { .. } + | ty::AliasTermKind::EvidenceProjectionTy { .. } | ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::OpaqueTy { .. } | ty::AliasTermKind::FreeTy { .. } => self.next_ty_var(span).into(), ty::AliasTermKind::FreeConst { .. } + | ty::AliasTermKind::EvidenceProjectionConst { .. } | ty::AliasTermKind::InherentConstSelf { .. } | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::AnonConst { .. } @@ -1526,11 +1528,19 @@ impl<'tcx> InferCtxt<'tcx> { where T: TypeFoldable>, { + let bound_vars = value.bound_vars(); + if bound_vars.iter().any(|kind| { + matches!( + kind, + ty::BoundVariableKind::Const(Some(_)) | ty::BoundVariableKind::Evidence(_) + ) + }) { + bug!("dependent binders require telescope instantiation"); + } if let Some(_) = value.as_ref().no_bound_vars() { return value.skip_binder(); } - let bound_vars = value.bound_vars(); let mut args = Vec::with_capacity(bound_vars.len()); for bound_var_kind in bound_vars { @@ -1539,7 +1549,10 @@ impl<'tcx> InferCtxt<'tcx> { ty::BoundVariableKind::Region(br) => { self.next_region_var(RegionVariableOrigin::BoundRegion(span, br, lbrct)).into() } - ty::BoundVariableKind::Const => self.next_const_var(span).into(), + ty::BoundVariableKind::Const(None) => self.next_const_var(span).into(), + ty::BoundVariableKind::Const(Some(_)) | ty::BoundVariableKind::Evidence(_) => { + unreachable!() + } }; args.push(arg); } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 35d04597451c0..d33347f72fe54 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -178,11 +178,13 @@ impl<'tcx> InferCtxt<'tcx> { } // The old solver only accepts projection predicates for associated types. ty::AliasTermKind::InherentTy { .. } + | ty::AliasTermKind::EvidenceProjectionTy { .. } | ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::OpaqueTy { .. } => { return Err(TypeError::CyclicTy(source_term.expect_type())); } ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::EvidenceProjectionConst { .. } | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::FreeConst { .. } | ty::AliasTermKind::AnonConst { .. } => { diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index cfd5032b7b245..f2f956482e170 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -932,7 +932,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { | ty::Alias( _, ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, + kind: + ty::Projection { .. } + | ty::EvidenceProjection { .. } + | ty::Inherent { .. } + | ty::Free { .. }, .. }, ) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index b7a07e1d9d162..dcd54a5050976 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -10,7 +10,7 @@ pub(super) use cstore_impl::provide; use rustc_ast as ast; use rustc_crate_store::{CrateSource, ExternCrate}; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::owned_slice::OwnedSlice; use rustc_data_structures::sync::Lock; use rustc_data_structures::unhash::UnhashMap; @@ -25,6 +25,7 @@ use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_index::Idx; use rustc_middle::middle::lib_features::LibFeatures; use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState}; +use rustc_middle::traits::solve::TraitEvidence; use rustc_middle::ty::codec::TyDecoder; use rustc_middle::ty::{RestrictionKind, Visibility}; use rustc_middle::{bug, implement_ty_decoder}; @@ -228,6 +229,7 @@ impl<'a> LazyDecoder for BlobDecodeContext<'a> { pub(super) struct MetadataDecodeContext<'a, 'tcx> { blob_decoder: BlobDecodeContext<'a>, cdata: &'a CrateMetadata, + trait_evidence_in_progress: FxHashSet, tcx: TyCtxt<'tcx>, // Used for decoding interpret::AllocIds in a cached & thread-safe manner. @@ -306,6 +308,7 @@ impl<'a, 'tcx> MetaDecoder for (&'a CrateMetadata, TyCtxt<'tcx>) { MetadataDecodeContext { blob_decoder: self.0.blob().decoder(pos), cdata: self.0, + trait_evidence_in_progress: Default::default(), tcx: self.1, alloc_decoding_session: self.0.alloc_decoding_state.new_decoding_session(), } @@ -420,6 +423,29 @@ impl<'a, 'tcx> TyDecoder<'tcx> for MetadataDecodeContext<'a, 'tcx> { ty } + fn cached_trait_evidence_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> TraitEvidence<'tcx> + where + F: FnOnce(&mut Self) -> TraitEvidence<'tcx>, + { + let tcx = self.tcx; + let key = ty::CReaderCacheKey { cnum: Some(self.cdata.cnum), pos: shorthand }; + if let Some(&evidence) = tcx.caches.trait_evidence_rcache.borrow().get(&key) { + return evidence; + } + + let evidence = or_insert_with(self); + tcx.caches.trait_evidence_rcache.borrow_mut().insert(key, evidence); + evidence + } + + fn trait_evidence_in_progress(&mut self) -> &mut FxHashSet { + &mut self.trait_evidence_in_progress + } + fn with_position(&mut self, pos: usize, f: F) -> R where F: FnOnce(&mut Self) -> R, diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 7c2354ebc61a1..6b05ca843a34c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -22,6 +22,7 @@ use rustc_middle::dep_graph::WorkProductId; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::mir::interpret; use rustc_middle::query::Providers; +use rustc_middle::traits::solve::TraitEvidence; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::AssocContainer; use rustc_middle::ty::codec::TyEncoder; @@ -52,6 +53,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { lazy_state: LazyState, span_shorthands: FxHashMap, type_shorthands: FxHashMap, usize>, + trait_evidence_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, interpret_allocs: FxIndexSet, @@ -387,6 +389,10 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { &mut self.predicate_shorthands } + fn trait_evidence_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.trait_evidence_shorthands + } + fn encode_alloc_id(&mut self, alloc_id: &rustc_middle::mir::interpret::AllocId) { let (index, _) = self.interpret_allocs.insert_full(*alloc_id); @@ -2582,6 +2588,7 @@ fn with_encode_metadata_header( lazy_state: LazyState::NoNode, span_shorthands: Default::default(), type_shorthands: Default::default(), + trait_evidence_shorthands: Default::default(), predicate_shorthands: Default::default(), source_file_cache, interpret_allocs: Default::default(), diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index bfaef6157d02c..0735f243ed8ab 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -129,6 +129,7 @@ rustc_arena::declare_arena! { >, external_constraints: rustc_middle::traits::solve::ExternalConstraintsData>, doc_link_resolutions: rustc_middle::middle::resolve::DocLinkResMap, + trait_evidence: rustc_type_ir::solve::TraitEvidenceData>, stripped_cfg_items: rustc_hir::attrs::StrippedCfgItem, mod_child: rustc_middle::middle::resolve::ModChild, features: rustc_feature::Features, diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 406a96ff7ca57..6d2ca67cf9ce8 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -111,6 +111,9 @@ impl<'tcx> TyCtxt<'tcx> { | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => def_id, + ty::AliasConstKind::EvidenceProjection { .. } => { + return Err(ErrorHandled::TooGeneric(DUMMY_SP)); + } }; let cid = match ty::Instance::try_resolve(self, typing_env, def_id, ct.args) { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 008742f96dd86..e8ea6081a3d01 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1504,6 +1504,9 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { | ty::AliasConstKind::InherentImpl { def_id } | ty::AliasConstKind::Free { def_id } | ty::AliasConstKind::Anon { def_id } => self.tcx.def_path_str(def_id), + ty::AliasConstKind::EvidenceProjection { projection } => { + self.tcx.def_path_str(projection.item_def_id) + } }; format!("ty::AliasConst({}, {:?})", kind, alias_const.args) } diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index eae4148a30ed4..9d840b4854e1b 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -297,6 +297,7 @@ impl<'tcx> QueryKey for ty::AliasTyKind<'tcx> { | ty::AliasTyKind::Inherent { def_id } | ty::AliasTyKind::Opaque { def_id } | ty::AliasTyKind::Free { def_id } => def_id, + ty::AliasTyKind::EvidenceProjection { projection } => &projection.item_def_id, }; tcx.def_span(*def_id) } @@ -451,6 +452,7 @@ fn def_id_of_type_cached<'a>(ty: Ty<'a>, visited: &mut SsoHashSet>) -> Op | ty::AliasTyKind::Inherent { def_id } | ty::AliasTyKind::Opaque { def_id } | ty::AliasTyKind::Free { def_id } => Some(def_id), + ty::AliasTyKind::EvidenceProjection { projection } => Some(projection.item_def_id), }, ty::Bool diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 1fa0aa421d584..ff1ba871f2a29 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use std::sync::Arc; use std::{fmt, mem}; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::sync::{HashMapExt, Lock, RwLock}; use rustc_data_structures::unhash::UnhashMap; @@ -29,6 +29,7 @@ use crate::dep_graph::{DepNodeIndex, QuerySideEffect, SerializedDepNodeIndex}; use crate::mir::interpret::{AllocDecodingSession, AllocDecodingState}; use crate::mir::{self, interpret}; use crate::mono::MonoItem; +use crate::traits::solve::TraitEvidence; use crate::ty::codec::{RefDecodable, TyDecoder, TyEncoder}; use crate::ty::{self, Ty, TyCtxt}; @@ -230,6 +231,7 @@ impl OnDiskCache { tcx, encoder, type_shorthands: Default::default(), + trait_evidence_shorthands: Default::default(), predicate_shorthands: Default::default(), interpret_allocs: Default::default(), caching_source_map_view: CachingSourceMapView::new(tcx.sess.source_map()), @@ -375,6 +377,7 @@ impl OnDiskCache { let serialized_data = self.serialized_data.read(); let mut decoder = CacheDecoder { tcx, + trait_evidence_in_progress: Default::default(), opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()) .unwrap(), file_index_to_file: &self.file_index_to_file, @@ -397,6 +400,7 @@ impl OnDiskCache { pub struct CacheDecoder<'a, 'tcx> { tcx: TyCtxt<'tcx>, opaque: MemDecoder<'a>, + trait_evidence_in_progress: FxHashSet, file_index_to_file: &'a Lock>>, file_index_to_stable_id: &'a FxHashMap, alloc_decoding_session: AllocDecodingSession<'a>, @@ -502,6 +506,29 @@ impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { ty } + fn cached_trait_evidence_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> TraitEvidence<'tcx> + where + F: FnOnce(&mut Self) -> TraitEvidence<'tcx>, + { + let tcx = self.tcx; + let key = ty::CReaderCacheKey { cnum: None, pos: shorthand }; + if let Some(&evidence) = tcx.caches.trait_evidence_rcache.borrow().get(&key) { + return evidence; + } + + let evidence = or_insert_with(self); + tcx.caches.trait_evidence_rcache.borrow_mut().insert_same(key, evidence); + evidence + } + + fn trait_evidence_in_progress(&mut self) -> &mut FxHashSet { + &mut self.trait_evidence_in_progress + } + fn with_position(&mut self, pos: usize, f: F) -> R where F: FnOnce(&mut Self) -> R, @@ -780,6 +807,7 @@ pub struct CacheEncoder<'tcx> { tcx: TyCtxt<'tcx>, encoder: FileEncoder<'static>, type_shorthands: FxHashMap, usize>, + trait_evidence_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, interpret_allocs: FxIndexSet, caching_source_map_view: CachingSourceMapView<'tcx>, @@ -961,6 +989,10 @@ impl<'tcx> TyEncoder<'tcx> for CacheEncoder<'tcx> { fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { &mut self.predicate_shorthands } + + fn trait_evidence_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.trait_evidence_shorthands + } #[inline] fn encode_alloc_id(&mut self, alloc_id: &interpret::AllocId) { let (index, _) = self.interpret_allocs.insert_full(*alloc_id); diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 02f9ef365f288..01b11a35f37cd 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -1,4 +1,12 @@ +use std::cell::RefCell; +use std::ptr; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; +use rustc_data_structures::stable_hash::{ + StableHash, StableHashControls, StableHashCtxt, StableHasher, +}; use rustc_macros::StableHash; use rustc_type_ir as ir; pub use rustc_type_ir::solve::*; @@ -21,6 +29,142 @@ pub type GoalStalledOnOpaques<'tcx> = ir::solve::GoalStalledOnOpaques = ir::solve::SucceededInErased>; pub type PredefinedOpaques<'tcx> = &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>; +pub type TraitEvidences<'tcx> = &'tcx ty::List>; + +/// Interned source contract rebased into one dependent binder telescope. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, StableHash)] +pub struct BoundRequiredContract<'tcx>( + pub(crate) Interned<'tcx, ty::BoundRequiredContractData>>, +); + +impl<'tcx> std::ops::Deref for BoundRequiredContract<'tcx> { + type Target = ty::BoundRequiredContractData>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'tcx> TypeFoldable> for BoundRequiredContract<'tcx> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_bound_required_contract(self) + } + + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_bound_required_contract(self) + } +} + +impl<'tcx> TypeVisitable> for BoundRequiredContract<'tcx> { + fn visit_with>>(&self, visitor: &mut V) -> V::Result { + (**self).visit_with(visitor) + } +} + +/// Interned compiler-internal trait evidence value. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct TraitEvidence<'tcx>(pub(crate) Interned<'tcx, TraitEvidenceData>>); + +impl StableHash for TraitEvidence<'_> { + fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + // Like interned lists, shared proof recipes must not be expanded into a tree. + thread_local! { + static CACHE: RefCell> = + RefCell::new(Default::default()); + } + + let hash = CACHE.with(|cache| { + let key = (ptr::from_ref(self.0.0).cast::<()>(), hcx.stable_hash_controls()); + if let Some(&hash) = cache.borrow().get(&key) { + return hash; + } + + let mut hasher = StableHasher::new(); + self.0.0.stable_hash(hcx, &mut hasher); + let hash: Fingerprint = hasher.finish(); + cache.borrow_mut().insert(key, hash); + hash + }); + + hash.stable_hash(hcx, hasher); + } +} + +impl<'tcx> std::ops::Deref for TraitEvidence<'tcx> { + type Target = TraitEvidenceData>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'tcx> TypeFoldable> for TraitEvidence<'tcx> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_trait_evidence(self) + } + + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_trait_evidence(self) + } +} + +impl<'tcx> TypeVisitable> for TraitEvidence<'tcx> { + fn visit_with>>(&self, visitor: &mut V) -> V::Result { + visitor.visit_trait_evidence(*self) + } +} + +impl<'tcx> TypeFoldable> for TraitEvidences<'tcx> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + ty::util::try_fold_list(self, folder, |tcx, values| tcx.mk_trait_evidences(values)) + } + + fn fold_with>>(self, folder: &mut F) -> Self { + ty::util::fold_list(self, folder, |tcx, values| tcx.mk_trait_evidences(values)) + } +} + +/// Interned payload shared by evidence-aware type and const projections. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, StableHash)] +pub struct EvidenceProjection<'tcx>( + pub(crate) Interned<'tcx, ty::EvidenceProjectionData>>, +); + +impl<'tcx> std::ops::Deref for EvidenceProjection<'tcx> { + type Target = ty::EvidenceProjectionData>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'tcx> TypeFoldable> for EvidenceProjection<'tcx> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_evidence_projection(self) + } + + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_evidence_projection(self) + } +} + +impl<'tcx> TypeVisitable> for EvidenceProjection<'tcx> { + fn visit_with>>(&self, visitor: &mut V) -> V::Result { + visitor.visit_evidence_projection(*self) + } +} // Interning CanonicalInput drastically reduces max memory usage when compiling a crate that has // trait solver recursion depth overflows with next-solver deduplicating individual inputs. diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 537015a2560dd..36c69c68d518a 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -8,10 +8,10 @@ use std::hash::Hash; use std::intrinsics; -use std::marker::{DiscriminantKind, PointeeSized}; +use std::marker::{DiscriminantKind, PhantomData, PointeeSized}; use rustc_abi::FieldIdx; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::Const; use rustc_serialize::{Decodable, Encodable}; @@ -20,6 +20,7 @@ use rustc_span::{Span, SpanDecoder, SpanEncoder, Spanned}; use crate::infer::canonical::{CanonicalVarKind, CanonicalVarKinds}; use crate::mir::interpret::{AllocId, ConstAllocation, CtfeProvenance}; use crate::mono::MonoItem; +use crate::traits::solve::{BoundRequiredContract, TraitEvidence, TraitEvidences}; use crate::ty::{self, AdtDef, GenericArgsRef, Ty, TyCtxt}; use crate::{mir, traits}; @@ -37,6 +38,8 @@ pub trait TyEncoder<'tcx>: SpanEncoder { fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize>; + fn trait_evidence_shorthands(&mut self) -> &mut FxHashMap, usize>; + fn encode_alloc_id(&mut self, alloc_id: &AllocId); } @@ -49,6 +52,16 @@ pub trait TyDecoder<'tcx>: where F: FnOnce(&mut Self) -> Ty<'tcx>; + fn cached_trait_evidence_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> TraitEvidence<'tcx> + where + F: FnOnce(&mut Self) -> TraitEvidence<'tcx>; + + fn trait_evidence_in_progress(&mut self) -> &mut FxHashSet; + fn with_position(&mut self, pos: usize, f: F) -> R where F: FnOnce(&mut Self) -> R; @@ -208,6 +221,36 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::ParamEnv<'tcx> { } } +impl<'tcx, E: TyEncoder<'tcx>> Encodable for BoundRequiredContract<'tcx> { + fn encode(&self, e: &mut E) { + self.0.0.encode(e); + } +} + +impl<'tcx, E: TyEncoder<'tcx>> Encodable for TraitEvidence<'tcx> { + fn encode(&self, e: &mut E) { + if let Some(&shorthand) = e.trait_evidence_shorthands().get(self) { + e.emit_usize(shorthand); + return; + } + + self.assert_well_formed(); + let start = e.position(); + // The data starts with a trait ref, so use an explicit inline marker instead + // of relying on the enum discriminant as type shorthands do. + e.emit_usize(0); + self.0.0.encode(e); + e.trait_evidence_shorthands().insert(*self, start + SHORTHAND_OFFSET); + } +} + +impl<'tcx, E: TyEncoder<'tcx>> Encodable for traits::solve::EvidenceProjection<'tcx> { + fn encode(&self, e: &mut E) { + self.evidence.assert_well_formed(); + self.0.0.encode(e); + } +} + impl<'tcx, D: TyDecoder<'tcx>> Decodable for Ty<'tcx> { #[allow(rustc::usage_of_ty_tykind)] fn decode(decoder: &mut D) -> Ty<'tcx> { @@ -284,6 +327,15 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for CanonicalVarKinds<'tcx> { } } +impl<'tcx, D: TyDecoder<'tcx>> Decodable for TraitEvidences<'tcx> { + fn decode(decoder: &mut D) -> Self { + let len = decoder.read_usize(); + decoder.interner().mk_trait_evidences_from_iter( + (0..len).map::, _>(|_| Decodable::decode(decoder)), + ) + } +} + impl<'tcx, D: TyDecoder<'tcx>> Decodable for AllocId { fn decode(decoder: &mut D) -> Self { decoder.decode_alloc_id() @@ -310,6 +362,62 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::ParamEnv<'tcx> { } } +impl<'tcx, D: TyDecoder<'tcx>> Decodable for BoundRequiredContract<'tcx> { + fn decode(d: &mut D) -> Self { + let data: ty::BoundRequiredContractData> = Decodable::decode(d); + d.interner().mk_bound_required_contract(data) + } +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for TraitEvidence<'tcx> { + fn decode(d: &mut D) -> Self { + let start = d.position(); + let marker = d.read_usize(); + if marker != 0 { + assert!(marker >= SHORTHAND_OFFSET); + let shorthand = marker - SHORTHAND_OFFSET; + assert!(shorthand < start, "trait evidence shorthand must refer to earlier data"); + assert!( + !d.trait_evidence_in_progress().contains(&shorthand), + "cycle in trait evidence shorthands" + ); + d.cached_trait_evidence_for_shorthand(shorthand, |d| { + d.with_position(shorthand, Self::decode) + }) + } else { + struct ActiveEvidence<'a, 'tcx, D: TyDecoder<'tcx>> { + decoder: &'a mut D, + position: usize, + marker: PhantomData<&'tcx ()>, + } + impl<'tcx, D: TyDecoder<'tcx>> Drop for ActiveEvidence<'_, 'tcx, D> { + fn drop(&mut self) { + self.decoder.trait_evidence_in_progress().remove(&self.position); + } + } + + assert!( + d.trait_evidence_in_progress().insert(start), + "cycle in trait evidence shorthands" + ); + let guard = ActiveEvidence { decoder: d, position: start, marker: PhantomData }; + let d = &mut *guard.decoder; + let data: traits::solve::TraitEvidenceData> = Decodable::decode(d); + let evidence = d.interner().mk_trait_evidence_data(data); + // Cache inline entries too, so the next edge to a shared proof never + // needs to decode its nested proofs again. + d.cached_trait_evidence_for_shorthand(start, |_| evidence) + } + } +} + +impl<'tcx, D: TyDecoder<'tcx>> Decodable for traits::solve::EvidenceProjection<'tcx> { + fn decode(d: &mut D) -> Self { + let data: ty::EvidenceProjectionData> = Decodable::decode(d); + d.interner().mk_evidence_projection(data) + } +} + macro_rules! impl_decodable_via_ref { ($($t:ty,)+) => { $(impl<'tcx, D: TyDecoder<'tcx>> Decodable for $t { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9242d7101e916..ee92ad767bca8 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -64,8 +64,9 @@ use crate::query::{IntoQueryKey, LocalCrate, Providers, QuerySystem, TyCtxtAt}; use crate::thir::Thir; use crate::traits; use crate::traits::solve::{ - CanonicalInput, CanonicalInputData, ExternalConstraints, ExternalConstraintsData, - PredefinedOpaques, + BoundRequiredContract, CandidateEvidence, CanonicalInput, CanonicalInputData, + EvidenceProjection, ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, + TraitEvidence, TraitEvidenceData, TraitEvidenceKind, TraitEvidences, }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ @@ -165,6 +166,10 @@ pub struct CtxtInterners<'tcx> { patterns: InternedSet<'tcx, List>>, outlives: InternedSet<'tcx, List>>, canonical_inputs: InternedSet<'tcx, CanonicalInputData>>, + bound_required_contract: InternedSet<'tcx, ty::BoundRequiredContractData>>, + trait_evidence: InternedSet<'tcx, TraitEvidenceData>>, + trait_evidences: InternedSet<'tcx, List>>, + evidence_projection: InternedSet<'tcx, ty::EvidenceProjectionData>>, } impl<'tcx> CtxtInterners<'tcx> { @@ -204,6 +209,10 @@ impl<'tcx> CtxtInterners<'tcx> { patterns: InternedSet::with_capacity(N), outlives: InternedSet::with_capacity(N), canonical_inputs: InternedSet::with_capacity(N), + bound_required_contract: InternedSet::with_capacity(N / 2), + trait_evidence: InternedSet::with_capacity(N), + trait_evidences: InternedSet::with_capacity(N), + evidence_projection: InternedSet::with_capacity(N), } } @@ -667,6 +676,7 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { pub struct GlobalCaches<'tcx> { // Internal caches for metadata decoding. No need to track deps on this. pub ty_rcache: Lock>>, + pub trait_evidence_rcache: Lock>>, /// Caches the results of trait selection. This cache is used /// for things that do not have to do with the parameters in scope. @@ -1718,6 +1728,9 @@ nop_lift! { predicate; Predicate<'a> => Predicate<'tcx> } nop_lift! { predicate; Clause<'a> => Clause<'tcx> } nop_lift! { layout; Layout<'a> => Layout<'tcx> } nop_lift! { valtree; ValTree<'a> => ValTree<'tcx> } +nop_lift! { bound_required_contract; BoundRequiredContract<'a> => BoundRequiredContract<'tcx> } +nop_lift! { trait_evidence; TraitEvidence<'a> => TraitEvidence<'tcx> } +nop_lift! { evidence_projection; EvidenceProjection<'a> => EvidenceProjection<'tcx> } impl<'a, 'tcx> Lift> for Interned<'a, RegionKind<'a>> { type Lifted = Interned<'tcx, RegionKind<'tcx>>; @@ -1732,6 +1745,7 @@ impl<'a, 'tcx> Lift> for Interned<'a, RegionKind<'a>> { } nop_list_lift! { type_lists; Ty<'a> => Ty<'tcx> } +nop_list_lift! { trait_evidences; TraitEvidence<'a> => TraitEvidence<'tcx> } nop_list_lift! { clauses: ListWithCachedTypeInfo; Clause<'a> => Clause<'tcx> } nop_list_lift! { poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx> @@ -1995,6 +2009,69 @@ direct_interners! { external_constraints: pub mk_external_constraints(ExternalConstraintsData>): ExternalConstraints -> ExternalConstraints<'tcx>, canonical_inputs: intern_canonical_input(CanonicalInputData>): CanonicalInput -> CanonicalInput<'tcx>, + bound_required_contract: pub(crate) intern_bound_required_contract( + ty::BoundRequiredContractData> + ): + BoundRequiredContract -> BoundRequiredContract<'tcx>, + trait_evidence: pub(crate) intern_trait_evidence(TraitEvidenceData>): + TraitEvidence -> TraitEvidence<'tcx>, + evidence_projection: pub(crate) intern_evidence_projection( + ty::EvidenceProjectionData> + ): + EvidenceProjection -> EvidenceProjection<'tcx>, +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn mk_bound_required_contract( + self, + data: ty::BoundRequiredContractData>, + ) -> BoundRequiredContract<'tcx> { + data.assert_well_formed(); + self.intern_bound_required_contract(data) + } + + /// Interns a selected proof recipe as a trait evidence value. + pub fn mk_trait_evidence(self, recipe: CandidateEvidence>) -> TraitEvidence<'tcx> { + self.mk_trait_evidence_data(TraitEvidenceData::selected(recipe)) + } + + /// Interns a trait evidence value with the supplied state for `trait_ref`. + /// The state may be a selected proof, a bound value, a placeholder, or an error marker. + pub fn mk_trait_evidence_kind( + self, + trait_ref: ty::TraitRef<'tcx>, + kind: TraitEvidenceKind>, + ) -> TraitEvidence<'tcx> { + self.mk_trait_evidence_data(TraitEvidenceData { trait_ref, kind }) + } + + /// Interns a complete first-class trait evidence value. Selected proof + /// recipes and their nested nodes are validated before interning. + pub fn mk_trait_evidence_data( + self, + data: TraitEvidenceData>, + ) -> TraitEvidence<'tcx> { + data.assert_well_formed(); + self.intern_trait_evidence(data) + } + + /// Interns an associated projection after checking that its evidence + /// predicate belongs to the trait which owns the associated item. + pub fn mk_evidence_projection( + self, + data: ty::EvidenceProjectionData>, + ) -> EvidenceProjection<'tcx> { + data.evidence.assert_well_formed(); + let item_def_id = data.item_def_id; + let evidence_trait_def_id = data.trait_ref().def_id; + assert_eq!( + self.trait_of_assoc(item_def_id), + Some(evidence_trait_def_id), + "evidence projection item {item_def_id:?} is not owned by evidence trait \ + {evidence_trait_def_id:?}" + ); + self.intern_evidence_projection(data) + } } macro_rules! slice_interners { @@ -2021,6 +2098,7 @@ slice_interners!( args: pub mk_args(GenericArg<'tcx>), type_lists: pub mk_type_list(Ty<'tcx>), canonical_var_kinds: pub mk_canonical_var_kinds(CanonicalVarKind<'tcx>), + trait_evidences: pub mk_trait_evidences(TraitEvidence<'tcx>), poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>), projs: pub mk_projs(ProjectionKind), place_elems: pub mk_place_elems(PlaceElem<'tcx>), @@ -2139,6 +2217,13 @@ impl<'tcx> TyCtxt<'tcx> { args: &'tcx [ty::GenericArg<'tcx>], ) -> bool { let (def_id, is_self_args) = match kind { + ty::AliasTermKind::EvidenceProjectionTy { projection } + | ty::AliasTermKind::EvidenceProjectionConst { projection } => { + let full_args = self.mk_args_from_iter( + projection.trait_ref().args.iter().chain(args.iter().copied()), + ); + return self.check_args_compatible_inner(projection.item_def_id, full_args, false); + } ty::AliasTermKind::ProjectionTy { def_id } | ty::AliasTermKind::OpaqueTy { def_id } | ty::AliasTermKind::FreeTy { def_id } @@ -2220,9 +2305,28 @@ impl<'tcx> TyCtxt<'tcx> { args: ty::GenericArgsRef<'tcx>, ) { if cfg!(debug_assertions) { + let source_kind = match kind { + ty::AliasTermKind::EvidenceProjectionTy { projection } => Some(( + ty::AliasTermKind::ProjectionTy { def_id: projection.item_def_id }, + projection, + )), + ty::AliasTermKind::EvidenceProjectionConst { projection } => Some(( + ty::AliasTermKind::ProjectionConst { def_id: projection.item_def_id }, + projection, + )), + _ => None, + }; + if let Some((source_kind, projection)) = source_kind { + let full_args = + self.mk_args_from_iter(projection.trait_ref().args.iter().chain(args.iter())); + self.debug_assert_alias_term_args_compatible(source_kind, full_args); + return; + } self.debug_assert_alias_term_kind_matches_def_kind(kind); if !self.check_alias_term_args_compatible(kind, args) { let (def_id, is_self_args) = match kind { + ty::AliasTermKind::EvidenceProjectionTy { .. } + | ty::AliasTermKind::EvidenceProjectionConst { .. } => unreachable!(), ty::AliasTermKind::ProjectionTy { def_id } | ty::AliasTermKind::OpaqueTy { def_id } | ty::AliasTermKind::FreeTy { def_id } @@ -2240,6 +2344,16 @@ impl<'tcx> TyCtxt<'tcx> { fn debug_assert_alias_term_kind_matches_def_kind(self, kind: ty::AliasTermKind<'tcx>) { match kind { + ty::AliasTermKind::EvidenceProjectionTy { projection } => { + self.debug_assert_alias_term_kind_matches_def_kind( + ty::AliasTermKind::ProjectionTy { def_id: projection.item_def_id }, + ); + } + ty::AliasTermKind::EvidenceProjectionConst { projection } => { + self.debug_assert_alias_term_kind_matches_def_kind( + ty::AliasTermKind::ProjectionConst { def_id: projection.item_def_id }, + ); + } ty::AliasTermKind::ProjectionTy { def_id } => { debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy); debug_assert_matches!( @@ -2558,6 +2672,14 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(iter, |xs| self.mk_canonical_var_kinds(xs)) } + pub fn mk_trait_evidences_from_iter(self, iter: I) -> T::Output + where + I: Iterator, + T: CollectAndApply, TraitEvidences<'tcx>>, + { + T::collect_and_apply(iter, |xs| self.mk_trait_evidences(xs)) + } + pub fn mk_place_elems_from_iter(self, iter: I) -> T::Output where I: Iterator, diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 58fbbf8378a8a..00e389629c167 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -20,7 +20,8 @@ use crate::dep_graph::{DepKind, DepNodeIndex}; use crate::infer::canonical::CanonicalVarKinds; use crate::traits::cache::WithDepNode; use crate::traits::solve::{ - self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, QueryResult, inspect, + self, BoundRequiredContract, CanonicalInput, EvidenceProjection, ExternalConstraints, + ExternalConstraintsData, QueryResult, TraitEvidence, TraitEvidences, inspect, }; use crate::ty::{ self, BoundRegion, Clause, Const, List, ParamTy, Pattern, PolyExistentialPredicate, Predicate, @@ -83,7 +84,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> Self::CanonicalVarKinds { self.mk_canonical_var_kinds(kinds) } - type ExternalConstraints = ExternalConstraints<'tcx>; fn mk_external_constraints( self, @@ -91,6 +91,38 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> ExternalConstraints<'tcx> { self.mk_external_constraints(data) } + type BoundRequiredContract = BoundRequiredContract<'tcx>; + fn mk_bound_required_contract( + self, + data: rustc_type_ir::BoundRequiredContractData, + ) -> Self::BoundRequiredContract { + self.mk_bound_required_contract(data) + } + type TraitEvidence = TraitEvidence<'tcx>; + fn mk_trait_evidence_data( + self, + data: rustc_type_ir::solve::TraitEvidenceData, + ) -> Self::TraitEvidence { + self.mk_trait_evidence_data(data) + } + type TraitEvidences = TraitEvidences<'tcx>; + fn mk_trait_evidences(self, values: &[Self::TraitEvidence]) -> Self::TraitEvidences { + self.mk_trait_evidences(values) + } + fn mk_trait_evidences_from_iter(self, values: I) -> T::Output + where + I: Iterator, + T: CollectAndApply, + { + self.mk_trait_evidences_from_iter(values) + } + type EvidenceProjection = EvidenceProjection<'tcx>; + fn mk_evidence_projection( + self, + data: rustc_type_ir::EvidenceProjectionData, + ) -> Self::EvidenceProjection { + self.mk_evidence_projection(data) + } type DepNodeIndex = DepNodeIndex; fn with_cached_task(self, task: impl FnOnce() -> T) -> (T, DepNodeIndex) { self.dep_graph.with_anon_task(self, DepKind::TraitSelect, task) @@ -188,6 +220,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { } fn is_direct_const(self, alias: ty::AliasConstKind<'tcx>) -> bool { match alias { + ty::AliasConstKind::EvidenceProjection { projection } => { + self.is_direct_const(projection.item_def_id) + } ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::InherentSelf { def_id } | ty::AliasConstKind::InherentImpl { def_id } @@ -200,6 +235,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { alias: ty::AliasConstKind<'tcx>, ) -> Option>> { match alias { + ty::AliasConstKind::EvidenceProjection { projection } => { + self.const_of_item(projection.item_def_id) + } ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::InherentSelf { def_id } | ty::AliasConstKind::InherentImpl { def_id } @@ -297,6 +335,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { (trait_ref, &args[trait_ref.args.len()..]) } + fn generic_args_slice(self, args: ty::GenericArgsRef<'tcx>) -> &'tcx [ty::GenericArg<'tcx>] { + args + } + fn mk_args(self, args: &[Self::GenericArg]) -> ty::GenericArgsRef<'tcx> { self.mk_args(args) } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index fb4e30b161d44..d666ddf3cc8c2 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -163,9 +163,14 @@ impl<'tcx> Ty<'tcx> { ty::Infer(ty::FreshTy(_)) => "fresh type".into(), ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), - ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. }, .. }) => { - "associated type".into() - } + ty::Alias( + _, + ty::AliasTy { + kind: + ty::Projection { .. } | ty::EvidenceProjection { .. } | ty::Inherent { .. }, + .. + }, + ) => "associated type".into(), ty::Param(p) => format!("type parameter `{p}`").into(), ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => { if tcx.ty_is_opaque_future(self) { "future".into() } else { "opaque type".into() } @@ -222,9 +227,14 @@ impl<'tcx> Ty<'tcx> { ty::Tuple(..) => "tuple".into(), ty::Placeholder(..) => "higher-ranked type".into(), ty::Bound(..) => "bound type variable".into(), - ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. } | ty::Inherent { .. }, .. }) => { - "associated type".into() - } + ty::Alias( + _, + ty::AliasTy { + kind: + ty::Projection { .. } | ty::EvidenceProjection { .. } | ty::Inherent { .. }, + .. + }, + ) => "associated type".into(), ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) => "type alias".into(), ty::Param(_) => "type parameter".into(), ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => "opaque type".into(), @@ -336,6 +346,10 @@ impl<'tcx> TyCtxt<'tcx> { | ty::AliasTermKind::FreeConst { def_id } | ty::AliasTermKind::InherentConstSelf { def_id } | ty::AliasTermKind::InherentConstImpl { def_id } => self.def_path_str(def_id), + ty::AliasTermKind::EvidenceProjectionTy { projection } + | ty::AliasTermKind::EvidenceProjectionConst { projection } => { + self.def_path_str(projection.item_def_id) + } } } } diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index c19cda4f3edea..c5bd4f5a50f55 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -3,6 +3,7 @@ use rustc_hir::def_id::DefId; use rustc_type_ir::PredicateProxy; use rustc_type_ir::data_structures::DelayedMap; +use crate::traits::solve::TraitEvidence; use crate::ty::{ self, Binder, BoundTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, @@ -62,6 +63,21 @@ pub trait BoundVarReplacerDelegate<'tcx> { fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx>; fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx>; fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx>; + + /// Replaces evidence bound by the binder currently being instantiated. + /// + /// `trait_ref` has already had its ordinary bound variables replaced and + /// is expressed at the replacer's current binder depth. The returned + /// evidence must be expressed at that same depth and prove exactly this + /// trait ref. Returning `Bound(INNERMOST, ..)` is also accepted: the + /// replacer adjusts that bound-evidence index to its current depth. + fn replace_evidence( + &mut self, + trait_ref: ty::TraitRef<'tcx>, + bound: ty::BoundEvidence<'tcx>, + ) -> TraitEvidence<'tcx> { + panic!("bound evidence requires an explicit replacement: {bound:?} proving {trait_ref:?}") + } } /// A simple delegate taking 3 mutable functions. The used functions must @@ -181,6 +197,42 @@ where } } + fn fold_trait_evidence(&mut self, evidence: TraitEvidence<'tcx>) -> TraitEvidence<'tcx> { + match &evidence.kind { + ty::solve::TraitEvidenceKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound) + if *debruijn == self.current_index => + { + let trait_ref = evidence.trait_ref.fold_with(self); + let replacement = self.delegate.replace_evidence(trait_ref, *bound); + let replacement = match &replacement.kind { + ty::solve::TraitEvidenceKind::Bound( + ty::BoundVarIndexKind::Bound(debruijn), + replacement_bound, + ) if *debruijn == ty::INNERMOST && self.current_index != ty::INNERMOST => { + self.tcx.mk_trait_evidence_kind( + replacement.trait_ref, + ty::solve::TraitEvidenceKind::Bound( + ty::BoundVarIndexKind::Bound(self.current_index), + *replacement_bound, + ), + ) + } + _ => replacement, + }; + replacement.assert_well_formed(); + assert_eq!( + replacement.trait_ref, trait_ref, + "bound evidence replacement proves a different predicate" + ); + replacement + } + _ if evidence.has_vars_bound_at_or_above(self.current_index) => { + self.tcx.mk_trait_evidence_data((*evidence).clone().fold_with(self)) + } + _ => evidence, + } + } + fn fold_predicate>>(&mut self, p: P) -> P { if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } } @@ -220,6 +272,67 @@ impl<'tcx> TyCtxt<'tcx> { (value, region_map) } + /// Replaces every ordinary region entry in `value` while retaining the + /// clauses owned by its dependent telescope. + /// + /// Unlike [`TyCtxt::instantiate_bound_regions`], this operation retains + /// evidence clauses in a dependent telescope. It only substitutes the + /// ordinary region prefix and returns the instantiated clauses so the caller + /// can add them as assumptions or obligations. + /// + /// With telescope clauses, every ordinary declaration must be a region. + /// Type and const declarations are rejected, including unused declarations. + /// Without telescope clauses, this delegates to `instantiate_bound_regions`, + /// which only visits variables used in the value. + pub fn instantiate_bound_regions_with_telescope_clauses( + self, + value: Binder<'tcx, T>, + mut fld_r: F, + ) -> ( + T, + FxIndexMap, ty::Region<'tcx>>, + Vec>>, + ) + where + F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>, + T: TypeFoldable>, + { + if !value.has_telescope_clauses() { + let (value, region_map) = self.instantiate_bound_regions(value, fld_r); + return (value, region_map, Vec::new()); + } + + let mut region_map = FxIndexMap::default(); + let mut args = Vec::with_capacity(value.ordinary_bound_var_count()); + + for (index, bound_var) in value.ordinary_bound_vars().enumerate() { + let var = ty::BoundVar::from_usize(index); + let arg = match bound_var { + ty::BoundVariableKind::Region(kind) => { + let bound_region = ty::BoundRegion { var, kind }; + let region = + *region_map.entry(bound_region).or_insert_with(|| fld_r(bound_region)); + region.into() + } + ty::BoundVariableKind::Ty(kind) => { + bug!("unexpected bound ty in region-only binder: {kind:?}") + } + ty::BoundVariableKind::Const(kind) => { + bug!("unexpected bound const in region-only binder: {kind:?}") + } + ty::BoundVariableKind::Evidence(_) => { + unreachable!("evidence entries are not ordinary binder variables") + } + }; + args.push(arg); + } + + let args = self.mk_args(&args); + let (value, clauses) = + value.instantiate_with_args_and_binder_assumption_evidence(self, args); + (value, region_map, clauses) + } + pub fn instantiate_bound_regions_uncached( self, value: Binder<'tcx, T>, @@ -229,6 +342,10 @@ impl<'tcx> TyCtxt<'tcx> { F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>, T: TypeFoldable>, { + assert!( + !value.has_telescope_clauses(), + "region-only binder instantiation cannot discharge telescope clauses: {value:?}" + ); let value = value.skip_binder(); if !value.has_escaping_bound_vars() { value @@ -267,6 +384,10 @@ impl<'tcx> TyCtxt<'tcx> { value: Binder<'tcx, T>, delegate: impl BoundVarReplacerDelegate<'tcx>, ) -> T { + assert!( + !value.has_telescope_clauses(), + "replacing bound variables without their telescope clauses: {value:?}" + ); self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) } @@ -286,32 +407,80 @@ impl<'tcx> TyCtxt<'tcx> { }) } + /// Liberates the ordinary late-bound regions while retaining every + /// clause in the binder's dependent telescope. + /// + /// Consumers which can encounter evidence entries must use this instead + /// of `liberate_late_bound_regions`, then explicitly decide whether the + /// returned clauses are assumptions or obligations at that boundary. + pub fn liberate_late_bound_regions_with_telescope_clauses( + self, + all_outlive_scope: DefId, + value: ty::Binder<'tcx, T>, + ) -> (T, Vec>>) + where + T: TypeFoldable>, + { + let (value, _, clauses) = + self.instantiate_bound_regions_with_telescope_clauses(value, |br| { + let kind = ty::LateParamRegionKind::from_bound(br.var, br.kind); + ty::Region::new_late_param(self, all_outlive_scope, kind) + }); + (value, clauses) + } + pub fn shift_bound_var_indices(self, bound_vars: usize, value: T) -> T where T: TypeFoldable>, { - let shift_bv = |bv: ty::BoundVar| bv + bound_vars; + struct ShiftBoundVarIndices<'tcx> { + tcx: TyCtxt<'tcx>, + amount: usize, + } + + impl<'tcx> BoundVarReplacerDelegate<'tcx> for ShiftBoundVarIndices<'tcx> { + fn replace_region(&mut self, r: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> { + ty::Region::new_bound( + self.tcx, + ty::INNERMOST, + ty::BoundRegion { var: r.var + self.amount, kind: r.kind }, + ) + } + + fn replace_ty(&mut self, t: ty::BoundTy<'tcx>) -> Ty<'tcx> { + Ty::new_bound( + self.tcx, + ty::INNERMOST, + ty::BoundTy { var: t.var + self.amount, kind: t.kind }, + ) + } + + fn replace_const(&mut self, c: ty::BoundConst<'tcx>) -> ty::Const<'tcx> { + ty::Const::new_bound( + self.tcx, + ty::INNERMOST, + ty::BoundConst::new(c.var + self.amount), + ) + } + + fn replace_evidence( + &mut self, + trait_ref: ty::TraitRef<'tcx>, + evidence: ty::BoundEvidence<'tcx>, + ) -> TraitEvidence<'tcx> { + self.tcx.mk_trait_evidence_kind( + trait_ref, + ty::solve::TraitEvidenceKind::Bound( + ty::BoundVarIndexKind::Bound(ty::INNERMOST), + ty::BoundEvidence::new(evidence.var + self.amount), + ), + ) + } + } + self.replace_escaping_bound_vars_uncached( value, - FnMutDelegate { - regions: &mut |r: ty::BoundRegion<'tcx>| { - ty::Region::new_bound( - self, - ty::INNERMOST, - ty::BoundRegion { var: shift_bv(r.var), kind: r.kind }, - ) - }, - types: &mut |t: ty::BoundTy<'tcx>| { - Ty::new_bound( - self, - ty::INNERMOST, - ty::BoundTy { var: shift_bv(t.var), kind: t.kind }, - ) - }, - consts: &mut |c| { - ty::Const::new_bound(self, ty::INNERMOST, ty::BoundConst::new(shift_bv(c.var))) - }, - }, + ShiftBoundVarIndices { tcx: self, amount: bound_vars }, ) } @@ -357,15 +526,93 @@ impl<'tcx> TyCtxt<'tcx> { let entry = self.map.entry(bc.var); let index = entry.index(); let var = ty::BoundVar::from_usize(index); - let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const(); + let () = entry.or_insert_with(|| ty::BoundVariableKind::Const(None)).expect_const(); ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst::new(var)) } + + fn replace_evidence( + &mut self, + trait_ref: ty::TraitRef<'tcx>, + bound: ty::BoundEvidence<'tcx>, + ) -> TraitEvidence<'tcx> { + let entry = self.map.entry(bound.var); + let index = entry.index(); + let var = ty::BoundVar::from_usize(index); + let clause = ty::ClauseKind::Trait(ty::TraitClause { + trait_ref, + polarity: ty::ClausePolarity::Positive, + }); + let _ = entry + .or_insert_with(|| { + ty::BoundVariableKind::Evidence(ty::EvidenceVariable::principal(clause)) + }) + .expect_evidence(); + self.tcx.mk_trait_evidence_kind( + trait_ref, + ty::solve::TraitEvidenceKind::Bound( + ty::BoundVarIndexKind::Bound(ty::INNERMOST), + ty::BoundEvidence::new(var), + ), + ) + } } - let mut map = Default::default(); - let delegate = Anonymize { tcx: self, map: &mut map }; - let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate); - let bound_vars = self.mk_bound_variable_kinds_from_iter(map.into_values()); - Binder::bind_with_vars(inner, bound_vars) + if value.has_telescope_clauses() { + // A dependent telescope's metadata is semantic: typed const + // declarations and evidence assumptions must survive + // anonymization. Keep its ordinary prefix in stable source order, + // pre-seed the replacement map, and fold the value and every entry + // with one substitution. This also preserves the invariant that an + // evidence suffix can only refer to preceding ordinary entries. + let original_bound_vars = value.bound_vars(); + let mut map: FxIndexMap<_, _> = value + .bound_vars() + .iter() + .enumerate() + .map(|(index, entry)| { + let anonymized = match entry { + ty::BoundVariableKind::Ty(_) => { + ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon) + } + ty::BoundVariableKind::Region(_) => { + ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon) + } + ty::BoundVariableKind::Const(_) => ty::BoundVariableKind::Const(None), + // Pre-seeding the complete suffix preserves its stable + // telescope indices even when an evidence variable is + // unused by the bound value. + ty::BoundVariableKind::Evidence(evidence) => { + ty::BoundVariableKind::Evidence(evidence) + } + }; + (ty::BoundVar::from_usize(index), anonymized) + }) + .collect(); + let delegate = Anonymize { tcx: self, map: &mut map }; + let (inner, bound_vars) = self.replace_escaping_bound_vars_uncached( + (value.skip_binder(), original_bound_vars.to_vec()), + delegate, + ); + let bound_vars = + self.mk_bound_variable_kinds_from_iter(bound_vars.into_iter().map(|entry| { + match entry { + ty::BoundVariableKind::Ty(_) => { + ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon) + } + ty::BoundVariableKind::Region(_) => { + ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon) + } + entry @ (ty::BoundVariableKind::Const(_) + | ty::BoundVariableKind::Evidence(_)) => entry, + } + })); + Binder::bind_with_vars(inner, bound_vars) + } else { + let mut map = Default::default(); + let delegate = Anonymize { tcx: self, map: &mut map }; + let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate); + let bound_vars = self.mk_bound_variable_kinds_from_iter(map.into_values()); + Binder::bind_with_vars(inner, bound_vars) + } } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index a7a64fe7cb964..77b77ebd24037 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -95,12 +95,12 @@ pub use self::region::{ EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, RegionKind, RegionVid, }; pub use self::sty::{ - Alias, AliasTy, AliasTyKind, Article, Binder, BoundConst, BoundRegion, BoundRegionKind, - BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig, CoroutineArgsExt, EarlyBinder, - FnSig, FnSigKind, FreeAliasTy, InherentAliasTy, InlineConstArgs, InlineConstArgsParts, - OpaqueAliasTy, ParamConst, ParamTy, PlaceholderConst, PlaceholderRegion, PlaceholderType, - PolyFnSig, ProjectionAliasTy, TyKind, TypeAndMut, TypingMode, TypingModeEqWrapper, - Unnormalized, UpvarArgs, + Alias, AliasTy, AliasTyKind, Article, Binder, BoundConst, BoundEvidence, BoundRegion, + BoundRegionKind, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig, CoroutineArgsExt, + EarlyBinder, FnSig, FnSigKind, FreeAliasTy, InherentAliasTy, InlineConstArgs, + InlineConstArgsParts, OpaqueAliasTy, ParamConst, ParamTy, PlaceholderConst, + PlaceholderEvidence, PlaceholderRegion, PlaceholderType, PolyFnSig, ProjectionAliasTy, TyKind, + TypeAndMut, TypingMode, TypingModeEqWrapper, Unnormalized, UpvarArgs, }; pub use self::trait_def::TraitDef; pub use self::typeck_results::{ diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 3ccf185405209..6fe3129f10b05 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -5,6 +5,7 @@ use rustc_hir::def_id::DefId; use rustc_macros::{StableHash, extension}; use rustc_type_ir as ir; +use crate::traits::solve::TraitEvidence; use crate::ty::{self, EarlyBinder, Ty, TyCtxt, TypeFlags, Upcast, UpcastFrom, WithCachedTypeInfo}; pub type TraitRef<'tcx> = ir::TraitRef>; @@ -422,16 +423,96 @@ impl<'tcx> Clause<'tcx> { let bound_pred = self.kind(); let pred_bound_vars = bound_pred.bound_vars(); let trait_bound_vars = trait_ref.bound_vars(); - // 1) Self: Bar1<'a, '^0.0> -> Self: Bar1<'a, '^0.1> - let shifted_pred = - tcx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder()); - // 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1> - let new = EarlyBinder::bind(tcx, shifted_pred) - .instantiate(tcx, trait_ref.skip_binder().args) + let trait_ordinary_count = trait_ref.ordinary_bound_var_count(); + let pred_ordinary_count = bound_pred.ordinary_bound_var_count(); + + // Both telescopes must retain an ordinary prefix followed by evidence. + // Their merged order is [trait ordinary, predicate ordinary, trait evidence, + // predicate evidence]. All four groups use indices in this complete list; + // only GenericArgs omits evidence slots. + struct Reindex<'tcx> { + tcx: TyCtxt<'tcx>, + ordinary_offset: usize, + evidence_offset: usize, + } + + impl<'tcx> ty::BoundVarReplacerDelegate<'tcx> for Reindex<'tcx> { + fn replace_region(&mut self, region: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> { + ty::Region::new_bound( + self.tcx, + ty::INNERMOST, + ty::BoundRegion { var: region.var + self.ordinary_offset, kind: region.kind }, + ) + } + + fn replace_ty(&mut self, ty: ty::BoundTy<'tcx>) -> Ty<'tcx> { + Ty::new_bound( + self.tcx, + ty::INNERMOST, + ty::BoundTy { var: ty.var + self.ordinary_offset, kind: ty.kind }, + ) + } + + fn replace_const(&mut self, ct: ty::BoundConst<'tcx>) -> ty::Const<'tcx> { + ty::Const::new_bound( + self.tcx, + ty::INNERMOST, + ty::BoundConst::new(ct.var + self.ordinary_offset), + ) + } + + fn replace_evidence( + &mut self, + trait_ref: ty::TraitRef<'tcx>, + evidence: ty::BoundEvidence<'tcx>, + ) -> TraitEvidence<'tcx> { + self.tcx.mk_trait_evidence_kind( + trait_ref, + ty::solve::TraitEvidenceKind::Bound( + ty::BoundVarIndexKind::Bound(ty::INNERMOST), + ty::BoundEvidence::new(evidence.var + self.evidence_offset), + ), + ) + } + } + + let trait_value = (trait_ref.skip_binder().args, trait_bound_vars.to_vec()); + let pred_value = (bound_pred.skip_binder(), pred_bound_vars.to_vec()); + let ((trait_args, trait_bound_vars), (shifted_pred, pred_bound_vars)) = + if trait_ref.has_evidence_bound_vars() { + ( + tcx.replace_escaping_bound_vars_uncached( + trait_value, + Reindex { tcx, ordinary_offset: 0, evidence_offset: pred_ordinary_count }, + ), + tcx.replace_escaping_bound_vars_uncached( + pred_value, + Reindex { + tcx, + ordinary_offset: trait_ordinary_count, + evidence_offset: trait_bound_vars.len(), + }, + ), + ) + } else { + (trait_value, tcx.shift_bound_var_indices(trait_ordinary_count, pred_value)) + }; + + // Substitute early parameters after both values and declaration metadata use + // the merged indices. Trait arguments may themselves refer to trait evidence. + let (new, pred_bound_vars) = EarlyBinder::bind(tcx, (shifted_pred, pred_bound_vars)) + .instantiate(tcx, trait_args) .skip_norm_wip(); - // 3) ['x] + ['b] -> ['x, 'b] - let bound_vars = - tcx.mk_bound_variable_kinds_from_iter(trait_bound_vars.iter().chain(pred_bound_vars)); + let (trait_ordinary, trait_evidence) = trait_bound_vars.split_at(trait_ordinary_count); + let (pred_ordinary, pred_evidence) = pred_bound_vars.split_at(pred_ordinary_count); + let bound_vars = tcx.mk_bound_variable_kinds_from_iter( + trait_ordinary + .iter() + .chain(pred_ordinary) + .chain(trait_evidence) + .chain(pred_evidence) + .copied(), + ); // FIXME: Is it really perf sensitive to use reuse_or_mk_predicate here? tcx.reuse_or_mk_predicate( diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 9df7bc38ce721..154ea41b5bc92 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -861,7 +861,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { ty::Alias( _, ref data @ ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, + kind: + ty::Projection { .. } + | ty::EvidenceProjection { .. } + | ty::Inherent { .. } + | ty::Free { .. }, .. }, ) => data.print(self)?, @@ -1543,6 +1547,17 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { | ty::AliasConstKind::Free { def_id } => { self.pretty_print_value_path(def_id, args)?; } + ty::AliasConstKind::EvidenceProjection { projection } => { + let alias = ty::AliasConst::new( + self.tcx(), + ty::AliasConstKind::EvidenceProjection { projection }, + args, + ); + self.pretty_print_value_path( + projection.item_def_id, + alias.full_args(self.tcx()), + )?; + } ty::AliasConstKind::Anon { def_id } => { if def_id.is_local() && let span = self.tcx().def_span(def_id) @@ -3184,6 +3199,17 @@ define_print! { p.print_def_path(def_id, self.args)?; } } + ty::AliasTermKind::EvidenceProjectionTy { projection } => { + let args = self.full_args(p.tcx()); + let def_id = projection.item_def_id; + if !(p.should_print_verbose() || with_reduced_queries()) + && p.tcx().is_impl_trait_in_trait(def_id) + { + p.pretty_print_rpitit(def_id, args)?; + } else { + p.print_def_path(def_id, args)?; + } + } ty::AliasTermKind::FreeTy { def_id } | ty::AliasTermKind::FreeConst { def_id } | ty::AliasTermKind::OpaqueTy { def_id } @@ -3192,6 +3218,9 @@ define_print! { | ty::AliasTermKind::InherentConstImpl { def_id } => { p.print_def_path(def_id, self.args)?; } + ty::AliasTermKind::EvidenceProjectionConst { projection } => { + p.print_def_path(projection.item_def_id, self.full_args(p.tcx()))?; + } } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index f2c70ffd37ef3..d853d4d166e74 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -54,8 +54,10 @@ pub type Placeholder<'tcx, T> = ir::Placeholder, T>; pub type PlaceholderRegion<'tcx> = ir::PlaceholderRegion>; pub type PlaceholderType<'tcx> = ir::PlaceholderType>; pub type PlaceholderConst<'tcx> = ir::PlaceholderConst>; +pub type PlaceholderEvidence<'tcx> = ir::PlaceholderEvidence>; pub type BoundTy<'tcx> = ir::BoundTy>; pub type BoundConst<'tcx> = ir::BoundConst>; +pub type BoundEvidence<'tcx> = ir::BoundEvidence>; pub type BoundRegion<'tcx> = ir::BoundRegion>; pub type BoundVariableKind<'tcx> = ir::BoundVariableKind>; pub type BoundRegionKind<'tcx> = ir::BoundRegionKind>; diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 4c80bbfa2cbcf..8ec51c164d18b 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -961,6 +961,14 @@ impl<'tcx> TyCtxt<'tcx> { None } } + ty::AliasTermKind::EvidenceProjectionTy { projection } => { + let def_id = projection.item_def_id; + if self.is_impl_trait_in_trait(def_id) { + Some(self.variances_of(def_id)) + } else { + None + } + } ty::AliasTermKind::OpaqueTy { def_id } => Some(self.variances_of(def_id)), ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::InherentConstSelf { .. } @@ -968,7 +976,8 @@ impl<'tcx> TyCtxt<'tcx> { | ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } | ty::AliasTermKind::AnonConst { .. } - | ty::AliasTermKind::ProjectionConst { .. } => None, + | ty::AliasTermKind::ProjectionConst { .. } + | ty::AliasTermKind::EvidenceProjectionConst { .. } => None, } } } diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 620e6da6a8f88..ad591425e2202 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -183,7 +183,10 @@ impl<'tcx> TypeVisitor> for LateBoundRegionsCollector<'tcx> { // inputs to a projection as they may not appear in the normalized form. ty::Alias(_, alias_ty) => { match alias_ty.kind { - ty::Projection { .. } | ty::Inherent { .. } | ty::Opaque { .. } => return, + ty::Projection { .. } + | ty::EvidenceProjection { .. } + | ty::Inherent { .. } + | ty::Opaque { .. } => return, // All free alias types should've been expanded beforehand. ty::Free { .. } => { diff --git a/compiler/rustc_monomorphize/src/offload/manifest.rs b/compiler/rustc_monomorphize/src/offload/manifest.rs index 99ed31f0a408d..b031f04b2056b 100644 --- a/compiler/rustc_monomorphize/src/offload/manifest.rs +++ b/compiler/rustc_monomorphize/src/offload/manifest.rs @@ -6,12 +6,13 @@ use std::fs; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lock; use rustc_hir::def_id::{DefId, DefIndex, LOCAL_CRATE, StableCrateId}; use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mono::MonoItem; +use rustc_middle::traits::solve::TraitEvidence; use rustc_middle::ty::codec::{TyDecoder, TyEncoder}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_serialize::opaque::{FileEncoder, MemDecoder}; @@ -23,6 +24,7 @@ use rustc_span::{ pub(crate) struct OffloadManifestEncoder<'a, 'tcx> { encoder: FileEncoder<'a>, type_shorthands: FxHashMap, usize>, + trait_evidence_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, tcx: TyCtxt<'tcx>, } @@ -33,6 +35,7 @@ impl<'a, 'tcx> OffloadManifestEncoder<'a, 'tcx> { Ok(OffloadManifestEncoder { encoder, type_shorthands: FxHashMap::default(), + trait_evidence_shorthands: FxHashMap::default(), predicate_shorthands: FxHashMap::default(), tcx, }) @@ -146,6 +149,10 @@ impl<'a, 'tcx> TyEncoder<'tcx> for OffloadManifestEncoder<'a, 'tcx> { &mut self.predicate_shorthands } + fn trait_evidence_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.trait_evidence_shorthands + } + fn encode_alloc_id(&mut self, _alloc_id: &rustc_middle::mir::interpret::AllocId) { // AllocIds are not expected in the manifest. } @@ -159,7 +166,9 @@ const UNRESOLVED_DEF_ID: DefId = DefId { /// Decoder used to read the offload monomorphization manifest. pub(crate) struct OffloadManifestDecoder<'a, 'tcx> { decoder: MemDecoder<'a>, + trait_evidence_in_progress: FxHashSet, type_shorthands: Lock>>, + trait_evidence_shorthands: Lock>>, #[allow(dead_code)] predicate_shorthands: Lock>>, tcx: TyCtxt<'tcx>, @@ -173,7 +182,9 @@ impl<'a, 'tcx> OffloadManifestDecoder<'a, 'tcx> { let decoder = MemDecoder::new(data, 0)?; Ok(OffloadManifestDecoder { decoder, + trait_evidence_in_progress: Default::default(), type_shorthands: Lock::new(FxHashMap::default()), + trait_evidence_shorthands: Lock::new(FxHashMap::default()), predicate_shorthands: Lock::new(FxHashMap::default()), tcx, def_path_map: Lock::new(None), @@ -343,6 +354,27 @@ impl<'a, 'tcx> TyDecoder<'tcx> for OffloadManifestDecoder<'a, 'tcx> { ty } + fn cached_trait_evidence_for_shorthand( + &mut self, + shorthand: usize, + or_insert_with: F, + ) -> TraitEvidence<'tcx> + where + F: FnOnce(&mut Self) -> TraitEvidence<'tcx>, + { + if let Some(&evidence) = self.trait_evidence_shorthands.lock().get(&shorthand) { + return evidence; + } + + let evidence = or_insert_with(self); + self.trait_evidence_shorthands.lock().insert(shorthand, evidence); + evidence + } + + fn trait_evidence_in_progress(&mut self) -> &mut FxHashSet { + &mut self.trait_evidence_in_progress + } + fn with_position(&mut self, pos: usize, f: F) -> R where F: FnOnce(&mut Self) -> R, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 485568850bee0..941d9498a1c4c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -853,6 +853,10 @@ where ty::Alias(ty::IsRigid::No, _) => unreachable!("non-rigid self type: {self_ty:?}"), + ty::Alias(ty::IsRigid::Yes, AliasTy { kind: ty::EvidenceProjection { .. }, .. }) => { + unreachable!("evidence projection in alias bound candidate assembly") + } + ty::Alias( ty::IsRigid::Yes, AliasTy { kind: ty::Inherent { .. } | ty::Free { .. }, .. }, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 4f061956765b8..8c91dbaf3ced1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -54,7 +54,12 @@ where | ty::Alias( ty::IsRigid::Yes, ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, .. + kind: + ty::Projection { .. } + | ty::EvidenceProjection { .. } + | ty::Inherent { .. } + | ty::Free { .. }, + .. }, ) | ty::Placeholder(..) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 04d1376d20b9f..1e96065ed46b0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -87,6 +87,9 @@ where let mut candidates = vec![]; let def_id = match alias_ty.kind { + ty::AliasTyKind::EvidenceProjection { .. } => { + unreachable!("evidence projection in alias const conditions") + } ty::AliasTyKind::Projection { def_id } => def_id.into(), ty::AliasTyKind::Inherent { def_id } => def_id.into(), ty::AliasTyKind::Opaque { def_id } => def_id.into(), diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index c5665246710a3..7eec45320b558 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1071,10 +1071,12 @@ where ) -> I::Term { match alias_term.kind { ty::AliasTermKind::ProjectionTy { .. } + | ty::AliasTermKind::EvidenceProjectionTy { .. } | ty::AliasTermKind::InherentTy { .. } | ty::AliasTermKind::OpaqueTy { .. } | ty::AliasTermKind::FreeTy { .. } => self.next_ty_infer().into(), ty::AliasTermKind::FreeConst { .. } + | ty::AliasTermKind::EvidenceProjectionConst { .. } | ty::AliasTermKind::InherentConstSelf { .. } | ty::AliasTermKind::InherentConstImpl { .. } | ty::AliasTermKind::AnonConst { .. } diff --git a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs index db326e6d736a4..3fb7fb71bf481 100644 --- a/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/project_goals/mod.rs @@ -24,6 +24,10 @@ where goal: Goal>, ) -> QueryResultOrRerunNonErased { match goal.predicate.projection_term.kind { + ty::AliasTermKind::EvidenceProjectionTy { .. } + | ty::AliasTermKind::EvidenceProjectionConst { .. } => { + Err(crate::solve::NoSolution.into()) + } ty::AliasTermKind::ProjectionTy { .. } | ty::AliasTermKind::ProjectionConst { .. } => { self.normalize_associated_term(goal) } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 294c887b5062c..e953bc3251e5c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -1334,7 +1334,11 @@ where | ty::Alias( ty::IsRigid::Yes, ty::AliasTy { - kind: ty::Projection { .. } | ty::Free { .. } | ty::Inherent { .. }, + kind: + ty::Projection { .. } + | ty::EvidenceProjection { .. } + | ty::Free { .. } + | ty::Inherent { .. }, .. }, ) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 8076eeb668a2f..b79cd8fd8945c 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -246,7 +246,7 @@ where match kind { ty::Inherent { .. } | ty::Projection { .. } => "associated type", ty::Free { .. } => "type alias", - ty::Opaque { .. } => unreachable!(), + ty::Opaque { .. } | ty::EvidenceProjection { .. } => unreachable!(), }, &LazyDefPathStr { def_id, tcx }, )); @@ -262,6 +262,30 @@ where ) }; } + ty::Alias( + _, + data @ ty::AliasTy { kind: ty::EvidenceProjection { projection }, .. }, + ) => { + if self.def_id_visitor.skip_assoc_tys() { + return V::Result::output(); + } + if !self.visited_tys.insert(ty) { + return V::Result::output(); + } + + let def_id = projection.item_def_id; + try_visit!(self.def_id_visitor.visit_def_id( + def_id, + "associated type", + &LazyDefPathStr { def_id, tcx }, + )); + + return if V::SHALLOW { + V::Result::output() + } else { + self.visit_projection_term(data.into()) + }; + } ty::Dynamic(predicates, ..) => { // All traits in the list are considered the "primary" part of the type // and are visited by shallow visitors. diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index e8a089fd86f60..546cce8f61a5f 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -458,7 +458,7 @@ impl RustcInternal for BoundVariableKind { } BoundRegionKind::BrEnv => rustc_ty::BoundRegionKind::ClosureEnv, }), - BoundVariableKind::Const => rustc_ty::BoundVariableKind::Const, + BoundVariableKind::Const => rustc_ty::BoundVariableKind::Const(None), } } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 17ce015d4ce10..b1d0d41ec0424 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -16,7 +16,9 @@ impl<'tcx> Stable<'tcx> for ty::AliasTyKind<'tcx> { type T = crate::ty::AliasKind; fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { match self { - ty::Projection { .. } => crate::ty::AliasKind::Projection, + ty::Projection { .. } | ty::EvidenceProjection { .. } => { + crate::ty::AliasKind::Projection + } ty::Inherent { .. } => crate::ty::AliasKind::Inherent, ty::Opaque { .. } => crate::ty::AliasKind::Opaque, ty::Free { .. } => crate::ty::AliasKind::Free, @@ -31,15 +33,30 @@ impl<'tcx> Stable<'tcx> for ty::AliasTy<'tcx> { tables: &mut Tables<'cx, BridgeTys>, cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { - let ty::AliasTy { args, kind, .. } = self; + let ty::AliasTy { kind, .. } = self; // rustc_public must change its API once we introduce a variant without a def_id. let def_id = match *kind { ty::AliasTyKind::Projection { def_id } | ty::AliasTyKind::Inherent { def_id } | ty::AliasTyKind::Opaque { def_id } | ty::AliasTyKind::Free { def_id } => def_id, + ty::AliasTyKind::EvidenceProjection { projection } => projection.item_def_id, }; - crate::ty::AliasTy { def_id: tables.alias_def(def_id), args: args.stable(tables, cx) } + crate::ty::AliasTy { + def_id: tables.alias_def(def_id), + args: match *kind { + ty::AliasTyKind::EvidenceProjection { projection } => GenericArgs( + projection + .trait_ref() + .args + .iter() + .chain(self.args.iter()) + .map(|arg| arg.kind().stable(tables, cx)) + .collect(), + ), + _ => self.args.stable(tables, cx), + }, + } } } @@ -50,7 +67,7 @@ impl<'tcx> Stable<'tcx> for ty::AliasTerm<'tcx> { tables: &mut Tables<'cx, BridgeTys>, cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { - let ty::AliasTerm { args, kind, .. } = self; + let ty::AliasTerm { kind, .. } = self; // rustc_public must change its API once we introduce a variant without a def_id. let def_id = match *kind { ty::AliasTermKind::ProjectionTy { def_id } @@ -62,8 +79,25 @@ impl<'tcx> Stable<'tcx> for ty::AliasTerm<'tcx> { | ty::AliasTermKind::FreeConst { def_id } | ty::AliasTermKind::InherentConstSelf { def_id } | ty::AliasTermKind::InherentConstImpl { def_id } => def_id, + ty::AliasTermKind::EvidenceProjectionTy { projection } + | ty::AliasTermKind::EvidenceProjectionConst { projection } => projection.item_def_id, }; - crate::ty::AliasTerm { def_id: tables.alias_def(def_id), args: args.stable(tables, cx) } + crate::ty::AliasTerm { + def_id: tables.alias_def(def_id), + args: match *kind { + ty::AliasTermKind::EvidenceProjectionTy { projection } + | ty::AliasTermKind::EvidenceProjectionConst { projection } => GenericArgs( + projection + .trait_ref() + .args + .iter() + .chain(self.args.iter()) + .map(|arg| arg.kind().stable(tables, cx)) + .collect(), + ), + _ => self.args.stable(tables, cx), + }, + } } } @@ -366,7 +400,10 @@ impl<'tcx> Stable<'tcx> for ty::BoundVariableKind<'tcx> { ty::BoundVariableKind::Region(bound_region_kind) => { BoundVariableKind::Region(bound_region_kind.stable(tables, cx)) } - ty::BoundVariableKind::Const => BoundVariableKind::Const, + ty::BoundVariableKind::Const(None) => BoundVariableKind::Const, + ty::BoundVariableKind::Const(Some(_)) | ty::BoundVariableKind::Evidence(_) => { + bug!("dependent bound variables cannot be converted independently") + } } } } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index a275c68bbdc17..98046fada68c2 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -282,6 +282,9 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }) => { panic!("unexpected inherent projection") } + ty::Alias(_, ty::AliasTy { kind: ty::EvidenceProjection { .. }, .. }) => { + panic!("evidence projection in symbol mangling") + } _ => self.pretty_print_type(ty), } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 5ed41ac456031..77c237643e916 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -748,6 +748,9 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // We may still encounter alias consts due to the printing // logic sometimes passing identity-substituted impl headers. ty::ConstKind::Alias(_, ty::AliasConst { kind, args, .. }) => match kind { + ty::AliasConstKind::EvidenceProjection { .. } => { + bug!("evidence projection in symbol mangling") + } ty::AliasConstKind::Projection { def_id } | ty::AliasConstKind::InherentSelf { def_id } | ty::AliasConstKind::InherentImpl { def_id } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 406349b621958..0357f378cee5d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1608,6 +1608,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ValuePairs::Aliases(ExpectedFound { expected, .. }) => { let def_id = match expected.kind { ty::AliasTermKind::ProjectionTy { def_id } => def_id.into(), + ty::AliasTermKind::EvidenceProjectionTy { projection } + | ty::AliasTermKind::EvidenceProjectionConst { projection } => { + projection.item_def_id + } ty::AliasTermKind::InherentTy { def_id } => def_id.into(), ty::AliasTermKind::OpaqueTy { def_id } => def_id.into(), ty::AliasTermKind::FreeTy { def_id } => def_id.into(), diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 37076b0b655f2..622585d73739b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -734,7 +734,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { GenericKind::Param(_) => format!("the parameter type `{bound_kind}`"), GenericKind::Placeholder(_) => format!("the placeholder type `{bound_kind}`"), GenericKind::Alias(p) => match p.kind { - ty::Projection { .. } | ty::Inherent { .. } => { + ty::Projection { .. } | ty::EvidenceProjection { .. } | ty::Inherent { .. } => { format!("the associated type `{bound_kind}`") } ty::Free { .. } => format!("the type alias `{bound_kind}`"), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 7ee4481d12431..537b45ec06e2b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2011,7 +2011,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::Closure(..) => Some(9), ty::Tuple(..) => Some(10), ty::Param(..) => Some(11), - ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }) => Some(12), + ty::Alias( + _, + ty::AliasTy { + kind: ty::Projection { .. } | ty::EvidenceProjection { .. }, .. + }, + ) => Some(12), ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }) => Some(13), ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => Some(14), ty::Alias(_, ty::AliasTy { kind: ty::Free { .. }, .. }) => Some(15), diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index d52dd8c98d91d..6982ca67ff87d 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -7,12 +7,12 @@ use rustc_infer::traits::{ FromSolverError, Normalized, Obligation, PredicateObligations, TraitEngine, TraitErrors, }; use rustc_macros::extension; -use rustc_middle::span_bug; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::{ self, AliasTerm, PredicateProxy, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypingMode, Unnormalized, }; +use rustc_middle::{bug, span_bug}; use thin_vec::ThinVec; use tracing::{debug, instrument}; @@ -453,6 +453,9 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx } ty::Projection { .. } => self.normalize_trait_projection(data.into()).expect_type(), + ty::EvidenceProjection { .. } => { + bug!("evidence projection in the old trait solver normalizer") + } ty::Inherent { .. } => self.normalize_inherent_projection(data.into()).expect_type(), ty::Free { .. } => self.normalize_free_alias(data.into()).expect_type(), } @@ -486,6 +489,9 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx // if it was marked with `type const`. Using this attribute without the mgca // feature gate causes a parse error. let ct = match alias_const.kind { + ty::AliasConstKind::EvidenceProjection { .. } => { + bug!("evidence projection in the old trait solver normalizer") + } ty::AliasConstKind::Projection { .. } => { self.normalize_trait_projection(alias_const.into()).expect_const() } diff --git a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs index eb89d79474d1c..5fc11cdc47ed0 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs @@ -30,6 +30,9 @@ pub(crate) fn live_args_for_alias_from_outlives_bounds<'tcx>( kind: ty::AliasTyKind<'tcx>, ) -> DenseBitSet { let def_id = match kind { + ty::AliasTyKind::EvidenceProjection { .. } => { + bug!("evidence projection in alias liveness query") + } ty::AliasTyKind::Projection { def_id } | ty::AliasTyKind::Inherent { def_id } | ty::AliasTyKind::Opaque { def_id } @@ -361,6 +364,9 @@ fn live_args_for_outlives_clause<'tcx>( return no_restriction(); }; let clause_def_id = match clause_alias_kind { + ty::AliasTyKind::EvidenceProjection { .. } => { + bug!("evidence projection in alias liveness clause") + } ty::AliasTyKind::Projection { def_id } | ty::AliasTyKind::Inherent { def_id } | ty::AliasTyKind::Opaque { def_id } @@ -525,6 +531,9 @@ where // args independently: only the args that can be live for *every* // source of information can be actually live, so we take the intersection. let def_id = match kind { + ty::AliasTyKind::EvidenceProjection { .. } => { + bug!("evidence projection in alias liveness computation") + } ty::AliasTyKind::Projection { def_id } | ty::AliasTyKind::Inherent { def_id } | ty::AliasTyKind::Opaque { def_id } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 782697782f94c..b4087114e61d3 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -271,6 +271,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { } } + ty::EvidenceProjection { .. } => return Err(NoSolution), kind @ (ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }) => self .try_fold_free_or_assoc(ty::AliasTerm::new(self.cx(), kind.into(), data.args))? .expect_type(), @@ -294,6 +295,7 @@ impl<'a, 'tcx> FallibleTypeFolder> for QueryNormalizer<'a, 'tcx> { }; let constant = match alias_const.kind { + ty::AliasConstKind::EvidenceProjection { .. } => return Err(NoSolution), ty::AliasConstKind::Anon { .. } => crate::traits::with_replaced_escaping_bound_vars( self.infcx, &mut self.universes, @@ -345,6 +347,8 @@ impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> { ty::AliasTermKind::ProjectionTy { .. } | ty::AliasTermKind::ProjectionConst { .. } => { tcx.normalize_canonicalized_projection(c_term) } + ty::AliasTermKind::EvidenceProjectionTy { .. } + | ty::AliasTermKind::EvidenceProjectionConst { .. } => return Err(NoSolution), ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } => { tcx.normalize_canonicalized_free_alias(c_term) } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index bd61bcc7f4d07..ece14e5a51f01 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -791,7 +791,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Alias( _, ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, + kind: + ty::Projection { .. } + | ty::EvidenceProjection { .. } + | ty::Inherent { .. } + | ty::Free { .. }, .. }, ) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index a9ac96424d018..159654d148da7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2309,7 +2309,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Alias( _, ty::AliasTy { - kind: ty::Projection { .. } | ty::Inherent { .. } | ty::Free { .. }, + kind: + ty::Projection { .. } + | ty::EvidenceProjection { .. } + | ty::Inherent { .. } + | ty::Free { .. }, .. }, ) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 9045d9a644ac0..39fbaa787e897 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -839,6 +839,9 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { self.add_wf_preds_for_inherent_projection(data.into()); return; // Subtree handled by compute_inherent_projection. } + ty::Alias(_, ty::AliasTy { kind: ty::EvidenceProjection { .. }, .. }) => { + bug!("evidence projection in well-formedness computation") + } ty::Adt(def, args) => { // WfNominalType @@ -1108,6 +1111,9 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } match alias_const.kind { + ty::AliasConstKind::EvidenceProjection { .. } => { + bug!("evidence projection in well-formedness computation") + } ty::AliasConstKind::InherentSelf { .. } => { self.add_wf_preds_for_inherent_projection(alias_const.into()); return; // Subtree is handled by above function diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index a16610a520406..f1c7854fe6f3b 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; use std::ops::{ControlFlow, Deref}; use derive_where::derive_where; +use rustc_ast_ir::visit::VisitorResult; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash, StableHash_NoContext}; use rustc_type_ir_macros::{ @@ -17,6 +18,7 @@ use crate::inherent::*; use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use crate::{ self as ty, DebruijnIndex, Interner, PredicateProxy, Region, UniverseIndex, Unnormalized, + Upcast, try_visit, }; /// `Binder` is a binder for higher-ranked lifetimes or types. It is part of the @@ -33,6 +35,113 @@ pub struct Binder { bound_vars: I::BoundVarKinds, } +/// A source contract rebased into the scope of one owning telescope. +/// +/// Every member in `clauses` retains its own `Clause` binder. References to +/// the owning telescope therefore occur one de Bruijn level outside that +/// member binder. This is intentionally distinct from +/// [`crate::solve::RequiredContract`]: removing the owning telescope +/// instantiates those outer references. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(GenericTypeVisitable, TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct BoundRequiredContractData { + pub identity: ty::solve::InstantiatedItemContract, + pub clauses: I::Clauses, + /// Index of the trait clause whose evidence owns this atomic bundle. + pub principal_index: u32, + /// Opening substitution, if this bound contract has already crossed an + /// enclosing telescope. Freshly rebased callable contracts use `None`; + /// removing their owning binder records one shared substitution here. + pub ordinary_args: Option, +} + +impl Eq for BoundRequiredContractData {} + +impl BoundRequiredContractData { + pub fn assert_well_formed(&self) { + let principal = self + .clauses + .get(self.principal_index as usize) + .and_then(|clause| clause.as_trait_clause()) + .expect("required-contract principal must be a trait clause"); + assert_eq!( + principal.skip_binder().polarity, + ty::ClausePolarity::Positive, + "required-contract principal must be positive" + ); + } + + fn instantiate(self, cx: I, ordinary_args: I::GenericArgs) -> ty::solve::RequiredContract { + assert!(self.ordinary_args.is_none(), "contract has already been instantiated"); + ty::solve::RequiredContract::new( + cx, + self.identity, + self.clauses, + self.principal_index, + Some(ordinary_args), + ) + } +} + +/// One compiler-internal proof declaration in a dependent binder telescope. +/// +/// `clause` is the principal predicate proved by the evidence slot. When the +/// slot originates from a source trait bound, `required_contract` carries the +/// complete bundle belonging to that exact source bound. Keeping the bundle +/// on the declaration, rather than looking it up from the principal trait ref, +/// lets two slots with identical principals retain different associated-item +/// equalities. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(GenericTypeVisitable, TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct EvidenceVariable { + pub clause: ty::ClauseKind, + pub required_contract: Option, +} + +impl Eq for EvidenceVariable {} + +impl EvidenceVariable { + pub fn principal(clause: ty::ClauseKind) -> Self { + EvidenceVariable { clause, required_contract: None } + } +} + +/// A clause introduced by one entry of a dependent binder telescope. +/// +/// `clause` is instantiated for the current inference context. `identity` +/// keeps the original binder and clause together, so a universally-instantiated +/// clause can retain its stable telescope identity after being appended to a +/// parameter environment. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(GenericTypeVisitable, TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct InstantiatedTelescopeClause { + pub index: u32, + pub clause: I::Clause, + pub identity: I::Clause, + /// The ordinary lifetime/type/const substitution used to instantiate the + /// owning binder. Evidence entries form a telescope suffix and therefore + /// never consume an argument in this list. + pub instantiation: I::GenericArgs, + /// Complete source contract instantiated by the same ordinary/evidence + /// substitution as `clause`. Typed-const declarations and synthetic + /// evidence declarations which have no source bundle use `None`. + pub required_contract: Option>, +} + +impl Eq for InstantiatedTelescopeClause {} + impl Eq for Binder {} impl Binder @@ -55,12 +164,23 @@ where pub fn bind_with_vars(value: T, bound_vars: I::BoundVarKinds) -> Binder { if cfg!(debug_assertions) { let mut validator = ValidateBoundVars::new(bound_vars); + validator.validate_telescope(); let _ = value.visit_with(&mut validator); } Binder { value, bound_vars } } } +fn evidence_clause_trait_ref(clause: ty::ClauseKind) -> ty::TraitRef { + match clause { + ty::ClauseKind::Trait(ty::TraitClause { + trait_ref, + polarity: ty::ClausePolarity::Positive, + }) => trait_ref, + _ => panic!("binder evidence entry must be a positive trait clause, found {clause:?}"), + } +} + impl> TypeFoldable for Binder { fn try_fold_with>(self, folder: &mut F) -> Result { folder.try_fold_binder(self) @@ -82,16 +202,586 @@ impl> TypeSuperFoldable for Binder { self, folder: &mut F, ) -> Result { - self.try_map_bound(|t| t.try_fold_with(folder)) + let Binder { value, bound_vars } = self; + let bound_vars = if bound_vars.iter().any(|entry| { + matches!(entry, BoundVariableKind::Const(Some(_)) | BoundVariableKind::Evidence(_)) + }) { + let entries = bound_vars + .iter() + .map(|entry| entry.try_fold_with(folder)) + .collect::, F::Error>>()?; + I::BoundVarKinds::from_vars(folder.cx(), entries) + } else { + bound_vars + }; + let value = value.try_fold_with(folder)?; + Ok(Binder::bind_with_vars(value, bound_vars)) } fn super_fold_with>(self, folder: &mut F) -> Self { - self.map_bound(|t| t.fold_with(folder)) + let Binder { value, bound_vars } = self; + let bound_vars = if bound_vars.iter().any(|entry| { + matches!(entry, BoundVariableKind::Const(Some(_)) | BoundVariableKind::Evidence(_)) + }) { + I::BoundVarKinds::from_vars( + folder.cx(), + bound_vars.iter().map(|entry| entry.fold_with(folder)), + ) + } else { + bound_vars + }; + Binder::bind_with_vars(value.fold_with(folder), bound_vars) + } +} + +impl> Binder { + /// Instantiates this binder with an explicit substitution for its ordinary + /// lifetime/type/const variables. + /// + /// Binder-owned typed-const and evidence clauses must not be silently + /// discarded. This operation is only valid when the binder has no telescope + /// clauses. Use [`Binder::instantiate_with_args_and_telescope_clauses`] for + /// ordinary dependent declarations, or the evidence-aware counterpart when + /// a value or declaration refers to a bound evidence entry. + pub fn instantiate_with_args(self, cx: I, args: I::GenericArgs) -> T { + assert!( + !self.has_telescope_clauses(), + "instantiating a dependent binder without its telescope clauses: {self:?}" + ); + self.validate_instantiation(cx, args); + self.skip_binder().fold_with(&mut LateBoundArgFolder::new(cx, args)) + } + + /// Instantiates this binder with separate substitutions for its ordinary + /// variables and its evidence suffix. + /// + /// `evidence_args` is dense and ordered like [`Binder::evidence_bound_vars`]. + /// A [`BoundEvidence`] nevertheless stores its index in the complete + /// telescope, so lookup subtracts the ordinary prefix length. Every proof + /// is checked against the fully-instantiated predicate of its declaration + /// before the bound value is folded. + /// + /// Supplying evidence discharges principal trait clauses. Typed const + /// declarations and complete contracts require the counterpart returning + /// telescope clauses and are rejected here. + pub fn instantiate_with_args_and_evidence( + self, + cx: I, + args: I::GenericArgs, + evidence_args: I::TraitEvidences, + ) -> T { + assert!( + !self.bound_vars.iter().any(|entry| { + matches!(entry, BoundVariableKind::Const(Some(_))) + || matches!(entry, BoundVariableKind::Evidence(evidence) + if evidence.required_contract.is_some()) + }), + "instantiating a telescope without its declaration obligations: {self:?}" + ); + self.validate_evidence_instantiation(cx, args, evidence_args); + + let ordinary_count = self.ordinary_bound_var_count(); + self.skip_binder().fold_with(&mut LateBoundArgFolder::with_evidence( + cx, + args, + ordinary_count, + evidence_args, + )) + } + + /// Instantiates the value and every binder-owned declaration using one + /// ordinary substitution and one proof substitution. + /// + /// Unlike [`Binder::instantiate_with_args_and_evidence`], this operation + /// is safe for a telescope containing typed const declarations: those + /// declarations are returned as [`InstantiatedTelescopeClause`]s instead + /// of being silently discharged. Evidence declarations are returned too, + /// preserving the exact binder identity, telescope index, and ordinary + /// substitution which produced each proof argument. + pub fn instantiate_with_args_and_evidence_and_telescope_clauses( + self, + cx: I, + args: I::GenericArgs, + evidence_args: I::TraitEvidences, + ) -> (T, Vec>) { + self.validate_evidence_instantiation(cx, args, evidence_args); + + let ordinary_count = self.ordinary_bound_var_count(); + let telescope_clauses = + self.instantiate_telescope_clauses(cx, args, Some((ordinary_count, evidence_args))); + let value = self.value.fold_with(&mut LateBoundArgFolder::with_evidence( + cx, + args, + ordinary_count, + evidence_args, + )); + (value, telescope_clauses) + } + + /// Universally instantiates this telescope and represents each evidence + /// declaration by the exact parameter-environment origin its clause will + /// receive. Source-contract slots use their instantiated item-contract + /// identity; synthetic slots retain their binder-owned identity. + /// + /// Evidence declarations form a dependent suffix. Recipes are therefore + /// built in telescope order: the predicate for one slot may use proof + /// values from earlier slots, but can never observe itself or a later + /// slot. The returned clauses must still be installed in the universal + /// parameter environment; carrying the same origin in the selected proof + /// prevents normalization from drifting to a different assumption. + pub fn instantiate_with_args_and_binder_assumption_evidence( + self, + cx: I, + args: I::GenericArgs, + ) -> (T, Vec>) { + self.validate_instantiation(cx, args); + + let ordinary_count = self.ordinary_bound_var_count(); + let mut evidence = Vec::with_capacity(self.evidence_bound_vars().count()); + + for (index, entry) in self.bound_vars.iter().enumerate() { + let BoundVariableKind::Evidence(evidence_variable) = entry else { continue }; + let telescope_index = u32::try_from(index).expect("binder telescope index overflow"); + let clause = evidence_variable.clause; + let evidence_prefix = cx.mk_trait_evidences(&evidence); + let mut folder = + LateBoundArgFolder::with_evidence(cx, args, ordinary_count, evidence_prefix); + let trait_ref = evidence_clause_trait_ref(clause).fold_with(&mut folder); + let required_contract = evidence_variable + .required_contract + .map(|contract| (*contract).clone().fold_with(&mut folder).instantiate(cx, args)); + let identity = Binder::bind_with_vars(clause, self.bound_vars).upcast(cx); + let source = ty::solve::CandidateEvidenceSource::ParamEnv { + source: ty::solve::ParamEnvSource::NonGlobal, + origin: required_contract.map_or_else( + || ty::solve::ParamEnvAssumption::Binder { + telescope_index, + identity, + instantiation: args, + }, + |required_contract| ty::solve::ParamEnvAssumption::ItemContract { + contract: required_contract.identity, + }, + ), + }; + evidence.push(cx.mk_trait_evidence(ty::solve::CandidateEvidence::new( + trait_ref, + source, + [], + ))); + } + + let evidence = cx.mk_trait_evidences(&evidence); + self.instantiate_with_args_and_evidence_and_telescope_clauses(cx, args, evidence) + } + + /// Instantiates the trait predicate of the next evidence declaration using + /// the ordinary substitution and the already-created proof prefix. + /// + /// Existential binder consumers use this to allocate one evidence variable + /// at a time. The telescope index must name the entry immediately following + /// `evidence_prefix`; this prevents a caller from observing a declaration + /// before all proofs on which it depends have been created. The prefix is + /// checked against the declarations before the next predicate is returned. + pub fn instantiate_evidence_trait_ref_with_prefix( + &self, + cx: I, + args: I::GenericArgs, + telescope_index: u32, + evidence_prefix: I::TraitEvidences, + ) -> ty::TraitRef { + self.validate_instantiation(cx, args); + + let ordinary_count = self.ordinary_bound_var_count(); + let evidence_entries = self.evidence_bound_vars().collect::>(); + assert!( + evidence_prefix.len() < evidence_entries.len(), + "requested an evidence declaration past the end of the binder telescope" + ); + let (current_index, current_clause) = evidence_entries[evidence_prefix.len()]; + assert_eq!( + current_index, telescope_index, + "evidence declarations must be instantiated in telescope order" + ); + assert_eq!( + usize::try_from(telescope_index).expect("binder telescope index overflow"), + ordinary_count + evidence_prefix.len(), + "evidence entries must be a contiguous binder suffix" + ); + + let mut folder = + LateBoundArgFolder::with_evidence(cx, args, ordinary_count, evidence_prefix); + for ((_, clause), replacement) in + evidence_entries.iter().take(evidence_prefix.len()).zip(evidence_prefix.iter()) + { + let expected = evidence_clause_trait_ref(*clause).fold_with(&mut folder); + replacement.assert_well_formed(); + assert_eq!( + replacement.trait_ref, expected, + "evidence prefix proves the wrong instantiated telescope predicate" + ); + } + + evidence_clause_trait_ref(current_clause).fold_with(&mut folder) + } + + fn validate_evidence_instantiation( + &self, + cx: I, + args: I::GenericArgs, + evidence_args: I::TraitEvidences, + ) { + self.validate_instantiation(cx, args); + + let ordinary_count = self.ordinary_bound_var_count(); + let evidence_entries = self.evidence_bound_vars().collect::>(); + assert_eq!( + evidence_entries.len(), + evidence_args.len(), + "wrong number of evidence arguments for binder instantiation: binder={self:?}, evidence_args={evidence_args:?}" + ); + + let mut folder = LateBoundArgFolder::with_evidence(cx, args, ordinary_count, evidence_args); + for (suffix_index, ((telescope_index, clause), replacement)) in + evidence_entries.into_iter().zip(evidence_args.iter()).enumerate() + { + assert_eq!( + usize::try_from(telescope_index).expect("binder telescope index overflow"), + ordinary_count + suffix_index, + "evidence entries must be a contiguous binder suffix" + ); + let expected = evidence_clause_trait_ref(clause).fold_with(&mut folder); + replacement.assert_well_formed(); + assert_eq!( + replacement.trait_ref, expected, + "evidence argument proves the wrong instantiated predicate at telescope index {telescope_index}" + ); + } + } + + /// Instantiates this binder's value and every binder-owned clause with one + /// explicit ordinary substitution. + /// + /// The returned clauses retain both their stable binder/telescope identity + /// and the exact substitution used at this instantiation site. This is + /// required even when an ordinary parameter is erased from the instantiated + /// trait ref: proof identity must not be reconstructed from the result. + /// References to bound evidence require + /// [`Binder::instantiate_with_args_and_evidence_and_telescope_clauses`]. + pub fn instantiate_with_args_and_telescope_clauses( + self, + cx: I, + args: I::GenericArgs, + ) -> (T, Vec>) { + let telescope_clauses = self.instantiate_telescope_clauses_with_args(cx, args); + let value = self.value.fold_with(&mut LateBoundArgFolder::new(cx, args)); + (value, telescope_clauses) + } + + /// Instantiates only the clauses owned by this binder's telescope. + /// + /// This lets an inference context inspect each instantiated telescope + /// predicate before it allocates corresponding evidence variables and + /// substitutes those proof values into the binder's main value. The method + /// only supplies ordinary arguments; use the evidence-aware method when a + /// value or declaration refers to a bound evidence entry. It is deliberately + /// separate from `skip_binder`: no bound value is exposed or claimed to have + /// been instantiated by this operation. + pub fn instantiate_telescope_clauses_with_args( + &self, + cx: I, + args: I::GenericArgs, + ) -> Vec> { + self.validate_instantiation(cx, args); + self.instantiate_telescope_clauses(cx, args, None) + } + + fn instantiate_telescope_clauses( + &self, + cx: I, + args: I::GenericArgs, + evidence: Option<(usize, I::TraitEvidences)>, + ) -> Vec> { + let bound_vars = self.bound_vars; + let telescope_clauses = bound_vars + .iter() + .enumerate() + .filter_map(|(index, entry)| { + let index = u32::try_from(index).expect("binder telescope index overflow"); + let (clause, required_contract) = match entry { + BoundVariableKind::Const(Some(expected_ty)) => { + let ct = I::Const::new_bound( + cx, + ty::INNERMOST, + ty::BoundConst::new(ty::BoundVar::from_u32(index)), + ); + (ty::ClauseKind::ConstArgHasType(ct, expected_ty), None) + } + BoundVariableKind::Evidence(evidence) => { + (evidence.clause, evidence.required_contract) + } + BoundVariableKind::Ty(_) + | BoundVariableKind::Region(_) + | BoundVariableKind::Const(None) => return None, + }; + let identity = Binder::bind_with_vars(clause, bound_vars).upcast(cx); + Some((index, clause, identity, required_contract)) + }) + .collect::>(); + + let mut folder = LateBoundArgFolder::new(cx, args); + folder.evidence = + evidence.map(|(ordinary_count, args)| LateBoundEvidenceArgs { ordinary_count, args }); + let telescope_clauses = telescope_clauses + .into_iter() + .map(|(index, clause, identity, required_contract)| { + // `identity` names the declaration in its original binder and + // must never be instantiated. In particular, folding it would + // enter that binder and make variables from an enclosing binder + // look like variables owned by the binder being removed here. + let clause = clause.fold_with(&mut folder); + // `I::Clause` is itself binder-shaped. If the instantiated + // clause still references an enclosing binder, moving it under + // this empty clause binder must shift that reference in once. + let clause = ty::shift_vars(cx, clause, 1); + let clause = Binder::bind_with_vars(clause, Default::default()).upcast(cx); + let required_contract = required_contract.map(|contract| { + (*contract).clone().fold_with(&mut folder).instantiate(cx, args) + }); + InstantiatedTelescopeClause { + index, + clause, + identity, + instantiation: args, + required_contract, + } + }) + .collect(); + telescope_clauses + } + + fn validate_instantiation(&self, cx: I, args: I::GenericArgs) { + // Check declarations and uses together before removing their scope. + let mut validator = ValidateBoundVars::new(self.bound_vars); + validator.cx = Some(cx); + validator.validate_telescope(); + let _ = self.value.visit_with(&mut validator); + assert_eq!( + self.ordinary_bound_var_count(), + args.len(), + "wrong number of arguments for binder instantiation: binder={self:?}, args={args:?}" + ); + for (index, (bound_var, arg)) in self.ordinary_bound_vars().zip(args.iter()).enumerate() { + let valid = matches!( + (bound_var, arg.kind()), + (BoundVariableKind::Region(_), ty::GenericArgKind::Lifetime(_)) + | (BoundVariableKind::Ty(_), ty::GenericArgKind::Type(_)) + | (BoundVariableKind::Const(_), ty::GenericArgKind::Const(_)) + ); + assert!( + valid, + "argument kind mismatch at binder index {index}: binder={self:?}, args={args:?}" + ); + } + } +} + +/// Removes one late binder while substituting its ordinary variables. +/// +/// Variables supplied by `args` are shifted into any nested binders traversed +/// while folding. Variables which were bound outside the removed binder are +/// shifted out once. Keeping both adjustments in one folder is what makes an +/// explicit instantiation reusable for a value and all dependent clauses. +struct LateBoundArgFolder { + cx: I, + args: I::GenericArgs, + current_index: DebruijnIndex, + evidence: Option>, +} + +#[derive(Clone, Copy)] +struct LateBoundEvidenceArgs { + ordinary_count: usize, + args: I::TraitEvidences, +} + +impl LateBoundArgFolder { + fn new(cx: I, args: I::GenericArgs) -> Self { + LateBoundArgFolder { cx, args, current_index: ty::INNERMOST, evidence: None } + } + + fn with_evidence( + cx: I, + args: I::GenericArgs, + ordinary_count: usize, + evidence: I::TraitEvidences, + ) -> Self { + LateBoundArgFolder { + cx, + args, + current_index: ty::INNERMOST, + evidence: Some(LateBoundEvidenceArgs { ordinary_count, args: evidence }), + } + } + + fn shifted_arg>(&self, value: T) -> T { + ty::shift_vars(self.cx, value, self.current_index.as_u32()) + } +} + +impl TypeFolder for LateBoundArgFolder { + fn cx(&self) -> I { + self.cx + } + + fn fold_binder>(&mut self, binder: Binder) -> Binder { + self.current_index.shift_in(1); + let binder = binder.super_fold_with(self); + self.current_index.shift_out(1); + binder + } + + fn fold_ty(&mut self, value: I::Ty) -> I::Ty { + match value.kind() { + ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ty) + if debruijn == self.current_index => + { + let arg = self.args.get(bound_ty.var().as_usize()).unwrap_or_else(|| { + panic!("bound type {bound_ty:?} is outside instantiation {:#?}", self.args) + }); + let ty::GenericArgKind::Type(ty) = arg.kind() else { + panic!("expected type argument for {bound_ty:?}, found {arg:?}") + }; + self.shifted_arg(ty) + } + ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ty) + if debruijn > self.current_index => + { + I::Ty::new_bound(self.cx, debruijn.shifted_out(1), bound_ty) + } + _ if value.has_vars_bound_at_or_above(self.current_index) => { + value.super_fold_with(self) + } + _ => value, + } + } + + fn fold_region(&mut self, value: Region) -> Region { + match value.kind() { + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), bound_region) + if debruijn == self.current_index => + { + let arg = self.args.get(bound_region.var().as_usize()).unwrap_or_else(|| { + panic!( + "bound region {bound_region:?} is outside instantiation {:#?}", + self.args + ) + }); + let ty::GenericArgKind::Lifetime(region) = arg.kind() else { + panic!("expected lifetime argument for {bound_region:?}, found {arg:?}") + }; + ty::shift_region(self.cx, region, self.current_index.as_u32()) + } + ty::ReBound(ty::BoundVarIndexKind::Bound(debruijn), bound_region) + if debruijn > self.current_index => + { + Region::new_bound(self.cx, debruijn.shifted_out(1), bound_region) + } + _ => value, + } + } + + fn fold_const(&mut self, value: I::Const) -> I::Const { + match value.kind() { + ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const) + if debruijn == self.current_index => + { + let arg = self.args.get(bound_const.var().as_usize()).unwrap_or_else(|| { + panic!("bound const {bound_const:?} is outside instantiation {:#?}", self.args) + }); + let ty::GenericArgKind::Const(ct) = arg.kind() else { + panic!("expected const argument for {bound_const:?}, found {arg:?}") + }; + self.shifted_arg(ct) + } + ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const) + if debruijn > self.current_index => + { + I::Const::new_bound(self.cx, debruijn.shifted_out(1), bound_const) + } + _ => value.super_fold_with(self), + } + } + + fn fold_trait_evidence(&mut self, evidence: I::TraitEvidence) -> I::TraitEvidence { + match &evidence.kind { + ty::solve::TraitEvidenceKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound) + if *debruijn == self.current_index => + { + let expected_trait_ref = evidence.trait_ref.fold_with(self); + let Some(substitution) = self.evidence else { + panic!( + "instantiating bound evidence without an explicit evidence substitution: {evidence:?}" + ); + }; + let telescope_index = bound.var().as_usize(); + let suffix_index = + telescope_index.checked_sub(substitution.ordinary_count).unwrap_or_else(|| { + panic!( + "bound evidence points into the ordinary binder prefix: {evidence:?}" + ) + }); + let replacement = substitution.args.get(suffix_index).unwrap_or_else(|| { + panic!( + "bound evidence index {telescope_index} is outside the binder evidence suffix" + ) + }); + let replacement = ty::shift_vars(self.cx, replacement, self.current_index.as_u32()); + replacement.assert_well_formed(); + assert_eq!( + replacement.trait_ref, expected_trait_ref, + "bound evidence replacement proves a different instantiated predicate" + ); + replacement + } + ty::solve::TraitEvidenceKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound) + if *debruijn > self.current_index => + { + let trait_ref = evidence.trait_ref.fold_with(self); + self.cx.mk_trait_evidence_kind( + trait_ref, + ty::solve::TraitEvidenceKind::Bound( + ty::BoundVarIndexKind::Bound(debruijn.shifted_out(1)), + *bound, + ), + ) + } + _ => self.cx.mk_trait_evidence_data((*evidence).clone().fold_with(self)), + } + } + + fn fold_predicate>(&mut self, value: P) -> P { + if value.has_vars_bound_at_or_above(self.current_index) { + value.super_fold_with(self) + } else { + value + } + } + + fn fold_clauses(&mut self, value: I::Clauses) -> I::Clauses { + if value.has_vars_bound_at_or_above(self.current_index) { + value.super_fold_with(self) + } else { + value + } } } impl> TypeSuperVisitable for Binder { fn super_visit_with>(&self, visitor: &mut V) -> V::Result { + for entry in self.bound_vars.iter() { + try_visit!(entry.visit_with(visitor)); + } self.as_ref().skip_binder().visit_with(visitor) } } @@ -118,6 +808,54 @@ impl Binder { self.bound_vars } + /// Returns the source-level lifetime/type/const entries in this telescope. + /// + /// Evidence entries are an internal suffix. Consumers which classify user + /// generic parameters or allocate new `BoundVar` indices must use this view + /// instead of treating every telescope entry as an ordinary bound variable. + pub fn ordinary_bound_vars(&self) -> impl Iterator> + '_ { + self.bound_vars.iter().take_while(|entry| !matches!(entry, BoundVariableKind::Evidence(_))) + } + + pub fn ordinary_bound_var_count(&self) -> usize { + self.ordinary_bound_vars().count() + } + + pub fn has_ordinary_bound_vars(&self) -> bool { + self.ordinary_bound_vars().next().is_some() + } + + /// Returns the compiler-internal proof assumptions owned by this binder, + /// together with their stable telescope indices. + /// + /// Evidence entries are required to form a suffix, so exposing them does + /// not change the indices of ordinary lifetime/type/const bound variables. + pub fn evidence_bound_vars(&self) -> impl Iterator)> + '_ { + self.bound_vars.iter().enumerate().filter_map(|(index, entry)| match entry { + BoundVariableKind::Evidence(evidence) => Some(( + u32::try_from(index).expect("binder telescope index overflow"), + evidence.clause, + )), + BoundVariableKind::Ty(_) + | BoundVariableKind::Region(_) + | BoundVariableKind::Const(_) => None, + }) + } + + pub fn has_evidence_bound_vars(&self) -> bool { + self.evidence_bound_vars().next().is_some() + } + + /// Whether instantiating this binder must also instantiate clauses owned + /// by telescope entries. Typed const declarations contribute a + /// `ConstArgHasType` clause and evidence entries contribute their stored + /// predicate. + pub fn has_telescope_clauses(&self) -> bool { + self.bound_vars.iter().any(|entry| { + matches!(entry, BoundVariableKind::Const(Some(_)) | BoundVariableKind::Evidence(_)) + }) + } + pub fn as_ref(&self) -> Binder { Binder { value: &self.value, bound_vars: self.bound_vars } } @@ -144,6 +882,7 @@ impl Binder { let value = f(value); if cfg!(debug_assertions) { let mut validator = ValidateBoundVars::new(bound_vars); + validator.validate_telescope(); let _ = value.visit_with(&mut validator); } Binder { value, bound_vars } @@ -157,6 +896,7 @@ impl Binder { let value = f(value)?; if cfg!(debug_assertions) { let mut validator = ValidateBoundVars::new(bound_vars); + validator.validate_telescope(); let _ = value.visit_with(&mut validator); } Ok(Binder { value, bound_vars }) @@ -181,7 +921,8 @@ impl Binder { /// Unwraps and returns the value within, but only if it contains /// no bound vars at all. (In other words, if this binder -- /// and indeed any enclosing binder -- doesn't bind anything at - /// all.) Otherwise, returns `None`. + /// all.) Dependent declarations must be explicitly instantiated, even if + /// unused by the value. Otherwise, returns `None`. /// /// (One could imagine having a method that just unwraps a single /// binder, but permits late-bound vars bound by enclosing @@ -192,8 +933,13 @@ impl Binder { where T: TypeVisitable, { - // `self.value` is equivalent to `self.skip_binder()` - if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) } + // Dependent declarations must be explicitly instantiated, even if the + // value does not refer to them. Unwrapping here would lose their clauses. + if self.has_telescope_clauses() || self.value.has_escaping_bound_vars() { + None + } else { + Some(self.skip_binder()) + } } } @@ -218,6 +964,10 @@ pub struct ValidateBoundVars { // a type at some point anyways. We may encounter the same variable at // different levels of binding, so this can't just be `Ty`. visited: SsoHashSet<(ty::DebruijnIndex, I::Ty)>, + /// While validating a telescope entry, references at the current + /// binder may only target earlier entries. + entry_limit: Option, + cx: Option, } impl ValidateBoundVars { @@ -226,6 +976,56 @@ impl ValidateBoundVars { bound_vars, binder_index: ty::INNERMOST, visited: SsoHashSet::default(), + entry_limit: None, + cx: None, + } + } + + fn validate_telescope(&mut self) { + let mut saw_evidence = false; + for (index, entry) in self.bound_vars.iter().enumerate() { + match entry { + BoundVariableKind::Evidence(evidence) => { + let _ = evidence_clause_trait_ref(evidence.clause); + if let Some(contract) = evidence.required_contract { + contract.assert_well_formed(); + if let Some(cx) = self.cx { + let principal = contract + .clauses + .get(contract.principal_index as usize) + .expect("required-contract principal is out of bounds"); + let expected = Binder::bind_with_vars( + ty::shift_vars(cx, evidence.clause, 1), + Default::default(), + ); + assert_eq!( + principal.kind(), + expected, + "evidence declaration does not match its contract principal" + ); + } + } + saw_evidence = true; + } + _ if saw_evidence => { + panic!("ordinary binder variable after evidence entry: {:?}", self.bound_vars) + } + _ => {} + } + self.entry_limit = Some(index); + let _ = entry.visit_with(self); + } + self.entry_limit = None; + } + + fn assert_in_entry_scope(&self, index: usize) { + if let Some(limit) = self.entry_limit + && index >= limit + { + panic!( + "binder telescope entry references non-previous variable {index} in {:?}", + self.bound_vars + ); } } } @@ -244,7 +1044,7 @@ impl TypeVisitor for ValidateBoundVars { if t.outer_exclusive_binder() < self.binder_index || !self.visited.insert((self.binder_index, t)) { - return ControlFlow::Break(()); + return ControlFlow::Continue(()); } match t.kind() { ty::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_ty) @@ -254,6 +1054,7 @@ impl TypeVisitor for ValidateBoundVars { if self.bound_vars.len() <= idx { panic!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); } + self.assert_in_entry_scope(idx); bound_ty.assert_eq(self.bound_vars.get(idx).unwrap()); } _ => {} @@ -264,7 +1065,7 @@ impl TypeVisitor for ValidateBoundVars { fn visit_const(&mut self, c: I::Const) -> Self::Result { if c.outer_exclusive_binder() < self.binder_index { - return ControlFlow::Break(()); + return ControlFlow::Continue(()); } match c.kind() { ty::ConstKind::Bound(debruijn, bound_const) @@ -274,6 +1075,7 @@ impl TypeVisitor for ValidateBoundVars { if self.bound_vars.len() <= idx { panic!("Not enough bound vars: {:?} not found in {:?}", c, self.bound_vars); } + self.assert_in_entry_scope(idx); bound_const.assert_eq(self.bound_vars.get(idx).unwrap()); } _ => {} @@ -289,6 +1091,7 @@ impl TypeVisitor for ValidateBoundVars { if self.bound_vars.len() <= idx { panic!("Not enough bound vars: {:?} not found in {:?}", r, self.bound_vars); } + self.assert_in_entry_scope(idx); br.assert_eq(self.bound_vars.get(idx).unwrap()); } @@ -297,6 +1100,43 @@ impl TypeVisitor for ValidateBoundVars { ControlFlow::Continue(()) } + + fn visit_trait_evidence(&mut self, evidence: I::TraitEvidence) -> Self::Result { + if let ty::solve::TraitEvidenceKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound) = + &evidence.kind + && *debruijn == self.binder_index + { + let idx = bound.var().as_usize(); + if self.bound_vars.len() <= idx { + panic!( + "Not enough bound vars: evidence {evidence:?} not found in {:?}", + self.bound_vars + ); + } + self.assert_in_entry_scope(idx); + let clause = bound.assert_eq(self.bound_vars.get(idx).unwrap()); + let expected_trait_ref = evidence_clause_trait_ref(clause); + if let Some(cx) = self.cx { + let expected_trait_ref = + ty::shift_vars(cx, expected_trait_ref, self.binder_index.as_u32()); + assert_eq!( + evidence.trait_ref, expected_trait_ref, + "bound evidence predicate does not match binder telescope entry {idx}" + ); + } else { + // Binder construction has no interner with which to shift the declaration. + // Explicit instantiation also checks the arguments at the current depth. + assert_eq!(evidence.trait_ref.def_id, expected_trait_ref.def_id); + if self.binder_index == ty::INNERMOST + || !expected_trait_ref.has_escaping_bound_vars() + { + assert_eq!(evidence.trait_ref, expected_trait_ref); + } + } + } + + (*evidence).visit_with(self) + } } /// Similar to [`Binder`] except that it tracks early bound generics, i.e. `struct Foo(T)` @@ -1057,7 +1897,7 @@ pub enum BoundTyKind { } #[derive_where(Clone, Copy, PartialEq, Eq, Debug, Hash; I: Interner)] -#[derive(Lift_Generic, GenericTypeVisitable)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) @@ -1065,7 +1905,52 @@ pub enum BoundTyKind { pub enum BoundVariableKind { Ty(BoundTyKind), Region(BoundRegionKind), - Const, + /// A const binder entry. New generalized binders record the binder-scoped + /// const type; `None` is retained as a migration representation for legacy + /// syntax-only binder construction sites. + Const(Option), + /// Compiler-internal proof assumption owned by this binder. Evidence + /// entries form a dependent suffix and may reference only earlier entries + /// of the same telescope. + Evidence(EvidenceVariable), +} + +impl TypeVisitable for BoundVariableKind { + fn visit_with>(&self, visitor: &mut V) -> V::Result { + match self { + BoundVariableKind::Const(Some(ty)) => ty.visit_with(visitor), + BoundVariableKind::Evidence(evidence) => evidence.visit_with(visitor), + BoundVariableKind::Ty(_) + | BoundVariableKind::Region(_) + | BoundVariableKind::Const(None) => V::Result::output(), + } + } +} + +impl TypeFoldable for BoundVariableKind { + fn try_fold_with>(self, folder: &mut F) -> Result { + Ok(match self { + BoundVariableKind::Const(Some(ty)) => { + BoundVariableKind::Const(Some(ty.try_fold_with(folder)?)) + } + BoundVariableKind::Evidence(evidence) => { + BoundVariableKind::Evidence(evidence.try_fold_with(folder)?) + } + _ => self, + }) + } + + fn fold_with>(self, folder: &mut F) -> Self { + match self { + BoundVariableKind::Const(Some(ty)) => { + BoundVariableKind::Const(Some(ty.fold_with(folder))) + } + BoundVariableKind::Evidence(evidence) => { + BoundVariableKind::Evidence(evidence.fold_with(folder)) + } + _ => self, + } + } } impl BoundVariableKind { @@ -1085,20 +1970,40 @@ impl BoundVariableKind { pub fn expect_const(self) { match self { - BoundVariableKind::Const => (), + BoundVariableKind::Const(_) => (), _ => panic!("expected a const, but found another kind"), } } + + pub fn const_ty(self) -> Option { + match self { + BoundVariableKind::Const(ty) => ty, + _ => panic!("expected a const, but found another kind"), + } + } + + pub fn expect_evidence(self) -> ty::ClauseKind { + match self { + BoundVariableKind::Evidence(evidence) => evidence.clause, + _ => panic!("expected evidence, but found another kind"), + } + } + + pub fn expect_evidence_variable(self) -> EvidenceVariable { + match self { + BoundVariableKind::Evidence(evidence) => evidence, + _ => panic!("expected evidence, but found another kind"), + } + } } #[derive_where(Clone, Copy, PartialEq, Eq, Hash; I: Interner)] -#[derive(GenericTypeVisitable, Lift_Generic)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, StableHash_NoContext, Decodable_NoContext) )] pub struct BoundRegion { - #[lift(identity)] pub var: ty::BoundVar, pub kind: BoundRegionKind, } @@ -1210,12 +2115,13 @@ impl PlaceholderType { } #[derive_where(Clone, Copy, PartialEq, Debug, Eq, Hash; I: Interner)] -#[derive(GenericTypeVisitable)] +#[derive(GenericTypeVisitable, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) )] pub struct BoundConst { + #[lift(identity)] pub var: ty::BoundVar, #[derive_where(skip(Debug))] pub _tcx: PhantomData I>, @@ -1293,3 +2199,60 @@ impl PlaceholderConst { ty } } + +/// The binder-local identity of a trait evidence value. +/// +/// Evidence variables share the telescope index space with ordinary bound +/// variables, but occupy a compiler-internal suffix and are not passed through +/// [`GenericArgs`](crate::GenericArgs). Keeping the index in its own type makes +/// it impossible to accidentally treat a proof as a type or const argument. +#[derive_where(Clone, Copy, PartialEq, Debug, Eq, Hash; I: Interner)] +#[derive(GenericTypeVisitable, Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext) +)] +pub struct BoundEvidence { + #[lift(identity)] + pub var: ty::BoundVar, + #[derive_where(skip(Debug))] + pub _tcx: PhantomData I>, +} + +impl BoundEvidence { + pub fn var(self) -> ty::BoundVar { + self.var + } + + pub fn assert_eq(self, var: BoundVariableKind) -> ty::ClauseKind { + var.expect_evidence() + } + + pub fn new(var: ty::BoundVar) -> Self { + Self { var, _tcx: PhantomData } + } +} + +pub type PlaceholderEvidence = ty::Placeholder>; + +impl PlaceholderEvidence { + pub fn universe(self) -> UniverseIndex { + self.universe + } + + pub fn var(self) -> ty::BoundVar { + self.bound.var + } + + pub fn with_updated_universe(self, ui: UniverseIndex) -> Self { + Self { universe: ui, bound: self.bound, _tcx: PhantomData } + } + + pub fn new(ui: UniverseIndex, bound: BoundEvidence) -> Self { + Self { universe: ui, bound, _tcx: PhantomData } + } + + pub fn new_anon(ui: UniverseIndex, var: ty::BoundVar) -> Self { + Self::new(ui, BoundEvidence::new(var)) + } +} diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 36cef1c13eb29..055e83198d69c 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -9,6 +9,7 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; +use crate::inherent::*; use crate::{self as ty, AliasConst, BoundVarIndexKind, Interner}; /// Represents a constant in Rust. @@ -72,10 +73,20 @@ impl fmt::Debug for ConstKind { impl AliasConst { #[inline] pub fn new(interner: I, kind: AliasConstKind, args: I::GenericArgs) -> AliasConst { + let alias = AliasConst { kind, args, _use_alias_new_instead: () }; if cfg!(debug_assertions) { interner.debug_assert_alias_term_args_compatible(kind.into(), args); } - AliasConst { kind, args, _use_alias_new_instead: () } + alias + } + + /// Reconstructs the associated item's full arguments from its proof prefix. + pub fn full_args(self, interner: I) -> I::GenericArgs { + match self.kind { + ty::AliasConstKind::EvidenceProjection { projection } => interner + .mk_args_from_iter(projection.trait_ref().args.iter().chain(self.args.iter())), + _ => self.args, + } } pub fn type_of(self, interner: I) -> ty::Unnormalized { @@ -87,10 +98,11 @@ impl AliasConst { ) } ty::AliasConstKind::InherentImpl { def_id } => def_id.into(), + ty::AliasConstKind::EvidenceProjection { projection } => projection.item_def_id.into(), ty::AliasConstKind::Free { def_id } => def_id.into(), ty::AliasConstKind::Anon { def_id } => def_id.into(), }; - interner.type_of(def_id).instantiate(interner, self.args) + interner.type_of(def_id).instantiate(interner, self.full_args(interner)) } } @@ -106,6 +118,8 @@ impl AliasConst { pub enum AliasConstKind { /// A projection `::AssocConst` Projection { def_id: I::TraitAssocConstId }, + /// An elaborated associated const projection indexed by exact trait evidence. + EvidenceProjection { projection: I::EvidenceProjection }, /// An associated const in an inherent `impl`. /// /// The generic args are in "Self form", i.e. @@ -169,6 +183,9 @@ impl AliasConstKind { AliasConstKind::Projection { def_id } => interner.def_span(def_id.into()), AliasConstKind::InherentSelf { def_id } => interner.def_span(def_id.into()), AliasConstKind::InherentImpl { def_id } => interner.def_span(def_id.into()), + AliasConstKind::EvidenceProjection { projection } => { + interner.def_span(projection.item_def_id.into()) + } AliasConstKind::Free { def_id } => interner.def_span(def_id.into()), AliasConstKind::Anon { def_id } => interner.def_span(def_id.into()), } @@ -179,6 +196,9 @@ impl AliasConstKind { AliasConstKind::Projection { def_id } => Some(def_id.into()), AliasConstKind::InherentSelf { def_id } => Some(def_id.into()), AliasConstKind::InherentImpl { def_id } => Some(def_id.into()), + AliasConstKind::EvidenceProjection { projection } => { + Some(projection.item_def_id.into()) + } AliasConstKind::Free { def_id } => Some(def_id.into()), AliasConstKind::Anon { def_id } => Some(def_id.into()), } diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index e9339338a6ef1..9106131840d3e 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -356,7 +356,19 @@ impl { - lhs_alias.kind == rhs_alias.kind + (lhs_alias.kind == rhs_alias.kind + || matches!( + (lhs_alias.kind, rhs_alias.kind), + ( + ty::AliasTyKind::EvidenceProjection { + projection: lhs_projection, + }, + ty::AliasTyKind::EvidenceProjection { + projection: rhs_projection, + }, + ) if lhs_projection.item_def_id + == rhs_projection.item_def_id + )) && self.args_may_unify_inner(lhs_alias.args, rhs_alias.args, depth) } _ => false, diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 7b0a098ad1948..18f68663949ac 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -1,3 +1,4 @@ +use crate::data_structures::HashSet; use crate::inherent::*; use crate::visit::Flags; use crate::{self as ty, Interner, Region}; @@ -158,6 +159,9 @@ bitflags::bitflags! { /// We have a separate flag from `HAS_ALIAS` because `HAS_ALIAS` doesn't care /// about rigidness while we rely on rigidness to skip renormalization. const HAS_NON_RIGID_ALIAS = 1 << 28; + + /// Does this have an evidence-indexed projection? + const HAS_EVIDENCE_PROJECTION = 1 << 29; } } @@ -237,6 +241,33 @@ impl FlagComputation { computation.add_flags(TypeFlags::HAS_BINDER_VARS); } + // Dependent telescope metadata is folded and hashed as part of the + // binder, so its flags and escaping depth must be visible as well. + // Otherwise a surrounding interned type may incorrectly skip a folder + // even though a typed const declaration or evidence assumption contains + // inference variables, placeholders, aliases, or outer bound vars. + for entry in value.bound_vars().iter() { + match entry { + ty::BoundVariableKind::Const(Some(ty)) => computation.add_ty(ty), + ty::BoundVariableKind::Evidence(evidence) => { + computation.add_predicate_atom(ty::PredicateKind::Clause(evidence.clause)); + if let Some(contract) = evidence.required_contract { + computation.add_args(contract.identity.complete_early_args.as_slice()); + for clause in contract.clauses.iter() { + computation.add_flags(clause.flags()); + computation.add_exclusive_binder(clause.outer_exclusive_binder()); + } + if let Some(args) = contract.ordinary_args { + computation.add_args(args.as_slice()); + } + } + } + ty::BoundVariableKind::Ty(_) + | ty::BoundVariableKind::Region(_) + | ty::BoundVariableKind::Const(None) => {} + } + } + f(&mut computation, value.skip_binder()); self.add_flags(computation.flags); @@ -310,7 +341,9 @@ impl FlagComputation { ty::Alias(is_rigid, alias) => { self.add_is_rigid(is_rigid); self.add_flags(match alias.kind { - ty::Projection { .. } => TypeFlags::HAS_TY_PROJECTION, + ty::Projection { .. } | ty::EvidenceProjection { .. } => { + TypeFlags::HAS_TY_PROJECTION + } ty::Free { .. } => TypeFlags::HAS_TY_FREE_ALIAS, ty::Opaque { .. } => TypeFlags::HAS_TY_OPAQUE, ty::Inherent { .. } => TypeFlags::HAS_TY_INHERENT, @@ -473,6 +506,17 @@ impl FlagComputation { ty::ConstKind::Alias(is_rigid, alias_const) => { self.add_is_rigid(is_rigid); self.add_args(alias_const.args.as_slice()); + match alias_const.kind { + ty::AliasConstKind::EvidenceProjection { projection } => { + self.add_flags(TypeFlags::HAS_EVIDENCE_PROJECTION); + self.add_trait_evidence(projection.evidence); + } + ty::AliasConstKind::Projection { .. } + | ty::AliasConstKind::InherentSelf { .. } + | ty::AliasConstKind::InherentImpl { .. } + | ty::AliasConstKind::Free { .. } + | ty::AliasConstKind::Anon { .. } => {} + } self.add_flags(TypeFlags::HAS_CONST_ALIAS); } ty::ConstKind::Infer(infer) => match infer { @@ -519,10 +563,144 @@ impl FlagComputation { fn add_alias_ty(&mut self, alias_ty: ty::AliasTy) { self.add_args(alias_ty.args.as_slice()); + if let ty::AliasTyKind::EvidenceProjection { projection } = alias_ty.kind { + self.add_flags(TypeFlags::HAS_EVIDENCE_PROJECTION); + self.add_trait_evidence(projection.evidence); + } } fn add_alias_term(&mut self, alias_term: ty::AliasTerm) { self.add_args(alias_term.args.as_slice()); + match alias_term.kind { + ty::AliasTermKind::EvidenceProjectionTy { projection } + | ty::AliasTermKind::EvidenceProjectionConst { projection } => { + self.add_flags(TypeFlags::HAS_EVIDENCE_PROJECTION); + self.add_trait_evidence(projection.evidence); + } + ty::AliasTermKind::ProjectionTy { .. } + | ty::AliasTermKind::ProjectionConst { .. } + | ty::AliasTermKind::InherentTy { .. } + | ty::AliasTermKind::OpaqueTy { .. } + | ty::AliasTermKind::FreeTy { .. } + | ty::AliasTermKind::AnonConst { .. } + | ty::AliasTermKind::FreeConst { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } => {} + } + } + + /// Adds every parent-scope value which is folded as part of a proof recipe. + /// + /// Evidence-indexed projections keep trait arguments in the evidence rather + /// than in `Alias::args`. Missing these flags lets folders incorrectly skip + /// the alias and overlook bound variables or placeholders in its proof. + fn add_trait_evidence(&mut self, evidence: I::TraitEvidence) { + // Interned nested proofs share this variable scope. Visit each handle once; + // a fresh cache for each root keeps separate binder scopes independent. + self.add_trait_evidence_with_cache(evidence, &mut HashSet::default()); + } + + fn add_trait_evidence_with_cache( + &mut self, + evidence: I::TraitEvidence, + visited: &mut HashSet, + ) { + if !visited.insert(evidence) { + return; + } + match &evidence.kind { + ty::solve::TraitEvidenceKind::Selected(recipe) => { + self.add_selected_trait_evidence(recipe, visited) + } + ty::solve::TraitEvidenceKind::Bound(index, _) => { + self.add_args(evidence.trait_ref.args.as_slice()); + self.add_flags(TypeFlags::HAS_TY_BOUND); + match index { + ty::BoundVarIndexKind::Bound(debruijn) => self.add_bound_var(*debruijn), + ty::BoundVarIndexKind::Canonical => { + self.add_flags(TypeFlags::HAS_CANONICAL_BOUND) + } + } + } + ty::solve::TraitEvidenceKind::Placeholder(_) => { + self.add_args(evidence.trait_ref.args.as_slice()); + self.add_flags(TypeFlags::HAS_TY_PLACEHOLDER); + } + ty::solve::TraitEvidenceKind::Error(_) => { + self.add_args(evidence.trait_ref.args.as_slice()); + self.add_flags(TypeFlags::HAS_NON_REGION_ERROR); + } + } + } + + fn add_selected_trait_evidence( + &mut self, + evidence: &ty::solve::CandidateEvidence, + visited: &mut HashSet, + ) { + for node in &evidence.nodes { + self.add_args(node.trait_ref.args.as_slice()); + + match node.source { + ty::solve::CandidateEvidenceSource::Unique(key) => { + self.add_args(key.trait_ref.args.as_slice()); + } + ty::solve::CandidateEvidenceSource::Impl { args, .. } => { + self.add_args(args.as_slice()) + } + ty::solve::CandidateEvidenceSource::Dyn { + object_bound, + instantiation, + operation, + .. + } => { + self.add_flags(object_bound.flags()); + self.add_exclusive_binder(object_bound.outer_exclusive_binder()); + if let Some(instantiation) = instantiation { + self.add_args(instantiation.as_slice()); + } + if let Some(operation) = operation { + self.add_flags(operation.projection_bound.flags()); + self.add_exclusive_binder( + operation.projection_bound.outer_exclusive_binder(), + ); + self.add_args(operation.ordinary_args.as_slice()); + } + } + ty::solve::CandidateEvidenceSource::ParamEnv { origin, .. } => match origin { + ty::solve::ParamEnvAssumption::ItemContract { contract } => { + self.add_args(contract.complete_early_args.as_slice()); + } + ty::solve::ParamEnvAssumption::Binder { identity, instantiation, .. } => { + self.add_flags(identity.flags()); + self.add_exclusive_binder(identity.outer_exclusive_binder()); + self.add_args(instantiation.as_slice()); + } + ty::solve::ParamEnvAssumption::CallerBound { .. } + | ty::solve::ParamEnvAssumption::ItemClause { .. } + | ty::solve::ParamEnvAssumption::Generated { .. } => {} + }, + ty::solve::CandidateEvidenceSource::Builtin { evidence, .. } => match evidence { + ty::solve::BuiltinEvidence::RuleOnly => {} + ty::solve::BuiltinEvidence::Fn { output, instantiation } + | ty::solve::BuiltinEvidence::AsyncFn { output, instantiation } => { + self.add_ty(output); + self.add_args(instantiation.as_slice()); + } + }, + ty::solve::CandidateEvidenceSource::AliasBound(_) + | ty::solve::CandidateEvidenceSource::Error + | ty::solve::CandidateEvidenceSource::CoherenceUnknowable => {} + } + + for nested in &node.nested_evidence { + match nested { + ty::solve::CandidateEvidenceUse::Instantiated(evidence) => { + self.add_trait_evidence_with_cache(*evidence, visited) + } + } + } + } } fn add_args(&mut self, args: &[I::GenericArg]) { diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 2b25de4132e62..599e43ab695ca 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -148,6 +148,37 @@ pub trait TypeFolder: Sized { c.super_fold_with(self) } + /// Folds the identity, clauses, and substitution of an interned contract. + fn fold_bound_required_contract( + &mut self, + contract: I::BoundRequiredContract, + ) -> I::BoundRequiredContract { + self.cx().mk_bound_required_contract((*contract).clone().fold_with(self)) + } + + /// Folds one compiler-internal trait evidence value. + /// + /// Bound and placeholder evidence are not represented by ordinary generic + /// arguments. This hook lets binder substitution replace those values + /// without teaching every evidence consumer about the traversal. + fn fold_trait_evidence(&mut self, evidence: I::TraitEvidence) -> I::TraitEvidence { + self.cx().mk_trait_evidence_data((*evidence).clone().fold_with(self)) + } + + /// Folds the proof payload of an evidence-indexed projection. + /// + /// This hook is separate from [`TypeFolder::fold_ty`] and + /// [`TypeFolder::fold_const`] because an [`ty::AliasTerm`] may carry an + /// evidence projection without first being wrapped in either one. It also + /// lets folders distinguish the selected proof recipe from the associated + /// item's own arguments in `Alias::args`. + fn fold_evidence_projection( + &mut self, + projection: I::EvidenceProjection, + ) -> I::EvidenceProjection { + self.cx().mk_evidence_projection((*projection).fold_with(self)) + } + fn fold_predicate>(&mut self, p: P) -> P { p.super_fold_with(self) } @@ -217,6 +248,30 @@ pub trait FallibleTypeFolder: Sized { c.try_super_fold_with(self) } + /// Fallible counterpart of [`TypeFolder::fold_bound_required_contract`]. + fn try_fold_bound_required_contract( + &mut self, + contract: I::BoundRequiredContract, + ) -> Result { + Ok(self.cx().mk_bound_required_contract((*contract).clone().try_fold_with(self)?)) + } + + /// Fallible counterpart of [`TypeFolder::fold_trait_evidence`]. + fn try_fold_trait_evidence( + &mut self, + evidence: I::TraitEvidence, + ) -> Result { + Ok(self.cx().mk_trait_evidence_data((*evidence).clone().try_fold_with(self)?)) + } + + /// Fallible counterpart of [`TypeFolder::fold_evidence_projection`]. + fn try_fold_evidence_projection( + &mut self, + projection: I::EvidenceProjection, + ) -> Result { + Ok(self.cx().mk_evidence_projection((*projection).try_fold_with(self)?)) + } + fn try_fold_predicate>(&mut self, p: P) -> Result { p.try_super_fold_with(self) } @@ -460,6 +515,27 @@ impl TypeFolder for Shifter { } } + fn fold_trait_evidence(&mut self, evidence: I::TraitEvidence) -> I::TraitEvidence { + match &evidence.kind { + ty::solve::TraitEvidenceKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound) + if *debruijn >= self.current_index => + { + let trait_ref = evidence.trait_ref.fold_with(self); + self.cx.mk_trait_evidence_kind( + trait_ref, + ty::solve::TraitEvidenceKind::Bound( + ty::BoundVarIndexKind::Bound(debruijn.shifted_in(self.amount)), + *bound, + ), + ) + } + _ if evidence.has_vars_bound_at_or_above(self.current_index) => { + self.cx.mk_trait_evidence_data((*evidence).clone().fold_with(self)) + } + _ => evidence, + } + } + fn fold_predicate>(&mut self, p: P) -> P { if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } } @@ -666,6 +742,15 @@ impl TypeFolder for RigidnessFolder { self.cx } + fn fold_evidence_projection( + &mut self, + projection: I::EvidenceProjection, + ) -> I::EvidenceProjection { + // Rigidity belongs to the alias, not its selected proof. Preserve the + // interned recipe while folding the associated item's own arguments. + projection + } + fn fold_binder>(&mut self, t: ty::Binder) -> ty::Binder { if self.mode.needs_change(&t) { t.super_fold_with(self) } else { t } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 31a027c15fd01..ae467acbdeabe 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -17,7 +17,8 @@ use crate::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTrait use crate::relate::Relate; use crate::search_graph::RequiredDepth; use crate::solve::{ - AccessedOpaques, CanonicalInputData, Certainty, ExternalConstraintsData, QueryResult, inspect, + AccessedOpaques, CandidateEvidence, CanonicalInputData, Certainty, ExternalConstraintsData, + QueryResult, TraitEvidenceData, TraitEvidenceKind, inspect, }; use crate::visit::{Flags, TypeVisitable}; use crate::{ @@ -159,6 +160,118 @@ pub trait Interner: data: ExternalConstraintsData, ) -> Self::ExternalConstraints; + #[cfg(feature = "nightly")] + type BoundRequiredContract: Copy + + Debug + + Hash + + Eq + + StableHash + + TypeFoldable + + TypeVisitable + + Deref>; + + #[cfg(not(feature = "nightly"))] + type BoundRequiredContract: Copy + + Debug + + Hash + + Eq + + TypeFoldable + + TypeVisitable + + Deref>; + + fn mk_bound_required_contract( + self, + data: ty::BoundRequiredContractData, + ) -> Self::BoundRequiredContract; + + #[cfg(feature = "nightly")] + type TraitEvidence: Copy + + Debug + + Hash + + Eq + + StableHash + + TypeFoldable + + TypeVisitable + + Deref>; + + #[cfg(not(feature = "nightly"))] + type TraitEvidence: Copy + + Debug + + Hash + + Eq + + TypeFoldable + + TypeVisitable + + Deref>; + fn mk_trait_evidence_data(self, data: TraitEvidenceData) -> Self::TraitEvidence; + + type TraitEvidences: Copy + + Debug + + Hash + + Eq + + Default + + TypeFoldable + + TypeVisitable + + SliceLike; + + fn mk_trait_evidences(self, values: &[Self::TraitEvidence]) -> Self::TraitEvidences; + + fn mk_trait_evidences_from_iter(self, values: I) -> T::Output + where + I: Iterator, + T: CollectAndApply; + + fn mk_trait_evidence(self, recipe: CandidateEvidence) -> Self::TraitEvidence { + self.mk_trait_evidence_data(TraitEvidenceData::selected(recipe)) + } + + fn mk_trait_evidence_kind( + self, + trait_ref: TraitRef, + kind: TraitEvidenceKind, + ) -> Self::TraitEvidence { + let data = match kind { + TraitEvidenceKind::Selected(recipe) => { + let data = TraitEvidenceData::selected(recipe); + assert_eq!( + data.trait_ref, trait_ref, + "selected evidence predicate does not match its proof recipe" + ); + data + } + TraitEvidenceKind::Bound(index, bound) => { + TraitEvidenceData::bound(trait_ref, index, bound) + } + TraitEvidenceKind::Placeholder(placeholder) => { + TraitEvidenceData::placeholder(trait_ref, placeholder) + } + TraitEvidenceKind::Error(guar) => TraitEvidenceData::error(trait_ref, guar), + }; + self.mk_trait_evidence_data(data) + } + + #[cfg(feature = "nightly")] + type EvidenceProjection: Copy + + Debug + + Hash + + Eq + + StableHash + + TypeFoldable + + TypeVisitable + + Deref>; + + #[cfg(not(feature = "nightly"))] + type EvidenceProjection: Copy + + Debug + + Hash + + Eq + + TypeFoldable + + TypeVisitable + + Deref>; + fn mk_evidence_projection( + self, + data: ty::EvidenceProjectionData, + ) -> Self::EvidenceProjection; + type DepNodeIndex; type Tracked: Debug; fn mk_tracked( @@ -312,6 +425,10 @@ pub trait Interner: args: Self::GenericArgs, ) -> (ty::TraitRef, Self::GenericArgsSlice); + /// Converts an interned generic-argument list into the implementation's + /// corresponding interned slice view. + fn generic_args_slice(self, args: Self::GenericArgs) -> Self::GenericArgsSlice; + fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs; fn mk_args_from_iter(self, args: I) -> T::Output @@ -584,7 +701,10 @@ macro_rules! declare_lift_into { } declare_lift_into! { + BoundRequiredContract, BoundVarKinds, + Clause, + Clauses, Const, DefId, EarlyParamRegion, @@ -602,6 +722,9 @@ declare_lift_into! { RegionAssumptions, Symbol, Term, + TraitEvidence, + TraitEvidences, + EvidenceProjection, TraitAssocConstId, TraitAssocTermId, TraitAssocTyId, diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index 15741c926092e..0a2b4e6660afa 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -234,7 +234,12 @@ pub fn compute_alias_components_recursive( let mut visitor = OutlivesCollector { cx, out, visited: Default::default() }; - for (index, child) in alias_ty.args.iter().enumerate() { + // Evidence projections store trait and `Self` arguments exclusively in + // their proof recipe. Outlives decomposition must nevertheless consider + // the same full argument list as a surface projection; using `args` alone + // would make a non-GAT associated type have no components and therefore + // vacuously outlive every region. + for (index, child) in alias_ty.full_args(cx).iter().enumerate() { if opt_variances.and_then(|variances| variances.get(index)) == Some(ty::Bivariant) { continue; } @@ -268,16 +273,19 @@ pub fn declared_bounds_from_definition( cx: I, alias_ty: AliasTy, ) -> impl Iterator> { - let def_id = match alias_ty.kind { - ty::AliasTyKind::Projection { def_id } => def_id.into(), - ty::AliasTyKind::Inherent { def_id } => def_id.into(), - ty::AliasTyKind::Opaque { def_id } => def_id.into(), - ty::AliasTyKind::Free { def_id } => def_id.into(), + let (def_id, args) = match alias_ty.kind { + ty::AliasTyKind::Projection { def_id } => (def_id.into(), alias_ty.args), + ty::AliasTyKind::EvidenceProjection { projection } => { + (projection.item_def_id.into(), alias_ty.full_args(cx)) + } + ty::AliasTyKind::Inherent { def_id } => (def_id.into(), alias_ty.args), + ty::AliasTyKind::Opaque { def_id } => (def_id.into(), alias_ty.args), + ty::AliasTyKind::Free { def_id } => (def_id.into(), alias_ty.args), }; let bounds = cx.item_self_bounds(def_id); bounds - .iter_instantiated(cx, alias_ty.args) + .iter_instantiated(cx, args) .map(Unnormalized::skip_norm_wip) .filter_map(|c| c.as_type_outlives_clause()) .filter_map(|c| c.no_bound_vars()) diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 0dd79d8d0449e..af780b2b73a3a 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -950,23 +950,7 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl // // we don't care about this when rewriting in the root universe as we know the complete set of assumptions if max_universe(infcx, bound_outlives) == u { - let mut replacer = PlaceholderReplacer { - cx: infcx.cx(), - existing_var_count: bound_outlives.bound_vars().len(), - bound_vars: IndexMap::default(), - universe: u, - current_index: DebruijnIndex::ZERO, - }; - let escaping_outlives = bound_outlives.skip_binder().fold_with(&mut replacer); - let bound_vars = bound_outlives.bound_vars().iter().chain( - core::mem::take(&mut replacer.bound_vars) - .into_iter() - .map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)), - ); - let bound_outlives = Binder::bind_with_vars( - escaping_outlives, - I::BoundVarKinds::from_vars(infcx.cx(), bound_vars), - ); + let bound_outlives = bind_placeholder_regions(infcx.cx(), bound_outlives, u); let candidate = Or::new_leaf(AliasTyOutlivesViaEnv(bound_outlives, ())); if max_universe(infcx, candidate.clone()) < u { candidates.push(candidate); @@ -994,25 +978,10 @@ fn rewrite_alias_ty_outlives_constraints_in_universe_for_eager_placeholder_handl // given a list of regions which outlive `'u2` // // we don't care about this when rewriting in the root universe as we know the complete set of assumptions - let (escaping_alias, escaping_r) = bound_outlives.skip_binder(); + let (_, escaping_r) = bound_outlives.skip_binder(); if max_universe(infcx, escaping_r) == u { - let mut replacer = PlaceholderReplacer { - cx: infcx.cx(), - existing_var_count: bound_outlives.bound_vars().len(), - bound_vars: IndexMap::default(), - universe: u, - current_index: DebruijnIndex::ZERO, - }; - let escaping_alias = escaping_alias.fold_with(&mut replacer); - let bound_vars = bound_outlives.bound_vars().iter().chain( - core::mem::take(&mut replacer.bound_vars) - .into_iter() - .map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)), - ); - let bound_alias = Binder::bind_with_vars( - escaping_alias, - I::BoundVarKinds::from_vars(infcx.cx(), bound_vars), - ); + let bound_alias = + bind_placeholder_regions(infcx.cx(), bound_outlives.map_bound(|(alias, _)| alias), u); // while we did skip the binder, bound vars aren't in any universe so // this can't be an escaping bound var @@ -1092,6 +1061,85 @@ pub fn regions_outlived_by_placeholder( }) } +fn bind_placeholder_regions>( + cx: I, + binder: Binder, + universe: UniverseIndex, +) -> Binder { + let ordinary_count = binder.ordinary_bound_var_count(); + let mut replacer = PlaceholderReplacer { + cx, + existing_var_count: ordinary_count, + bound_vars: IndexMap::default(), + universe, + current_index: DebruijnIndex::ZERO, + }; + // Declarations and value share the same mapping, including placeholders + // occurring only in a declaration's predicate or required contract. + let original_bound_vars = binder.bound_vars(); + let value = binder.skip_binder().fold_with(&mut replacer); + let entries: Vec<_> = + original_bound_vars.iter().map(|entry| entry.fold_with(&mut replacer)).collect(); + let new_regions: Vec<_> = replacer + .bound_vars + .into_iter() + .map(|(_, region)| BoundVariableKind::Region(region.kind)) + .collect(); + let mut reindex = ReindexEvidence { + cx, + ordinary_count, + amount: new_regions.len(), + current_index: DebruijnIndex::ZERO, + }; + let value = value.fold_with(&mut reindex); + let entries: Vec<_> = entries.into_iter().map(|entry| entry.fold_with(&mut reindex)).collect(); + let bound_vars = I::BoundVarKinds::from_vars( + cx, + entries[..ordinary_count] + .iter() + .copied() + .chain(new_regions) + .chain(entries[ordinary_count..].iter().copied()), + ); + Binder::bind_with_vars(value, bound_vars) +} + +/// Inserting regions before the evidence suffix moves only that binder's +/// evidence slots. Nested binders' own slots and canonical scopes stay intact. +struct ReindexEvidence { + cx: I, + ordinary_count: usize, + amount: usize, + current_index: DebruijnIndex, +} + +impl TypeFolder for ReindexEvidence { + fn cx(&self) -> I { + self.cx + } + + fn fold_binder>(&mut self, binder: Binder) -> Binder { + self.current_index.shift_in(1); + let binder = binder.super_fold_with(self); + self.current_index.shift_out(1); + binder + } + + fn fold_trait_evidence(&mut self, evidence: I::TraitEvidence) -> I::TraitEvidence { + let mut data = (*evidence).clone().fold_with(self); + if let crate::solve::TraitEvidenceKind::Bound(crate::BoundVarIndexKind::Bound(index), bound) = + &mut data.kind + && *index == self.current_index + { + assert!(bound.var().as_usize() >= self.ordinary_count); + *bound = crate::BoundEvidence::new(BoundVar::from_usize( + bound.var().as_usize() + self.amount, + )); + } + self.cx.mk_trait_evidence_data(data) + } +} + pub struct PlaceholderReplacer { cx: I, existing_var_count: usize, diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index f6491bac642e3..52ae0d5171491 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -7,7 +7,7 @@ use tracing::{instrument, trace}; use crate::error::{ExpectedFound, TypeError}; use crate::fold::TypeFoldable; use crate::inherent::*; -use crate::{self as ty, Interner, Region}; +use crate::{self as ty, Interner, Region, Upcast}; pub mod combine; pub mod solver_relating; @@ -92,6 +92,19 @@ pub trait TypeRelation: Sized { fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult; + /// Relates the proof identities carried by evidence-indexed projections. + /// + /// Evidence is intentionally not a `GenericArg`, so it needs an explicit + /// relation hook just like types and consts. The default is the strict + /// structural relation for fully-instantiated proofs. + fn evidences( + &mut self, + a: I::TraitEvidence, + b: I::TraitEvidence, + ) -> RelateResult { + relate_trait_evidence_invariantly(self, a, b) + } + fn binders( &mut self, a: ty::Binder, @@ -199,23 +212,388 @@ impl Relate for ty::FnSig { } } +enum EvidenceRelateError { + DifferentProof, + Type(TypeError), +} + +impl From> for EvidenceRelateError { + fn from(error: TypeError) -> Self { + EvidenceRelateError::Type(error) + } +} + +fn relate_evidence_args>( + relation: &mut R, + a: I::GenericArgs, + b: I::GenericArgs, +) -> Result> { + if a.len() != b.len() { + return Err(EvidenceRelateError::DifferentProof); + } + Ok(relate_args_invariantly(relation, a, b)?) +} + +fn relate_evidence_source>( + relation: &mut R, + a: ty::solve::CandidateEvidenceSource, + b: ty::solve::CandidateEvidenceSource, + trait_ref: ty::TraitRef, +) -> Result, EvidenceRelateError> { + use ty::solve::CandidateEvidenceSource; + + Ok(match (a, b) { + (CandidateEvidenceSource::Unique(_), CandidateEvidenceSource::Unique(_)) => { + CandidateEvidenceSource::Unique(ty::solve::CoherenceKey { trait_ref }) + } + ( + CandidateEvidenceSource::Impl { impl_def_id: a_impl, args: a_args }, + CandidateEvidenceSource::Impl { impl_def_id: b_impl, args: b_args }, + ) if a_impl == b_impl => CandidateEvidenceSource::Impl { + impl_def_id: a_impl, + args: relate_evidence_args(relation, a_args, b_args)?, + }, + ( + CandidateEvidenceSource::Builtin { source: a_source, evidence: a_evidence }, + CandidateEvidenceSource::Builtin { source: b_source, evidence: b_evidence }, + ) if a_source == b_source => { + use ty::solve::BuiltinEvidence; + + let evidence = match (a_evidence, b_evidence) { + (BuiltinEvidence::RuleOnly, BuiltinEvidence::RuleOnly) => BuiltinEvidence::RuleOnly, + ( + BuiltinEvidence::Fn { output: a_output, instantiation: a_instantiation }, + BuiltinEvidence::Fn { output: b_output, instantiation: b_instantiation }, + ) => BuiltinEvidence::Fn { + instantiation: relate_evidence_args( + relation, + a_instantiation, + b_instantiation, + )?, + output: relation.relate_with_variance( + ty::Invariant, + VarianceDiagInfo::default(), + a_output, + b_output, + )?, + }, + ( + BuiltinEvidence::AsyncFn { output: a_output, instantiation: a_instantiation }, + BuiltinEvidence::AsyncFn { output: b_output, instantiation: b_instantiation }, + ) => BuiltinEvidence::AsyncFn { + instantiation: relate_evidence_args( + relation, + a_instantiation, + b_instantiation, + )?, + output: relation.relate_with_variance( + ty::Invariant, + VarianceDiagInfo::default(), + a_output, + b_output, + )?, + }, + _ => return Err(EvidenceRelateError::DifferentProof), + }; + CandidateEvidenceSource::Builtin { source: a_source, evidence } + } + ( + CandidateEvidenceSource::Dyn { + object_bound: a_bound, + instantiation: a_instantiation, + vtable_slot: a_slot, + operation: a_operation, + }, + CandidateEvidenceSource::Dyn { + object_bound: b_bound, + instantiation: b_instantiation, + vtable_slot: b_slot, + operation: b_operation, + }, + ) if a_slot == b_slot => { + let a_bound = a_bound.as_trait_clause().ok_or(EvidenceRelateError::DifferentProof)?; + let b_bound = b_bound.as_trait_clause().ok_or(EvidenceRelateError::DifferentProof)?; + let object_bound = relation + .relate(a_bound, b_bound)? + .map_bound(ty::ClauseKind::Trait) + .upcast(relation.cx()); + let instantiation = match (a_instantiation, b_instantiation) { + (Some(a), Some(b)) => Some(relate_evidence_args(relation, a, b)?), + (None, None) => None, + _ => return Err(EvidenceRelateError::DifferentProof), + }; + let operation = match (a_operation, b_operation) { + (Some(a), Some(b)) => { + let a_bound = a + .projection_bound + .as_projection_clause() + .ok_or(EvidenceRelateError::DifferentProof)?; + let b_bound = b + .projection_bound + .as_projection_clause() + .ok_or(EvidenceRelateError::DifferentProof)?; + let projection_bound = relation + .relate(a_bound, b_bound)? + .map_bound(ty::ClauseKind::Projection) + .upcast(relation.cx()); + Some(ty::solve::DynProjectionOperation { + projection_bound, + ordinary_args: relate_evidence_args( + relation, + a.ordinary_args, + b.ordinary_args, + )?, + }) + } + (None, None) => None, + _ => return Err(EvidenceRelateError::DifferentProof), + }; + CandidateEvidenceSource::Dyn { + object_bound, + instantiation, + vtable_slot: a_slot, + operation, + } + } + ( + CandidateEvidenceSource::ParamEnv { source: a_source, origin: a_origin }, + CandidateEvidenceSource::ParamEnv { source: b_source, origin: b_origin }, + ) if a_source == b_source => { + let origin = match (a_origin, b_origin) { + ( + ty::solve::ParamEnvAssumption::ItemContract { contract: a_contract }, + ty::solve::ParamEnvAssumption::ItemContract { contract: b_contract }, + ) if a_contract.key == b_contract.key => { + ty::solve::ParamEnvAssumption::ItemContract { + contract: ty::solve::InstantiatedItemContract { + key: a_contract.key, + complete_early_args: relate_evidence_args( + relation, + a_contract.complete_early_args, + b_contract.complete_early_args, + )?, + }, + } + } + ( + ty::solve::ParamEnvAssumption::Binder { + telescope_index: a_index, + identity: a_identity, + instantiation: a_instantiation, + }, + ty::solve::ParamEnvAssumption::Binder { + telescope_index: b_index, + identity: b_identity, + instantiation: b_instantiation, + }, + ) if a_index == b_index && a_identity == b_identity => { + ty::solve::ParamEnvAssumption::Binder { + telescope_index: a_index, + identity: a_identity, + instantiation: relate_evidence_args( + relation, + a_instantiation, + b_instantiation, + )?, + } + } + (a_origin, b_origin) if a_origin == b_origin => a_origin, + _ => return Err(EvidenceRelateError::DifferentProof), + }; + CandidateEvidenceSource::ParamEnv { source: a_source, origin } + } + (CandidateEvidenceSource::AliasBound(a), CandidateEvidenceSource::AliasBound(b)) + if a == b => + { + CandidateEvidenceSource::AliasBound(a) + } + (CandidateEvidenceSource::Error, CandidateEvidenceSource::Error) => { + CandidateEvidenceSource::Error + } + ( + CandidateEvidenceSource::CoherenceUnknowable, + CandidateEvidenceSource::CoherenceUnknowable, + ) => CandidateEvidenceSource::CoherenceUnknowable, + _ => { + trace!(?a, ?b, ?trait_ref, "evidence source mismatch"); + return Err(EvidenceRelateError::DifferentProof); + } + }) +} + +fn relate_trait_evidence>( + relation: &mut R, + a: I::TraitEvidence, + b: I::TraitEvidence, +) -> Result> { + if a == b { + return Ok(a); + } + if a.trait_ref.args.len() != b.trait_ref.args.len() { + return Err(EvidenceRelateError::DifferentProof); + } + + use ty::solve::TraitEvidenceKind; + + let kind = match (&a.kind, &b.kind) { + (TraitEvidenceKind::Selected(a), TraitEvidenceKind::Selected(b)) => { + let evidence = relate_candidate_evidence(relation, a, b)?; + return Ok(relation.cx().mk_trait_evidence(evidence)); + } + ( + TraitEvidenceKind::Bound(a_index, a_bound), + TraitEvidenceKind::Bound(b_index, b_bound), + ) if a_index == b_index && a_bound == b_bound => { + TraitEvidenceKind::Bound(*a_index, *a_bound) + } + ( + TraitEvidenceKind::Placeholder(a_placeholder), + TraitEvidenceKind::Placeholder(b_placeholder), + ) if a_placeholder == b_placeholder => TraitEvidenceKind::Placeholder(*a_placeholder), + // Error evidence is only a recovery marker. Two independently + // constructed error values must not establish semantic equality. + (TraitEvidenceKind::Error(_), _) + | (TraitEvidenceKind::Selected(_), _) + | (TraitEvidenceKind::Bound(..), _) + | (TraitEvidenceKind::Placeholder(_), _) => { + trace!(?a, ?b, "trait evidence kind mismatch"); + return Err(EvidenceRelateError::DifferentProof); + } + }; + + let trait_ref = relation.relate(a.trait_ref, b.trait_ref)?; + Ok(relation.cx().mk_trait_evidence_kind(trait_ref, kind)) +} + +/// Invariantly relates two fully-instantiated evidence values. +/// +/// Evidence is kept outside [`GenericArg`](ty::GenericArgKind), so it cannot +/// use the blanket [`Relate`] entry point. Relate selected proof recipes +/// structurally and expose a normal [`TypeError`] to callers. +pub fn relate_trait_evidence_invariantly>( + relation: &mut R, + a: I::TraitEvidence, + b: I::TraitEvidence, +) -> RelateResult { + match relate_trait_evidence(relation, a, b) { + Ok(evidence) => Ok(evidence), + Err(EvidenceRelateError::Type(error)) => Err(error), + Err(EvidenceRelateError::DifferentProof) => Err(TypeError::Mismatch), + } +} + +fn relate_candidate_evidence>( + relation: &mut R, + a: &ty::solve::CandidateEvidence, + b: &ty::solve::CandidateEvidence, +) -> Result, EvidenceRelateError> { + if a.root != b.root || a.nodes.len() != b.nodes.len() { + trace!(?a, ?b, "evidence graph shape mismatch"); + return Err(EvidenceRelateError::DifferentProof); + } + let mut nodes = Vec::with_capacity(a.nodes.len()); + for (a_node, b_node) in a.nodes.iter().zip(&b.nodes) { + if a_node.trait_ref.args.len() != b_node.trait_ref.args.len() + || a_node.nested != b_node.nested + || a_node.nested_evidence.len() != b_node.nested_evidence.len() + { + trace!(?a_node, ?b_node, "evidence node edge mismatch"); + return Err(EvidenceRelateError::DifferentProof); + } + + let trait_ref = relation.relate(a_node.trait_ref, b_node.trait_ref)?; + let source = relate_evidence_source(relation, a_node.source, b_node.source, trait_ref)?; + let mut nested_evidence = Vec::with_capacity(a_node.nested_evidence.len()); + for (&a_nested, &b_nested) in a_node.nested_evidence.iter().zip(&b_node.nested_evidence) { + use ty::solve::CandidateEvidenceUse; + let ( + CandidateEvidenceUse::Instantiated(a_evidence), + CandidateEvidenceUse::Instantiated(b_evidence), + ) = (a_nested, b_nested); + nested_evidence.push(CandidateEvidenceUse::Instantiated( + relation.evidences(a_evidence, b_evidence)?, + )); + } + + nodes.push(ty::solve::CandidateEvidenceNode { + trait_ref, + source, + nested: a_node.nested.clone(), + nested_evidence, + }); + } + + let evidence = ty::solve::CandidateEvidence { root: a.root, nodes }; + evidence.assert_well_formed(); + Ok(evidence) +} + +fn relate_evidence_projection>( + relation: &mut R, + a: I::EvidenceProjection, + b: I::EvidenceProjection, +) -> Result> { + if a.item_def_id != b.item_def_id { + return Err(EvidenceRelateError::DifferentProof); + } + let evidence = relation.evidences(a.evidence, b.evidence).map_err(EvidenceRelateError::Type)?; + Ok(relation.cx().mk_evidence_projection(ty::EvidenceProjectionData { + item_def_id: a.item_def_id, + evidence, + })) +} + impl Relate for ty::AliasTy { fn relate>( relation: &mut R, a: ty::AliasTy, b: ty::AliasTy, ) -> RelateResult> { - if a.kind != b.kind { - Err(TypeError::ProjectionMismatched(ExpectedFound::new(a.kind.into(), b.kind.into()))) + let kind = if a.kind == b.kind { + a.kind + } else if let ( + ty::AliasTyKind::EvidenceProjection { projection: a_projection }, + ty::AliasTyKind::EvidenceProjection { projection: b_projection }, + ) = (a.kind, b.kind) + { + match relate_evidence_projection(relation, a_projection, b_projection) { + Ok(projection) => ty::AliasTyKind::EvidenceProjection { projection }, + Err(EvidenceRelateError::Type(error)) => return Err(error), + Err(EvidenceRelateError::DifferentProof) => { + return Err(TypeError::ProjectionMismatched(ExpectedFound::new( + a.kind.into(), + b.kind.into(), + ))); + } + } } else { - let cx = relation.cx(); - let args = if let Some(variances) = cx.opt_alias_variances(a.kind) { - relate_args_with_variances(relation, variances, a.args, b.args)? + return Err(TypeError::ProjectionMismatched(ExpectedFound::new( + a.kind.into(), + b.kind.into(), + ))); + }; + + let cx = relation.cx(); + let args = if let Some(variances) = cx.opt_alias_variances(kind) { + if let ty::AliasTyKind::EvidenceProjection { projection } = kind { + let parent_count = projection.trait_ref().args.len(); + cx.mk_args_from_iter(iter::zip(a.args.iter(), b.args.iter()).enumerate().map( + |(index, (a, b))| { + relation.relate_with_variance( + variances.get(parent_count + index).unwrap(), + VarianceDiagInfo::None, + a, + b, + ) + }, + ))? } else { - relate_args_invariantly(relation, a.args, b.args)? - }; - Ok(ty::AliasTy::new_from_args(relation.cx(), a.kind, args)) - } + relate_args_with_variances(relation, variances, a.args, b.args)? + } + } else { + relate_args_invariantly(relation, a.args, b.args)? + }; + Ok(ty::AliasTy::new_from_args(relation.cx(), kind, args)) } } @@ -226,19 +604,32 @@ impl Relate for ty::AliasConst { b: ty::AliasConst, ) -> RelateResult> { let cx = relation.cx(); - if a.kind != b.kind { - Err(TypeError::ConstMismatch(ExpectedFound::new( + let mismatch = || { + TypeError::ConstMismatch(ExpectedFound::new( Const::new_alias(cx, ty::IsRigid::yes_if_next_solver(cx), a), Const::new_alias(cx, ty::IsRigid::yes_if_next_solver(cx), b), - ))) + )) + }; + let kind = if a.kind == b.kind { + a.kind + } else if let ( + ty::AliasConstKind::EvidenceProjection { projection: a_projection }, + ty::AliasConstKind::EvidenceProjection { projection: b_projection }, + ) = (a.kind, b.kind) + { + match relate_evidence_projection(relation, a_projection, b_projection) { + Ok(projection) => ty::AliasConstKind::EvidenceProjection { projection }, + Err(EvidenceRelateError::Type(error)) => return Err(error), + Err(EvidenceRelateError::DifferentProof) => return Err(mismatch()), + } } else { - // FIXME(mgca): remove this - debug_assert_eq!(a.type_of(cx).skip_norm_wip(), b.type_of(cx).skip_norm_wip()); - - let args = relate_args_invariantly(relation, a.args, b.args)?; + return Err(mismatch()); + }; + // FIXME(mgca): remove this + debug_assert_eq!(a.type_of(cx).skip_norm_wip(), b.type_of(cx).skip_norm_wip()); - Ok(ty::AliasConst::new(cx, a.kind, args)) - } + let args = relate_args_invariantly(relation, a.args, b.args)?; + Ok(ty::AliasConst::new(cx, kind, args)) } } @@ -248,29 +639,59 @@ impl Relate for ty::AliasTerm { a: ty::AliasTerm, b: ty::AliasTerm, ) -> RelateResult> { - if a.kind != b.kind { - Err(TypeError::ProjectionMismatched(ExpectedFound::new(a.kind, b.kind))) + let kind = if a.kind == b.kind { + a.kind } else { - let args = match a.kind { - ty::AliasTermKind::OpaqueTy { def_id } => relate_args_with_variances( - relation, - relation.cx().variances_of(def_id.into()), - a.args, - b.args, - )?, - ty::AliasTermKind::ProjectionTy { .. } - | ty::AliasTermKind::FreeConst { .. } - | ty::AliasTermKind::FreeTy { .. } - | ty::AliasTermKind::InherentTy { .. } - | ty::AliasTermKind::InherentConstSelf { .. } - | ty::AliasTermKind::InherentConstImpl { .. } - | ty::AliasTermKind::AnonConst { .. } - | ty::AliasTermKind::ProjectionConst { .. } => { - relate_args_invariantly(relation, a.args, b.args)? - } + let projections = match (a.kind, b.kind) { + ( + ty::AliasTermKind::EvidenceProjectionTy { projection: a_projection }, + ty::AliasTermKind::EvidenceProjectionTy { projection: b_projection }, + ) => Some((a_projection, b_projection, true)), + ( + ty::AliasTermKind::EvidenceProjectionConst { projection: a_projection }, + ty::AliasTermKind::EvidenceProjectionConst { projection: b_projection }, + ) => Some((a_projection, b_projection, false)), + _ => None, }; - Ok(a.with_args(relation.cx(), args)) - } + if let Some((a_projection, b_projection, is_ty)) = projections { + match relate_evidence_projection(relation, a_projection, b_projection) { + Ok(projection) if is_ty => { + ty::AliasTermKind::EvidenceProjectionTy { projection } + } + Ok(projection) => ty::AliasTermKind::EvidenceProjectionConst { projection }, + Err(EvidenceRelateError::Type(error)) => return Err(error), + Err(EvidenceRelateError::DifferentProof) => { + return Err(TypeError::ProjectionMismatched(ExpectedFound::new( + a.kind, b.kind, + ))); + } + } + } else { + return Err(TypeError::ProjectionMismatched(ExpectedFound::new(a.kind, b.kind))); + } + }; + + let args = match kind { + ty::AliasTermKind::OpaqueTy { def_id } => relate_args_with_variances( + relation, + relation.cx().variances_of(def_id.into()), + a.args, + b.args, + )?, + ty::AliasTermKind::ProjectionTy { .. } + | ty::AliasTermKind::EvidenceProjectionTy { .. } + | ty::AliasTermKind::EvidenceProjectionConst { .. } + | ty::AliasTermKind::FreeConst { .. } + | ty::AliasTermKind::FreeTy { .. } + | ty::AliasTermKind::InherentTy { .. } + | ty::AliasTermKind::InherentConstSelf { .. } + | ty::AliasTermKind::InherentConstImpl { .. } + | ty::AliasTermKind::AnonConst { .. } + | ty::AliasTermKind::ProjectionConst { .. } => { + relate_args_invariantly(relation, a.args, b.args)? + } + }; + Ok(ty::AliasTerm::new_from_args(relation.cx(), kind, args)) } } @@ -659,3 +1080,20 @@ impl Relate for ty::TraitClause { Ok(ty::TraitClause { trait_ref, polarity: a.polarity }) } } + +impl Relate for ty::ProjectionClause { + fn relate>( + relation: &mut R, + a: ty::ProjectionClause, + b: ty::ProjectionClause, + ) -> RelateResult> { + let projection_term = relation.relate(a.projection_term, b.projection_term)?; + let term = relation.relate_with_variance( + ty::Invariant, + VarianceDiagInfo::default(), + a.term, + b.term, + )?; + Ok(ty::ProjectionClause { projection_term, term }) + } +} diff --git a/compiler/rustc_type_ir/src/serialize.rs b/compiler/rustc_type_ir/src/serialize.rs index 835383b101136..d6a66c670fcb8 100644 --- a/compiler/rustc_type_ir/src/serialize.rs +++ b/compiler/rustc_type_ir/src/serialize.rs @@ -1,6 +1,5 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use crate::inherent::*; use crate::visit::TypeVisitable; use crate::{self as ty, Interner, Region, RegionKind, UnsafeBinderInner}; @@ -17,46 +16,11 @@ pub trait InternerDecoder: Decoder { fn interner(&self) -> Self::Interner; } -macro_rules! impl_binder_encode_decode { - ($($t:ty),+ $(,)?) => { - $( - impl rustc_serialize::Encodable for ty::Binder - where - $t: rustc_serialize::Encodable, - I::BoundVarKinds: rustc_serialize::Encodable, - { - fn encode(&self, e: &mut E) { - self.bound_vars().encode(e); - self.as_ref().skip_binder().encode(e); - } - } - impl rustc_serialize::Decodable for ty::Binder - where - $t: TypeVisitable + rustc_serialize::Decodable, - I::BoundVarKinds: rustc_serialize::Decodable, - { - fn decode(decoder: &mut D) -> Self { - let bound_vars = rustc_serialize::Decodable::decode(decoder); - ty::Binder::bind_with_vars(rustc_serialize::Decodable::decode(decoder), bound_vars) - } - } - )* - } -} - -impl_binder_encode_decode! { - ty::FnSig, - ty::FnSigTys, - ty::TraitClause, - ty::ExistentialPredicate, - ty::TraitRef, - ty::ExistentialTraitRef, - ty::HostEffectClause, -} - -impl, I: Interner, E: Encoder> Encodable for ty::Binder +// Every binder uses the same wire representation: its ordered declarations, +// followed by its payload. Keep this generic so a closed evidence projection +// can cross MIR and metadata boundaries without losing its region binder. +impl, E: Encoder> Encodable for ty::Binder where - T: Encodable, I::BoundVarKinds: Encodable, { fn encode(&self, e: &mut E) { @@ -65,9 +29,8 @@ where } } -impl, I: Interner, D: Decoder> Decodable for ty::Binder +impl + Decodable, D: Decoder> Decodable for ty::Binder where - T: TypeVisitable + Decodable, I::BoundVarKinds: Decodable, { fn decode(decoder: &mut D) -> Self { diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 88d2184ed95b2..69629faa509af 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -3,6 +3,7 @@ pub mod inspect; use std::convert::Infallible; use std::fmt::Debug; use std::hash::Hash; +use std::ops::Deref; use derive_where::derive_where; #[cfg(feature = "nightly")] @@ -18,8 +19,10 @@ use crate::lang_items::SolverTraitLangItem; use crate::region_constraint::RegionConstraint; use crate::search_graph::PathKind; use crate::{ - self as ty, Canonical, CanonicalVarValues, CantBeErased, ConstVid, FloatVid, GenericArgKind, - InferConst, IntVid, Interner, TermKind, TyVid, TypingMode, Upcast, + self as ty, BoundEvidence, BoundVarIndexKind, Canonical, CanonicalVarValues, CantBeErased, + ConstVid, FallibleTypeFolder, FloatVid, GenericArgKind, InferConst, IntVid, Interner, + PlaceholderEvidence, TermKind, TyVid, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, + TypingMode, Upcast, }; pub type CanonicalInputData = @@ -551,6 +554,8 @@ pub enum CandidateSource { impl Eq for CandidateSource {} #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(StableHash, Encodable_NoContext, Decodable_NoContext))] pub enum ParamEnvSource { /// Preferred eagerly. NonGlobal, @@ -558,8 +563,146 @@ pub enum ParamEnvSource { Global, } +/// Source identity shared by the principal trait clause and every associated +/// equality written in the same HIR trait bound. +/// +/// The local id is scoped by `owner`; it is never interpreted without that +/// owner and therefore remains stable across clause elaboration and reordering. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct ItemContractKey { + pub owner: I::DefId, + #[lift(identity)] + pub hir_local_id: u32, +} + +impl Eq for ItemContractKey {} + +/// One source contract instantiated with all early arguments of its owner. +/// +/// Keeping the complete substitution in the identity prevents two inherited +/// uses of the same source bound from sharing proof identity accidentally. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct InstantiatedItemContract { + pub key: ItemContractKey, + pub complete_early_args: I::GenericArgs, +} + +impl Eq for InstantiatedItemContract {} + +/// A source dictionary contract instantiated in one variable scope. +/// +/// The identity records the source bound and its complete early substitution. +/// The clauses retain the principal trait clause, associated equalities, and +/// host-effect requirements as one bundle. `ordinary_args` records the binder +/// substitution used to open the contract. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct RequiredContract(pub I::BoundRequiredContract); + +impl Eq for RequiredContract {} + +impl Deref for RequiredContract { + type Target = ty::BoundRequiredContractData; + + fn deref(&self) -> &Self::Target { + &*self.0 + } +} + +impl RequiredContract { + pub fn new( + cx: I, + identity: InstantiatedItemContract, + clauses: I::Clauses, + principal_index: u32, + ordinary_args: Option, + ) -> Self { + assert!( + clauses.get(principal_index as usize).is_some(), + "required-contract principal index is out of bounds", + ); + RequiredContract(cx.mk_bound_required_contract(ty::BoundRequiredContractData { + identity, + clauses, + principal_index, + ordinary_args, + })) + } + + pub fn principal_clause(self) -> I::Clause { + self.clauses + .get(self.principal_index as usize) + .expect("required-contract principal index is out of bounds") + } + + pub fn is_principal_clause(self, clause: I::Clause) -> bool { + self.principal_clause() == clause + } +} + +/// Stable semantic identity of one clause in a parameter environment. +/// +/// Origins distinguish positional caller bounds, item-owned clauses, and +/// binder-owned assumptions. Source contracts carry their complete early +/// substitution alongside the source key. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub enum ParamEnvAssumption { + CallerBound { + #[lift(identity)] + index: u32, + }, + /// Clause declared on a concrete item. The owner plus the index in that + /// item's own clause list stays stable when inherited clauses are rebased + /// or a parameter environment is rebuilt with a different prefix. + ItemClause { + owner: I::DefId, + #[lift(identity)] + index: u32, + }, + /// A source-written trait bound and all clauses derived from its single + /// dictionary contract. Unlike `ItemClause`, this identity is independent + /// of clause ordering and survives supertrait elaboration. + ItemContract { contract: InstantiatedItemContract }, + /// Compiler-generated clause attached to an item (for example an RPITIT + /// equality or a const condition). These use a separate index namespace + /// from user-written item clauses. + Generated { + owner: I::DefId, + #[lift(identity)] + index: u32, + }, + Binder { + #[lift(identity)] + telescope_index: u32, + identity: I::Clause, + instantiation: I::GenericArgs, + }, +} + +impl Eq for ParamEnvAssumption {} + #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(StableHash, Encodable_NoContext, Decodable_NoContext))] pub enum AliasBoundKind { /// Alias bound from the self type of a projection SelfBounds, @@ -586,6 +729,578 @@ pub enum BuiltinImplSource { TraitUpcasting(usize), } +/// Data attached to a builtin proof node. +/// +/// Callable proof nodes retain their output and ordinary binder substitution +/// together, preserving the relationship between their arguments and result. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub enum BuiltinEvidence { + /// The builtin rule needs no proof-local data beyond the proven trait ref. + RuleOnly, + /// The output and binder substitution of an `Fn`, `FnMut`, or `FnOnce` + /// proof. The instantiated inputs are in the owning node's trait ref; + /// the substitution also retains variables absent from those inputs. + Fn { output: I::Ty, instantiation: I::GenericArgs }, + /// Awaited output and the input binder substitution of an async callable. + AsyncFn { output: I::Ty, instantiation: I::GenericArgs }, +} + +impl Eq for BuiltinEvidence {} + +/// One call-operation instantiation of an output-only projection bound carried +/// by a trait object. The bound retains its declarations, and `ordinary_args` +/// records their ordinary substitution separately from the owning Dyn evidence. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct DynProjectionOperation { + pub projection_bound: I::Clause, + pub ordinary_args: I::GenericArgs, +} + +impl Eq for DynProjectionOperation {} + +/// Stable identity of the rule selected for one node in a trait proof. +/// +/// Unlike [`CandidateSource`], this also identifies a specific param-env +/// assumption. Its type and const payloads use the enclosing proof's variable +/// scope and participate in substitution with the proven trait reference. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub enum CandidateEvidenceSource { + /// Multiple proof paths were explicitly quotiented by coherence. The + /// selected concrete recipe is the sole nested node of this proof node. + Unique(CoherenceKey), + Impl { + impl_def_id: I::ImplId, + args: I::GenericArgs, + }, + Builtin { + source: BuiltinImplSource, + evidence: BuiltinEvidence, + }, + /// Proof obtained from a bound carried by a trait object. The bound is + /// stored in binder-preserving clause form, with an optional stable vtable slot. + Dyn { + object_bound: I::Clause, + /// The exact ordinary binder substitution, or `None` for a proof + /// independent of every ordinary binder variable. + instantiation: Option, + vtable_slot: Option, + operation: Option>, + }, + ParamEnv { + source: ParamEnvSource, + origin: ParamEnvAssumption, + }, + AliasBound(AliasBoundKind), + /// Error-recovery data, which cannot establish proof identity. + Error, + CoherenceUnknowable, +} + +impl Eq for CandidateEvidenceSource {} + +/// Stable identity used when coherence proves that candidate choice is not +/// semantically observable for a trait ref. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct CoherenceKey { + pub trait_ref: ty::TraitRef, +} + +impl Eq for CoherenceKey {} + +impl CandidateEvidenceSource { + /// Converts source tags that need no additional proof payload. + /// Impl, param-env, and alias-bound tags lack the arguments or origin + /// required by their evidence representation and return `None`. + pub fn from_source(source: CandidateSource) -> Option { + match source { + CandidateSource::Impl(_) + | CandidateSource::ParamEnv(_) + | CandidateSource::AliasBound(_) => None, + CandidateSource::BuiltinImpl(source) => Some(CandidateEvidenceSource::Builtin { + source, + evidence: BuiltinEvidence::RuleOnly, + }), + CandidateSource::CoherenceUnknowable => { + Some(CandidateEvidenceSource::CoherenceUnknowable) + } + } + } +} + +/// One node in a trait proof DAG. +/// +/// `nested` contains stable indices into the owning [`CandidateEvidence`]. A +/// node stores the instantiated trait ref it proves so that consumers can +/// validate that a recipe is not accidentally reused for a different goal. +#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct CandidateEvidenceNode { + pub trait_ref: ty::TraitRef, + pub source: CandidateEvidenceSource, + /// Edges to nodes in the same variable scope. This is used for + /// wrappers such as [`CandidateEvidenceSource::Unique`]. + pub nested: Vec, + /// Interned nested proofs in this node's variable scope. + pub nested_evidence: Vec>, +} + +impl Eq for CandidateEvidenceNode {} + +/// A proof recipe for a trait goal. +/// +/// Nodes in one variable scope use a flat DAG with interned nested proofs. +#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct CandidateEvidence { + pub root: u32, + pub nodes: Vec>, +} + +impl Eq for CandidateEvidence {} + +impl CandidateEvidence { + /// Whether this proof and all of its nested proofs are structural builtin + /// rules which carry no associated value or dictionary choice. + pub fn is_rule_only(&self) -> bool { + self.nodes.iter().all(|node| { + matches!( + node.source, + CandidateEvidenceSource::Builtin { evidence: BuiltinEvidence::RuleOnly, .. } + | CandidateEvidenceSource::Unique(_) + ) && node.nested_evidence.iter().all(CandidateEvidenceUse::is_rule_only) + }) + } + + /// Returns the equivalence classes of proof nodes whose trait refs are one + /// semantic identity. A coherence `Unique` node and the concrete recipe it + /// wraps deliberately repeat that identity in the serialized DAG, but a + /// type folder must not instantiate the repeated occurrences separately. + fn trait_ref_classes(&self) -> Vec { + fn find(parents: &mut [usize], mut index: usize) -> usize { + while parents[index] != index { + let parent = parents[index]; + parents[index] = parents[parent]; + index = parents[index]; + } + index + } + + let mut parents = (0..self.nodes.len()).collect::>(); + for (index, node) in self.nodes.iter().enumerate() { + if matches!(node.source, CandidateEvidenceSource::Unique(_)) { + let nested = usize::try_from(node.nested[0]).expect("proof node index overflow"); + let index_root = find(&mut parents, index); + let nested_root = find(&mut parents, nested); + if index_root != nested_root { + parents[nested_root] = index_root; + } + } + } + for index in 0..parents.len() { + parents[index] = find(&mut parents, index); + } + parents + } +} + +impl TypeFoldable for CandidateEvidence { + fn try_fold_with>(self, folder: &mut F) -> Result { + let classes = self.trait_ref_classes(); + let mut folded_trait_refs = vec![None; self.nodes.len()]; + let mut nodes = Vec::with_capacity(self.nodes.len()); + + for (index, node) in self.nodes.into_iter().enumerate() { + let class = classes[index]; + let trait_ref = match folded_trait_refs[class] { + Some(trait_ref) => trait_ref, + None => { + let trait_ref = node.trait_ref.try_fold_with(folder)?; + folded_trait_refs[class] = Some(trait_ref); + trait_ref + } + }; + let source = match node.source { + CandidateEvidenceSource::Unique(_) => { + CandidateEvidenceSource::Unique(CoherenceKey { trait_ref }) + } + source => source.try_fold_with(folder)?, + }; + nodes.push(CandidateEvidenceNode { + trait_ref, + source, + nested: node.nested, + nested_evidence: node.nested_evidence.try_fold_with(folder)?, + }); + } + + Ok(CandidateEvidence { root: self.root, nodes }) + } + + fn fold_with>(self, folder: &mut F) -> Self { + let classes = self.trait_ref_classes(); + let mut folded_trait_refs = vec![None; self.nodes.len()]; + let mut nodes = Vec::with_capacity(self.nodes.len()); + + for (index, node) in self.nodes.into_iter().enumerate() { + let class = classes[index]; + let trait_ref = match folded_trait_refs[class] { + Some(trait_ref) => trait_ref, + None => { + let trait_ref = node.trait_ref.fold_with(folder); + folded_trait_refs[class] = Some(trait_ref); + trait_ref + } + }; + let source = match node.source { + CandidateEvidenceSource::Unique(_) => { + CandidateEvidenceSource::Unique(CoherenceKey { trait_ref }) + } + source => source.fold_with(folder), + }; + nodes.push(CandidateEvidenceNode { + trait_ref, + source, + nested: node.nested, + nested_evidence: node.nested_evidence.fold_with(folder), + }); + } + + CandidateEvidence { root: self.root, nodes } + } +} + +impl CandidateEvidence { + /// Builds a proof root from existing interned proofs. Nested recipe ordering + /// remains semantically significant, while their handles provide sharing. + pub fn new( + trait_ref: ty::TraitRef, + source: CandidateEvidenceSource, + nested_evidence: impl IntoIterator>, + ) -> Self { + let nested_evidence: Vec<_> = nested_evidence.into_iter().collect(); + let evidence = CandidateEvidence { + root: 0, + nodes: vec![CandidateEvidenceNode { + trait_ref, + source, + nested: vec![], + nested_evidence, + }], + }; + evidence.assert_well_formed(); + evidence + } + + pub fn root_node(&self) -> &CandidateEvidenceNode { + &self.nodes[usize::try_from(self.root).expect("proof node index overflow")] + } + + pub fn root_source(&self) -> CandidateEvidenceSource { + self.root_node().source + } + + /// Returns the concrete proof node selected by this recipe, looking + /// through any coherence `Unique` wrapper. + pub fn selected_node(&self) -> &CandidateEvidenceNode { + let mut index = self.root; + for _ in 0..=self.nodes.len() { + let node = &self.nodes[usize::try_from(index).expect("proof node index overflow")]; + match node.source { + CandidateEvidenceSource::Unique(_) => index = node.nested[0], + _ => return node, + } + } + panic!("cycle while selecting a concrete trait proof node") + } + + /// Returns the concrete rule selected by this recipe, looking through a + /// coherence `Unique` wrapper when present. + pub fn selected_source(&self) -> CandidateEvidenceSource { + self.selected_node().source + } + + /// Wraps this concrete recipe in a coherence-quotiented `Unique` node. + pub fn into_unique(mut self, key: CoherenceKey) -> Self { + self.assert_well_formed(); + assert_eq!( + key.trait_ref, + self.root_node().trait_ref, + "coherence key does not match proof goal" + ); + if matches!(self.root_source(), CandidateEvidenceSource::Unique(existing) if existing == key) + { + return self; + } + let selected = self.root; + self.nodes.push(CandidateEvidenceNode { + trait_ref: key.trait_ref, + source: CandidateEvidenceSource::Unique(key), + nested: vec![selected], + nested_evidence: vec![], + }); + self.root = u32::try_from(self.nodes.len() - 1).expect("too many proof nodes"); + self.assert_well_formed(); + self + } + + /// Validates the node indices and acyclic shape of this proof recipe. + pub fn assert_well_formed(&self) { + assert!(!self.nodes.is_empty(), "empty trait proof recipe"); + let root = usize::try_from(self.root).expect("proof node index overflow"); + assert!(root < self.nodes.len(), "trait proof root is out of bounds"); + for node in &self.nodes { + for evidence in &node.nested_evidence { + // Nested proofs are interned. Avoid expanding shared recipes + // into an exponential tree for structural traits with repeated fields. + evidence.assert_well_formed(); + } + for &nested in &node.nested { + let nested = usize::try_from(nested).expect("proof node index overflow"); + assert!(nested < self.nodes.len(), "nested trait proof node is out of bounds"); + } + match node.source { + CandidateEvidenceSource::Unique(key) => { + assert_eq!(node.trait_ref, key.trait_ref, "invalid coherence proof key"); + assert_eq!(node.nested.len(), 1, "unique proof must wrap one selected recipe"); + assert!( + node.nested_evidence.is_empty(), + "unique wrapper must not own nested proofs" + ); + let selected = &self.nodes + [usize::try_from(node.nested[0]).expect("proof node index overflow")]; + assert_eq!( + node.trait_ref, selected.trait_ref, + "unique proof selected a recipe for a different goal" + ); + } + CandidateEvidenceSource::Impl { .. } + | CandidateEvidenceSource::Builtin { .. } + | CandidateEvidenceSource::Dyn { .. } + | CandidateEvidenceSource::ParamEnv { .. } + | CandidateEvidenceSource::AliasBound(_) + | CandidateEvidenceSource::Error + | CandidateEvidenceSource::CoherenceUnknowable => {} + } + } + + fn visit(index: usize, nodes: &[CandidateEvidenceNode], state: &mut [u8]) { + match state[index] { + 2 => return, + 1 => panic!("non-productive cycle in trait proof DAG"), + 0 => {} + _ => unreachable!(), + } + state[index] = 1; + for &nested in &nodes[index].nested { + visit(usize::try_from(nested).expect("proof node index overflow"), nodes, state); + } + state[index] = 2; + } + + let mut state = vec![0; self.nodes.len()]; + visit(root, &self.nodes, &mut state); + assert!(state.into_iter().all(|state| state == 2), "unreachable node in trait proof DAG"); + } +} + +/// The semantic state of a compiler-internal trait evidence value. +/// +/// A selected proof recipe is only one possible value. Higher-ranked +/// instantiation may instead use a late-bound value or a universe placeholder. Error +/// evidence is explicit so recovery cannot accidentally masquerade as a +/// selected candidate. +#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner)] +#[derive(GenericTypeVisitable)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub enum TraitEvidenceKind { + Selected(CandidateEvidence), + Bound(BoundVarIndexKind, BoundEvidence), + Placeholder(PlaceholderEvidence), + Error(I::ErrorGuaranteed), +} + +impl Eq for TraitEvidenceKind {} + +/// An interned evidence value together with the trait predicate it proves. +/// +/// Keeping `trait_ref` on every state lets evidence-indexed projections remain +/// well-formed even while their proof is unresolved. For `Selected`, the field +/// is required to equal the root node's trait ref and is folded from that root +/// rather than independently, preserving shared inference identity. +#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner)] +#[derive(GenericTypeVisitable)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub struct TraitEvidenceData { + pub trait_ref: ty::TraitRef, + pub kind: TraitEvidenceKind, +} + +impl Eq for TraitEvidenceData {} + +impl TraitEvidenceData { + pub fn selected(recipe: CandidateEvidence) -> Self { + recipe.assert_well_formed(); + let trait_ref = recipe.root_node().trait_ref; + TraitEvidenceData { trait_ref, kind: TraitEvidenceKind::Selected(recipe) } + } + + pub fn bound( + trait_ref: ty::TraitRef, + index: BoundVarIndexKind, + bound: BoundEvidence, + ) -> Self { + TraitEvidenceData { trait_ref, kind: TraitEvidenceKind::Bound(index, bound) } + } + + pub fn placeholder(trait_ref: ty::TraitRef, placeholder: PlaceholderEvidence) -> Self { + TraitEvidenceData { trait_ref, kind: TraitEvidenceKind::Placeholder(placeholder) } + } + + pub fn error(trait_ref: ty::TraitRef, guar: I::ErrorGuaranteed) -> Self { + TraitEvidenceData { trait_ref, kind: TraitEvidenceKind::Error(guar) } + } + + pub fn as_selected(&self) -> Option<&CandidateEvidence> { + match &self.kind { + TraitEvidenceKind::Selected(recipe) => Some(recipe), + TraitEvidenceKind::Bound(..) + | TraitEvidenceKind::Placeholder(_) + | TraitEvidenceKind::Error(_) => None, + } + } + + pub fn into_selected(self) -> Option> { + match self.kind { + TraitEvidenceKind::Selected(recipe) => Some(recipe), + TraitEvidenceKind::Bound(..) + | TraitEvidenceKind::Placeholder(_) + | TraitEvidenceKind::Error(_) => None, + } + } + + pub fn assert_well_formed(&self) { + if let TraitEvidenceKind::Selected(recipe) = &self.kind { + recipe.assert_well_formed(); + assert_eq!( + self.trait_ref, + recipe.root_node().trait_ref, + "selected evidence predicate does not match its proof recipe" + ); + } + } +} + +impl TypeFoldable for TraitEvidenceData { + fn try_fold_with>(self, folder: &mut F) -> Result { + match self.kind { + TraitEvidenceKind::Selected(recipe) => { + Ok(TraitEvidenceData::selected(recipe.try_fold_with(folder)?)) + } + kind => { + Ok(TraitEvidenceData { trait_ref: self.trait_ref.try_fold_with(folder)?, kind }) + } + } + } + + fn fold_with>(self, folder: &mut F) -> Self { + match self.kind { + TraitEvidenceKind::Selected(recipe) => { + TraitEvidenceData::selected(recipe.fold_with(folder)) + } + kind => TraitEvidenceData { trait_ref: self.trait_ref.fold_with(folder), kind }, + } + } +} + +impl TypeVisitable for TraitEvidenceData { + fn visit_with>(&self, visitor: &mut V) -> V::Result { + match &self.kind { + // The recipe root is the authoritative occurrence of the selected + // predicate. Visiting the duplicate field would make a shared + // proof identity appear twice to stateful visitors. + TraitEvidenceKind::Selected(recipe) => recipe.visit_with(visitor), + TraitEvidenceKind::Bound(..) | TraitEvidenceKind::Placeholder(_) => { + self.trait_ref.visit_with(visitor) + } + TraitEvidenceKind::Error(guar) => { + rustc_ast_ir::try_visit!(self.trait_ref.visit_with(visitor)); + visitor.visit_error(*guar) + } + } + } +} + +/// One use of a nested proof recipe. +/// +/// Nested evidence is interned and shares the enclosing proof's variable scope. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic, GenericTypeVisitable)] +#[cfg_attr( + feature = "nightly", + derive(StableHash_NoContext, Encodable_NoContext, Decodable_NoContext) +)] +pub enum CandidateEvidenceUse { + /// A proof in the same variable scope, folded with its parent recipe. + Instantiated(I::TraitEvidence), +} + +impl Eq for CandidateEvidenceUse {} + +impl CandidateEvidenceUse { + pub fn is_rule_only(&self) -> bool { + let evidence = match self { + CandidateEvidenceUse::Instantiated(evidence) => *evidence, + }; + matches!( + &evidence.kind, + TraitEvidenceKind::Selected(recipe) if recipe.is_rule_only() + ) + } + + pub fn assert_well_formed(&self) { + // Each immutable nested recipe is validated when it is interned. Check + // the duplicated root predicate without recursively expanding the DAG. + let Self::Instantiated(evidence) = self; + if let TraitEvidenceKind::Selected(recipe) = &evidence.kind { + assert_eq!(evidence.trait_ref, recipe.root_node().trait_ref); + } + } +} + #[derive_where(Copy, Clone, Debug; I: Interner)] pub enum FetchEligibleAssocItemResponse { Err(I::ErrorGuaranteed), diff --git a/compiler/rustc_type_ir/src/term_kind.rs b/compiler/rustc_type_ir/src/term_kind.rs index ed23b196fe269..3e0c19c14abaf 100644 --- a/compiler/rustc_type_ir/src/term_kind.rs +++ b/compiler/rustc_type_ir/src/term_kind.rs @@ -39,12 +39,19 @@ pub enum AliasTermKind { /// Note that the `def_id` is not the `DefId` of the `TraitRef` containing this /// associated type, which is in `interner.associated_item(def_id).container`, /// aka. `interner.parent(def_id)`. - ProjectionTy { def_id: I::TraitAssocTyId }, + ProjectionTy { + def_id: I::TraitAssocTyId, + }, + EvidenceProjectionTy { + projection: I::EvidenceProjection, + }, /// An associated type in an inherent `impl` /// /// The `def_id` is the `DefId` of the `ImplItem` for the associated type. - InherentTy { def_id: I::InherentAssocTyId }, + InherentTy { + def_id: I::InherentAssocTyId, + }, /// An opaque type (usually from `impl Trait` in type aliases or function return types) /// @@ -54,33 +61,53 @@ pub enum AliasTermKind { /// /// During codegen, `interner.type_of(def_id)` can be used to get the type of the /// underlying type if the type is an opaque. - OpaqueTy { def_id: I::OpaqueTyId }, + OpaqueTy { + def_id: I::OpaqueTyId, + }, /// A type alias that actually checks its trait bounds. /// /// Currently only used if the type alias references opaque types. /// Can always be normalized away. - FreeTy { def_id: I::FreeTyAliasId }, + FreeTy { + def_id: I::FreeTyAliasId, + }, /// An anonymous constant. - AnonConst { def_id: I::AnonConstId }, + AnonConst { + def_id: I::AnonConstId, + }, /// A const alias coming from an associated const. - ProjectionConst { def_id: I::TraitAssocConstId }, + ProjectionConst { + def_id: I::TraitAssocConstId, + }, + EvidenceProjectionConst { + projection: I::EvidenceProjection, + }, /// A top level const item not part of a trait or impl. - FreeConst { def_id: I::FreeConstAliasId }, + FreeConst { + def_id: I::FreeConstAliasId, + }, /// An associated const in an inherent `impl`. See [`ty::AliasConstKind::InherentSelf`] for a /// description on the difference between `InherentConstSelf` and `InherentConstImpl`. - InherentConstSelf { def_id: I::InherentAssocConstId }, + InherentConstSelf { + def_id: I::InherentAssocConstId, + }, /// An associated const in an inherent `impl`. See [`ty::AliasConstKind::InherentSelf`] for a /// description on the difference between `InherentConstSelf` and `InherentConstImpl`. - InherentConstImpl { def_id: I::InherentAssocConstId }, + InherentConstImpl { + def_id: I::InherentAssocConstId, + }, } impl AliasTermKind { pub fn descr(self) -> &'static str { match self { - AliasTermKind::ProjectionTy { .. } => "associated type", - AliasTermKind::ProjectionConst { .. } => "associated const", + AliasTermKind::ProjectionTy { .. } | AliasTermKind::EvidenceProjectionTy { .. } => { + "associated type" + } + AliasTermKind::ProjectionConst { .. } + | AliasTermKind::EvidenceProjectionConst { .. } => "associated const", AliasTermKind::InherentTy { .. } => "inherent associated type", AliasTermKind::InherentConstSelf { .. } | AliasTermKind::InherentConstImpl { .. } => { "inherent associated const" @@ -95,6 +122,7 @@ impl AliasTermKind { pub fn is_type(self) -> bool { match self { AliasTermKind::ProjectionTy { .. } + | AliasTermKind::EvidenceProjectionTy { .. } | AliasTermKind::InherentTy { .. } | AliasTermKind::OpaqueTy { .. } | AliasTermKind::FreeTy { .. } => true, @@ -103,13 +131,17 @@ impl AliasTermKind { | AliasTermKind::ProjectionConst { .. } | AliasTermKind::InherentConstSelf { .. } | AliasTermKind::InherentConstImpl { .. } + | AliasTermKind::EvidenceProjectionConst { .. } | AliasTermKind::FreeConst { .. } => false, } } pub fn is_trait_projection(self) -> bool { match self { - AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. } => true, + AliasTermKind::ProjectionTy { .. } + | AliasTermKind::EvidenceProjectionTy { .. } + | AliasTermKind::ProjectionConst { .. } + | AliasTermKind::EvidenceProjectionConst { .. } => true, AliasTermKind::InherentTy { .. } | AliasTermKind::OpaqueTy { .. } | AliasTermKind::FreeTy { .. } @@ -125,6 +157,9 @@ impl From> for AliasTermKind { fn from(value: ty::AliasTyKind) -> Self { match value { ty::Projection { def_id } => AliasTermKind::ProjectionTy { def_id }, + ty::EvidenceProjection { projection } => { + AliasTermKind::EvidenceProjectionTy { projection } + } ty::Opaque { def_id } => AliasTermKind::OpaqueTy { def_id }, ty::Free { def_id } => AliasTermKind::FreeTy { def_id }, ty::Inherent { def_id } => AliasTermKind::InherentTy { def_id }, @@ -142,6 +177,9 @@ impl From> for AliasTermKind { ty::AliasConstKind::InherentImpl { def_id } => { AliasTermKind::InherentConstImpl { def_id } } + ty::AliasConstKind::EvidenceProjection { projection } => { + AliasTermKind::EvidenceProjectionConst { projection } + } ty::AliasConstKind::Free { def_id } => AliasTermKind::FreeConst { def_id }, ty::AliasConstKind::Anon { def_id } => AliasTermKind::AnonConst { def_id }, } @@ -182,6 +220,9 @@ impl AliasTerm { pub fn expect_ty(self) -> ty::AliasTy { let kind = match self.kind { AliasTermKind::ProjectionTy { def_id } => ty::AliasTyKind::Projection { def_id }, + AliasTermKind::EvidenceProjectionTy { projection } => { + ty::AliasTyKind::EvidenceProjection { projection } + } AliasTermKind::InherentTy { def_id } => ty::AliasTyKind::Inherent { def_id }, AliasTermKind::OpaqueTy { def_id } => ty::AliasTyKind::Opaque { def_id }, AliasTermKind::FreeTy { def_id } => ty::AliasTyKind::Free { def_id }, @@ -189,7 +230,8 @@ impl AliasTerm { | AliasTermKind::InherentConstImpl { .. } | AliasTermKind::FreeConst { .. } | AliasTermKind::AnonConst { .. } - | AliasTermKind::ProjectionConst { .. }) => { + | AliasTermKind::ProjectionConst { .. } + | AliasTermKind::EvidenceProjectionConst { .. }) => { panic!("Cannot turn `{}` into `AliasTy`", kind.descr()) } }; @@ -207,7 +249,11 @@ impl AliasTerm { AliasTermKind::FreeConst { def_id } => ty::AliasConstKind::Free { def_id }, AliasTermKind::AnonConst { def_id } => ty::AliasConstKind::Anon { def_id }, AliasTermKind::ProjectionConst { def_id } => ty::AliasConstKind::Projection { def_id }, + AliasTermKind::EvidenceProjectionConst { projection } => { + ty::AliasConstKind::EvidenceProjection { projection } + } kind @ (AliasTermKind::ProjectionTy { .. } + | AliasTermKind::EvidenceProjectionTy { .. } | AliasTermKind::InherentTy { .. } | AliasTermKind::OpaqueTy { .. } | AliasTermKind::FreeTy { .. }) => { @@ -238,7 +284,13 @@ impl AliasTerm { AliasTermKind::ProjectionConst { def_id } => { alias_const(ty::AliasConstKind::Projection { def_id }) } + AliasTermKind::EvidenceProjectionConst { projection } => { + alias_const(ty::AliasConstKind::EvidenceProjection { projection }) + } AliasTermKind::ProjectionTy { def_id } => alias_ty(ty::Projection { def_id }), + AliasTermKind::EvidenceProjectionTy { projection } => { + alias_ty(ty::EvidenceProjection { projection }) + } AliasTermKind::InherentTy { def_id } => alias_ty(ty::Inherent { def_id }), AliasTermKind::OpaqueTy { def_id } => alias_ty(ty::Opaque { def_id }), AliasTermKind::FreeTy { def_id } => alias_ty(ty::Free { def_id }), @@ -252,6 +304,10 @@ impl AliasTerm { pub fn expect_projection_ty_def_id(self) -> I::TraitAssocTyId { match self.kind { AliasTermKind::ProjectionTy { def_id } => def_id, + AliasTermKind::EvidenceProjectionTy { projection } => { + I::TraitAssocTyId::try_from(projection.item_def_id) + .unwrap_or_else(|_| panic!("evidence projection is not an associated type")) + } kind => panic!("expected projection ty, found {kind:?}"), } } @@ -280,11 +336,27 @@ impl AliasTerm { } pub fn self_ty(self) -> I::Ty { - self.debug_assert_has_self(); - self.args.type_at(0) + match self.kind { + AliasTermKind::EvidenceProjectionTy { projection } + | AliasTermKind::EvidenceProjectionConst { projection } => { + projection.trait_ref().self_ty() + } + _ => { + self.debug_assert_has_self(); + self.args.type_at(0) + } + } } pub fn with_replaced_self_ty(self, interner: I, self_ty: I::Ty) -> Self { + assert!( + !matches!( + self.kind, + AliasTermKind::EvidenceProjectionTy { .. } + | AliasTermKind::EvidenceProjectionConst { .. } + ), + "cannot replace Self in an evidence-indexed projection without replacing its proof" + ); self.debug_assert_has_self(); AliasTerm::new( interner, @@ -297,6 +369,8 @@ impl AliasTerm { match self.kind { AliasTermKind::ProjectionTy { def_id } => def_id.into(), AliasTermKind::ProjectionConst { def_id } => def_id.into(), + AliasTermKind::EvidenceProjectionTy { projection } + | AliasTermKind::EvidenceProjectionConst { projection } => projection.item_def_id, kind => panic!("expected projection alias, found {kind:?}"), } } @@ -310,7 +384,27 @@ impl AliasTerm { /// then this function would return a `T: StreamingIterator` trait reference and /// `['a]` as the own args. pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef, I::GenericArgsSlice) { - interner.trait_ref_and_own_args_for_alias(self.expect_projection_def_id(), self.args) + match self.kind { + AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. } => interner + .trait_ref_and_own_args_for_alias(self.expect_projection_def_id(), self.args), + AliasTermKind::EvidenceProjectionTy { projection } + | AliasTermKind::EvidenceProjectionConst { projection } => { + (projection.trait_ref(), interner.generic_args_slice(self.args)) + } + _ => panic!("expected projection alias"), + } + } + + /// Returns the trait arguments followed by the associated item's own + /// arguments, regardless of whether this is a surface or elaborated + /// projection. + pub fn full_args(self, interner: I) -> I::GenericArgs { + match self.kind { + AliasTermKind::EvidenceProjectionTy { projection } + | AliasTermKind::EvidenceProjectionConst { projection } => interner + .mk_args_from_iter(projection.trait_ref().args.iter().chain(self.args.iter())), + _ => self.args, + } } /// Extracts the underlying trait reference from this projection. diff --git a/compiler/rustc_type_ir/src/ty/alias.rs b/compiler/rustc_type_ir/src/ty/alias.rs index 10fd4c6a25394..8870b83ea9d0c 100644 --- a/compiler/rustc_type_ir/src/ty/alias.rs +++ b/compiler/rustc_type_ir/src/ty/alias.rs @@ -7,6 +7,31 @@ use rustc_type_ir_macros::{ use crate::{AliasConstKind, AliasTermKind, AliasTyKind, Interner}; +/// Interned identity of an associated type or const projection whose trait +/// proof has already been elaborated. +/// +/// The associated item's own generic arguments remain in [`Alias::args`]. +/// Trait and `Self` arguments are obtained exclusively from `evidence`, so the +/// projection cannot drift to a different trait candidate during normalization. +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] +#[cfg_attr( + feature = "nightly", + derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) +)] +pub struct EvidenceProjectionData { + pub item_def_id: I::TraitAssocTermId, + pub evidence: I::TraitEvidence, +} + +impl Eq for EvidenceProjectionData {} + +impl EvidenceProjectionData { + pub fn trait_ref(self) -> crate::TraitRef { + self.evidence.trait_ref + } +} + /// Represents an alias of a type, constant, or other term-like item. /// /// * For a projection, this would be `>::N<...>`. diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 248ba00348749..6acfc84b63f8a 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -43,6 +43,11 @@ pub enum AliasTyKind { /// aka. `interner.parent(def_id)`. Projection { def_id: I::TraitAssocTyId }, + /// An elaborated associated type projection indexed by the exact trait + /// proof selected for it. `AliasTy::args` contains only the associated + /// item's own generic arguments. + EvidenceProjection { projection: I::EvidenceProjection }, + /// An associated type in an inherent `impl` /// /// The `def_id` is the `DefId` of the `ImplItem` for the associated type. @@ -69,7 +74,9 @@ pub enum AliasTyKind { impl AliasTyKind { pub fn descr(self) -> &'static str { match self { - AliasTyKind::Projection { .. } => "associated type", + AliasTyKind::Projection { .. } | AliasTyKind::EvidenceProjection { .. } => { + "associated type" + } AliasTyKind::Inherent { .. } => "inherent associated type", AliasTyKind::Opaque { .. } => "opaque type", AliasTyKind::Free { .. } => "type alias", @@ -79,6 +86,7 @@ impl AliasTyKind { pub fn try_to_projection(self) -> Option { match self { AliasTyKind::Projection { def_id } => Some(def_id), + AliasTyKind::EvidenceProjection { .. } => None, _ => None, } } @@ -367,6 +375,12 @@ impl TyKind { match self { ty::FnPtr(sig_tys, hdr) => Unnormalized::new_wip(sig_tys.with(hdr)), ty::FnDef(def_id, args) => { + // The semantic signature owns its ordinary late-bound + // variables. The FnDef binder additionally makes its + // dependent telescope visible to type relation, but synthetic + // FnDefs may legitimately omit unused declarations. Preserve + // the signature binder instead of replacing it with the + // function-item binder. interner.fn_sig(def_id).instantiate(interner, args.no_bound_vars().unwrap()) } ty::Error(_) => { @@ -582,10 +596,17 @@ impl ProjectionAliasTy { impl AliasTy { #[track_caller] pub fn self_ty(self) -> I::Ty { - self.args.type_at(0) + match self.kind { + AliasTyKind::EvidenceProjection { projection } => projection.trait_ref().self_ty(), + _ => self.args.type_at(0), + } } pub fn with_replaced_self_ty(self, interner: I, self_ty: I::Ty) -> Self { + assert!( + !matches!(self.kind, AliasTyKind::EvidenceProjection { .. }), + "cannot replace Self in an evidence-indexed projection without replacing its proof" + ); AliasTy::new( interner, self.kind, @@ -594,9 +615,11 @@ impl AliasTy { } pub fn trait_def_id(self, interner: I) -> I::TraitId { - let AliasTyKind::Projection { def_id } = self.kind else { panic!("expected a projection") }; - - interner.projection_parent(def_id.into()) + interner.projection_parent(match self.kind { + AliasTyKind::Projection { def_id } => def_id.into(), + AliasTyKind::EvidenceProjection { projection } => projection.item_def_id, + _ => panic!("expected a projection"), + }) } /// Extracts the underlying trait reference and own args from this projection. @@ -605,9 +628,26 @@ impl AliasTy { /// then this function would return a `T: StreamingIterator` trait reference and /// `['a]` as the own args. pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef, I::GenericArgsSlice) { - let AliasTyKind::Projection { def_id } = self.kind else { panic!("expected a projection") }; + match self.kind { + AliasTyKind::Projection { def_id } => { + interner.trait_ref_and_own_args_for_alias(def_id.into(), self.args) + } + AliasTyKind::EvidenceProjection { projection } => { + (projection.trait_ref(), interner.generic_args_slice(self.args)) + } + _ => panic!("expected a projection"), + } + } - interner.trait_ref_and_own_args_for_alias(def_id.into(), self.args) + /// Returns all arguments needed to instantiate bounds on the associated + /// item. Evidence projections store only item-owned arguments in the alias + /// and recover the trait prefix from their proof recipe. + pub fn full_args(self, interner: I) -> I::GenericArgs { + match self.kind { + AliasTyKind::EvidenceProjection { projection } => interner + .mk_args_from_iter(projection.trait_ref().args.iter().chain(self.args.iter())), + _ => self.args, + } } /// Extracts the underlying trait reference from this projection. diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index 2b9908f4337ef..b16abc3cbd46e 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -549,6 +549,18 @@ impl TypeFolder for FoldEscapingRegions { r } } + + fn fold_trait_evidence(&mut self, evidence: I::TraitEvidence) -> I::TraitEvidence { + if let ty::solve::TraitEvidenceKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), _) = + &evidence.kind + && *debruijn >= self.debruijn + { + panic!( + "region-only closure binder instantiation cannot replace bound evidence: {evidence:?}" + ); + } + self.interner.mk_trait_evidence_data((*evidence).clone().fold_with(self)) + } } #[derive_where(Clone, Copy, PartialEq, Hash, Debug; I: Interner)] diff --git a/compiler/rustc_type_ir/src/universe.rs b/compiler/rustc_type_ir/src/universe.rs index 1f38edd78023d..eaa7bb554c2e9 100644 --- a/compiler/rustc_type_ir/src/universe.rs +++ b/compiler/rustc_type_ir/src/universe.rs @@ -2,6 +2,7 @@ use tracing::{debug, instrument}; use crate::data_structures::HashSet; use crate::inherent::*; +use crate::solve::TraitEvidenceKind; use crate::visit::TypeVisitableExt; use crate::{ ConstKind, InferCtxtLike, InferTy, Interner, Region, RegionKind, TyKind, TypeFoldable, @@ -169,4 +170,14 @@ impl< _ => (), } } + + fn visit_trait_evidence(&mut self, evidence: I::TraitEvidence) { + match &evidence.kind { + TraitEvidenceKind::Placeholder(placeholder) if VISIT_PLACEHOLDER => { + self.max_universe = self.max_universe.max(placeholder.universe()); + evidence.trait_ref.visit_with(self); + } + _ => (*evidence).visit_with(self), + } + } } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 1138d8cf9edad..4f1b760a69c2c 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -51,8 +51,9 @@ use rustc_index::{Idx, IndexVec}; use smallvec::SmallVec; use thin_vec::ThinVec; +use crate::fold::PredicateProxy; use crate::inherent::*; -use crate::{self as ty, Interner, PredicateProxy, Region, TypeFlags}; +use crate::{self as ty, Interner, Region, TypeFlags}; /// This trait is implemented for every type that can be visited, /// providing the skeleton of the traversal. @@ -116,6 +117,25 @@ pub trait TypeVisitor: Sized { c.super_visit_with(self) } + /// Visits one compiler-internal trait evidence value. + /// + /// This is a separate hook because evidence is not a generic argument and + /// its bound, placeholder, selected, and error states need + /// custom handling by binder-aware visitors. + fn visit_trait_evidence(&mut self, evidence: I::TraitEvidence) -> Self::Result { + (*evidence).visit_with(self) + } + + /// Visits the identity-bearing payload of an evidence-indexed projection. + /// + /// Unlike an evidence projection nested in an interned `Ty` or `Const`, an + /// `AliasTerm` stores this payload directly. Giving it a dedicated visitor + /// hook lets flag queries observe the projection itself instead of seeing + /// only the types contained in its proof recipe. + fn visit_evidence_projection(&mut self, projection: I::EvidenceProjection) -> Self::Result { + (*projection).visit_with(self) + } + fn visit_predicate>(&mut self, p: P) -> Self::Result { p.super_visit_with(self) } @@ -379,6 +399,11 @@ pub trait TypeVisitableExt: TypeVisitable { fn has_non_rigid_aliases(&self) -> bool { self.has_type_flags(TypeFlags::HAS_NON_RIGID_ALIAS) } + + /// True if this value contains an evidence-indexed projection. + fn has_evidence_projections(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_EVIDENCE_PROJECTION) + } } impl> TypeVisitableExt for T { @@ -484,6 +509,39 @@ impl TypeVisitor for HasTypeFlagsVisitor { } } + #[inline] + fn visit_trait_evidence(&mut self, evidence: I::TraitEvidence) -> Self::Result { + // Trait evidence uses the existing type-state flags so all generic + // pruning continues to account for unresolved proof values without + // spending another bit in `TypeFlags`. + let evidence_flags = match &evidence.kind { + ty::solve::TraitEvidenceKind::Selected(_) => TypeFlags::empty(), + ty::solve::TraitEvidenceKind::Bound(index, _) => { + let mut flags = TypeFlags::HAS_TY_BOUND; + if matches!(index, ty::BoundVarIndexKind::Canonical) { + flags.insert(TypeFlags::HAS_CANONICAL_BOUND); + } + flags + } + ty::solve::TraitEvidenceKind::Placeholder(_) => TypeFlags::HAS_TY_PLACEHOLDER, + ty::solve::TraitEvidenceKind::Error(_) => TypeFlags::HAS_NON_REGION_ERROR, + }; + if self.flags.intersects(evidence_flags) { + ControlFlow::Break(FoundFlags) + } else { + (*evidence).visit_with(self) + } + } + + #[inline] + fn visit_evidence_projection(&mut self, projection: I::EvidenceProjection) -> Self::Result { + if self.flags.intersects(TypeFlags::HAS_EVIDENCE_PROJECTION) { + ControlFlow::Break(FoundFlags) + } else { + (*projection).visit_with(self) + } + } + #[inline] fn visit_predicate>(&mut self, predicate: P) -> Self::Result { // Note: no `super_visit_with` call. @@ -596,6 +654,18 @@ impl TypeVisitor for HasEscapingVarsVisitor { } } + #[inline] + fn visit_trait_evidence(&mut self, evidence: I::TraitEvidence) -> Self::Result { + if let ty::solve::TraitEvidenceKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), _) = + &evidence.kind + && *debruijn >= self.outer_index + { + ControlFlow::Break(FoundEscapingVars) + } else { + (*evidence).visit_with(self) + } + } + #[inline] fn visit_predicate>(&mut self, predicate: P) -> Self::Result { if predicate.outer_exclusive_binder() > self.outer_index { diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 11c81c47b4cf5..c358edb2fb487 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -14,6 +14,10 @@ type TypeWalkerStack = SmallVec<[::GenericArg; 8]>; /// An iterator for walking the type tree. /// +/// Visits the value's types, lifetimes, and consts, including the trait arguments +/// of evidence projections. Binder declarations and proof recipes are not +/// traversed; use [`ty::TypeVisitor`] to visit those as well. +/// /// It's very easy to produce a deeply /// nested type tree with a lot of /// identical subtrees. In order to work efficiently @@ -108,6 +112,9 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg } ty::Alias(_, alias) => { stack.extend(alias.args.iter().rev()); + if let ty::AliasTyKind::EvidenceProjection { projection } = alias.kind { + stack.extend(projection.trait_ref().args.iter().rev()); + } } ty::Dynamic(obj, lt) => { stack.push(lt.into()); @@ -164,6 +171,9 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()), ty::ConstKind::Alias(_, ct) => { stack.extend(ct.args.iter().rev()); + if let ty::AliasConstKind::EvidenceProjection { projection } = ct.kind { + stack.extend(projection.trait_ref().args.iter().rev()); + } } }, } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7f6392fce691a..f2000933c974e 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2292,12 +2292,29 @@ pub(crate) fn clean_middle_ty<'tcx>( Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None, None)).collect()) } - ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) => { + ty::Alias( + _, + alias_ty @ ty::AliasTy { + kind: ty::Projection { .. } | ty::EvidenceProjection { .. }, + .. + }, + ) => { + let def_id = match alias_ty.kind { + ty::Projection { def_id } => def_id, + ty::EvidenceProjection { projection } => projection.item_def_id, + _ => unreachable!(), + }; + let args = alias_ty.full_args(cx.tcx); if cx.tcx.is_impl_trait_in_trait(def_id) { clean_middle_opaque_bounds(cx, def_id, args) } else { + let projection = ty::AliasTerm::new_from_args( + cx.tcx, + ty::AliasTermKind::ProjectionTy { def_id }, + args, + ); Type::QPath(Box::new(clean_projection( - bound_ty.rebind(alias_ty.into()), + bound_ty.rebind(projection), cx, parent_def_id, ))) @@ -3374,7 +3391,10 @@ fn clean_bound_vars<'tcx>( }) } // FIXME(non_lifetime_binders): Support higher-ranked const parameters. - ty::BoundVariableKind::Const => None, + ty::BoundVariableKind::Const(_) => None, + ty::BoundVariableKind::Evidence(_) => { + bug!("binder evidence cannot be displayed as a generic parameter") + } _ => None, }) .collect() diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 012c4997db9c1..d339d16129183 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -358,6 +358,7 @@ pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { ty::ConstKind::Alias(_, ty::AliasConst { kind, .. }) => { let def_id: DefId = match kind { ty::AliasConstKind::Projection { def_id } => def_id.into(), + ty::AliasConstKind::EvidenceProjection { projection } => projection.item_def_id, ty::AliasConstKind::InherentSelf { def_id } => def_id.into(), ty::AliasConstKind::InherentImpl { def_id } => def_id.into(), ty::AliasConstKind::Free { def_id } => def_id.into(), diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 1bfac8e67748c..064c6267da3c5 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -220,6 +220,14 @@ impl SelfTyHead { Self::of(bound_ty.rebind(alias_ty.self_ty()), tcx, parent) } + ty::Alias( + _, + alias_ty @ ty::AliasTy { kind: ty::EvidenceProjection { projection }, .. }, + ) => { + debug_assert!(!tcx.is_impl_trait_in_trait(projection.item_def_id)); + Self::of(bound_ty.rebind(alias_ty.self_ty()), tcx, parent) + } + ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Inherent { .. }, .. }) => { let alias_ty = bound_ty.rebind(alias_ty); Self::of(alias_ty.map_bound(|ty| ty.self_ty()), tcx, parent) diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 3e2227329e5eb..71c542f71b754 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -948,7 +948,7 @@ impl TyCoercionStability { | ty::Alias( _, ty::AliasTy { - kind: ty::Opaque { .. }, + kind: ty::Opaque { .. } | ty::EvidenceProjection { .. }, .. }, ) diff --git a/tests/ui-fulldeps/auxiliary/dependent-binder-representation-input.rs b/tests/ui-fulldeps/auxiliary/dependent-binder-representation-input.rs new file mode 100644 index 0000000000000..9d7e5d473e3ef --- /dev/null +++ b/tests/ui-fulldeps/auxiliary/dependent-binder-representation-input.rs @@ -0,0 +1,29 @@ +pub trait Family { + type Item; +} + +impl Family for () { + type Item = [u8; N]; +} + +impl Family for bool { + type Item = [bool; N]; +} + +pub trait Other { + type Item; +} + +pub trait Borrowing<'a> { + const VALUE: usize; +} + +impl<'a> Borrowing<'a> for () { + const VALUE: usize = 0; +} + +pub trait ReturnType { + fn method<'a>() -> impl Sized + 'a; +} + +fn main() {} diff --git a/tests/ui-fulldeps/auxiliary/dependent-evidence-codec-input.rs b/tests/ui-fulldeps/auxiliary/dependent-evidence-codec-input.rs new file mode 100644 index 0000000000000..ff251457b7ba2 --- /dev/null +++ b/tests/ui-fulldeps/auxiliary/dependent-evidence-codec-input.rs @@ -0,0 +1,17 @@ +pub trait Family { + type Item; +} + +impl Family for bool { + type Item = u8; +} + +impl Family for (T, T) { + type Item = (T::Item, T::Item); +} + +pub trait Other { + type Item; +} + +fn main() {} diff --git a/tests/ui-fulldeps/dependent-binder-representation.rs b/tests/ui-fulldeps/dependent-binder-representation.rs new file mode 100644 index 0000000000000..d9f3d07fefac1 --- /dev/null +++ b/tests/ui-fulldeps/dependent-binder-representation.rs @@ -0,0 +1,851 @@ +//@ edition: 2021 +//@ run-pass +// ignore-tidy-linelength +//@ run-flags: --sysroot {{sysroot-base}} {{src-base}}/auxiliary/dependent-binder-representation-input.rs +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-stage1 (requires matching sysroot built with in-tree compiler) + +#![feature(rustc_private)] + +extern crate rustc_driver; +extern crate rustc_hir; +extern crate rustc_interface; +extern crate rustc_middle; +extern crate rustc_span; +extern crate rustc_type_ir; + +use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::process::ExitCode; + +use rustc_driver::{Callbacks, Compilation}; +use rustc_hir::def_id::DefId; +use rustc_interface::interface::Compiler; +use rustc_middle::traits::solve::{ + CandidateEvidence, CandidateEvidenceSource, TraitEvidence, TraitEvidenceKind, +}; +use rustc_middle::ty::{ + self, FallibleTypeFolder, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, Upcast, +}; +use rustc_type_ir::relate::{self, Relate, RelateResult, TypeRelation, VarianceDiagInfo}; + +fn main() -> ExitCode { + rustc_driver::catch_with_exit_code(|| { + rustc_driver::run_compiler(&std::env::args().collect::>(), &mut Check); + }) +} + +struct Check; + +impl Callbacks for Check { + fn after_analysis<'tcx>(&mut self, _: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation { + tcx.sess.dcx().abort_if_errors(); + let family = find_item(tcx, "Family"); + let item = associated_item(tcx, family); + let other_item = associated_item(tcx, find_item(tcx, "Other")); + let seven = ty::Const::from_target_usize(tcx, 7); + let trait_ref = family_ref(tcx, family, tcx.types.unit, seven); + let impl_def_id = tcx + .all_impls(family) + .find(|&impl_def_id| { + tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip() == tcx.types.unit + }) + .unwrap(); + let recipe = CandidateEvidence::new( + trait_ref, + CandidateEvidenceSource::Impl { impl_def_id, args: tcx.mk_args(&[seven.into()]) }, + [], + ); + let evidence = tcx.mk_trait_evidence(recipe.clone()); + + test_typed_const(tcx); + test_telescope_projection(tcx, item, evidence); + test_supertrait_telescope(tcx, item, evidence); + test_nested_evidence(tcx, trait_ref); + test_dependent_nested_evidence(tcx, evidence); + test_contract_substitution(tcx, item, evidence); + test_malformed_telescopes(tcx, trait_ref); + test_unused_telescope(tcx, trait_ref); + test_contract_flags(tcx, trait_ref); + test_outlives_components(tcx, item, trait_ref); + test_const_evidence_relation(tcx); + test_projection_own_variance(tcx); + test_proof_relation(tcx, recipe.clone()); + test_constructor_validation(tcx, other_item, recipe, evidence); + Compilation::Stop + } +} + +fn find_item(tcx: TyCtxt<'_>, name: &str) -> DefId { + tcx.hir_crate_items(()) + .free_items() + .map(|item| item.owner_id.to_def_id()) + .find(|&def_id| tcx.opt_item_name(def_id).is_some_and(|item| item.as_str() == name)) + .unwrap() +} + +fn associated_item(tcx: TyCtxt<'_>, trait_id: DefId) -> DefId { + tcx.associated_items(trait_id).in_definition_order().next().unwrap().def_id +} + +fn bound_ty(tcx: TyCtxt<'_>, depth: u32, index: u32) -> ty::Ty<'_> { + ty::Ty::new_bound( + tcx, + ty::DebruijnIndex::from_u32(depth), + ty::BoundTy { var: ty::BoundVar::from_u32(index), kind: ty::BoundTyKind::Anon }, + ) +} + +fn bound_const(tcx: TyCtxt<'_>, depth: u32, index: u32) -> ty::Const<'_> { + ty::Const::new_bound( + tcx, + ty::DebruijnIndex::from_u32(depth), + ty::BoundConst::new(ty::BoundVar::from_u32(index)), + ) +} + +fn family_ref<'tcx>( + tcx: TyCtxt<'tcx>, + family: DefId, + self_ty: ty::Ty<'tcx>, + n: ty::Const<'tcx>, +) -> ty::TraitRef<'tcx> { + ty::TraitRef::new_from_args(tcx, family, tcx.mk_args(&[self_ty.into(), n.into()])) +} + +fn trait_clause(trait_ref: ty::TraitRef<'_>) -> ty::ClauseKind<'_> { + ty::ClauseKind::Trait(ty::TraitClause { trait_ref, polarity: ty::ClausePolarity::Positive }) +} + +fn evidence_entry(trait_ref: ty::TraitRef<'_>) -> ty::BoundVariableKind<'_> { + ty::BoundVariableKind::Evidence(rustc_type_ir::EvidenceVariable::principal(trait_clause( + trait_ref, + ))) +} + +fn bound_evidence<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + depth: u32, + index: u32, +) -> TraitEvidence<'tcx> { + tcx.mk_trait_evidence_kind( + trait_ref, + TraitEvidenceKind::Bound( + ty::BoundVarIndexKind::Bound(ty::DebruijnIndex::from_u32(depth)), + ty::BoundEvidence::new(ty::BoundVar::from_u32(index)), + ), + ) +} + +fn test_typed_const(tcx: TyCtxt<'_>) { + let t = bound_ty(tcx, 0, 0); + let c = bound_const(tcx, 0, 1); + let vars = tcx.mk_bound_variable_kinds(&[ + ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon), + ty::BoundVariableKind::Const(Some(t)), + ]); + let value = ty::Const::from_bool(tcx, true); + let args = tcx.mk_args(&[tcx.types.bool.into(), value.into()]); + let binder = ty::Binder::bind_with_vars(c, vars); + let (result, clauses) = binder.instantiate_with_args_and_telescope_clauses(tcx, args); + assert_eq!(result, value); + assert_eq!(clauses.len(), 1); + assert_eq!(clauses[0].index, 1); + assert_eq!( + clauses[0].clause.kind(), + ty::Binder::dummy(ty::ClauseKind::ConstArgHasType(value, tcx.types.bool)), + ); + assert_eq!( + clauses[0].identity.kind(), + ty::Binder::bind_with_vars(ty::ClauseKind::ConstArgHasType(c, t), vars), + ); + assert_eq!(clauses[0].instantiation, args); + assert!(clauses[0].required_contract.is_none()); + + // A type used only by an inner declaration still belongs to the outer binder. + let inner = ty::Binder::bind_with_vars( + bound_const(tcx, 0, 0), + tcx.mk_bound_variable_kinds(&[ty::BoundVariableKind::Const(Some(bound_ty(tcx, 1, 0)))]), + ); + let outer = ty::Binder::bind_with_vars( + inner, + tcx.mk_bound_variable_kinds(&[ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon)]), + ); + let result = outer.instantiate_with_args(tcx, tcx.mk_args(&[tcx.types.bool.into()])); + assert_eq!(result.bound_vars()[0].const_ty(), Some(tcx.types.bool)); + assert_eq!(result.skip_binder(), bound_const(tcx, 0, 0)); +} + +fn test_telescope_projection<'tcx>(tcx: TyCtxt<'tcx>, item: DefId, evidence: TraitEvidence<'tcx>) { + let t = bound_ty(tcx, 0, 0); + let c = bound_const(tcx, 0, 1); + let trait_ref = family_ref(tcx, evidence.trait_ref.def_id, t, c); + let vars = tcx.mk_bound_variable_kinds(&[ + ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon), + ty::BoundVariableKind::Const(Some(tcx.types.usize)), + evidence_entry(trait_ref), + ]); + let projection = tcx.mk_evidence_projection(ty::EvidenceProjectionData { + item_def_id: item, + evidence: bound_evidence(tcx, trait_ref, 0, 2), + }); + let alias = + ty::AliasTy::new_from_args(tcx, ty::EvidenceProjection { projection }, tcx.mk_args(&[])); + let binder = ty::Binder::bind_with_vars(alias, vars); + let args = evidence.trait_ref.args; + let (alias, clauses) = binder.instantiate_with_args_and_evidence_and_telescope_clauses( + tcx, + args, + tcx.mk_trait_evidences(&[evidence]), + ); + + // The evidence slot is outside GenericArgs; the projection recovers Self and N from it. + assert_eq!(binder.ordinary_bound_var_count(), 2); + assert!(alias.args.is_empty()); + assert_eq!(alias.full_args(tcx), args); + let walked: Vec<_> = ty::Ty::new_alias(tcx, ty::IsRigid::Yes, alias).walk().collect(); + assert!(walked.contains(&args.type_at(0).into())); + assert!(walked.contains(&args.const_at(1).into())); + assert_eq!(alias.trait_ref(tcx), evidence.trait_ref); + let ty::EvidenceProjection { projection } = alias.kind else { panic!() }; + assert_eq!(projection.evidence, evidence); + assert_eq!(projection.item_def_id, item); + assert_eq!(tcx.trait_of_assoc(item), Some(evidence.trait_ref.def_id)); + assert_eq!(clauses.iter().map(|clause| clause.index).collect::>(), [1, 2]); + let expected = [ + ty::ClauseKind::ConstArgHasType(args.const_at(1), tcx.types.usize), + trait_clause(evidence.trait_ref), + ]; + let identities = [ty::ClauseKind::ConstArgHasType(c, tcx.types.usize), trait_clause(trait_ref)]; + for ((clause, expected), identity) in clauses.iter().zip(expected).zip(identities) { + assert_eq!(clause.clause.kind(), ty::Binder::dummy(expected)); + assert_eq!(clause.identity.kind(), ty::Binder::bind_with_vars(identity, vars)); + assert_eq!(clause.instantiation, args); + assert!(clause.required_contract.is_none()); + } +} + +fn test_supertrait_telescope<'tcx>(tcx: TyCtxt<'tcx>, item: DefId, evidence: TraitEvidence<'tcx>) { + let family = evidence.trait_ref.def_id; + let n = evidence.trait_ref.args.const_at(1); + let identity_args = ty::GenericArgs::identity_for_item(tcx, family); + let ordinary = ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon); + let project = |evidence: TraitEvidence<'tcx>| { + let projection = + tcx.mk_evidence_projection(ty::EvidenceProjectionData { item_def_id: item, evidence }); + ty::AliasTy::new_from_args(tcx, ty::EvidenceProjection { projection }, tcx.mk_args(&[])) + }; + let projection_ty = |evidence| ty::Ty::new_alias(tcx, ty::IsRigid::No, project(evidence)); + + let pred_ref = family_ref(tcx, family, bound_ty(tcx, 0, 0), identity_args.const_at(1)); + let pred_vars = tcx.mk_bound_variable_kinds(&[ordinary, evidence_entry(pred_ref)]); + let pred: ty::Clause<'tcx> = ty::Binder::bind_with_vars( + ty::ClauseKind::Projection(ty::ProjectionClause { + projection_term: project(bound_evidence(tcx, pred_ref, 0, 1)).into(), + term: identity_args.type_at(0).into(), + }), + pred_vars, + ) + .upcast(tcx); + + let bool_ref = family_ref(tcx, family, tcx.types.bool, n); + let bool_impl = tcx + .all_impls(family) + .find(|&impl_def_id| { + tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip() == tcx.types.bool + }) + .unwrap(); + let bool_evidence = tcx.mk_trait_evidence(CandidateEvidence::new( + bool_ref, + CandidateEvidenceSource::Impl { impl_def_id: bool_impl, args: tcx.mk_args(&[n.into()]) }, + [], + )); + let trait_evidence_ref = family_ref(tcx, family, bound_ty(tcx, 0, 0), n); + let merged_pred_ref = family_ref(tcx, family, bound_ty(tcx, 0, 1), n); + + // Exercise both the ordinary supertrait path and the merge of two evidence suffixes. + for trait_has_evidence in [false, true] { + let mut trait_vars = vec![ordinary]; + let trait_self = if trait_has_evidence { + trait_vars.push(evidence_entry(trait_evidence_ref)); + projection_ty(bound_evidence(tcx, trait_evidence_ref, 0, 1)) + } else { + bound_ty(tcx, 0, 0) + }; + let trait_ref = ty::Binder::bind_with_vars( + family_ref(tcx, family, trait_self, n), + tcx.mk_bound_variable_kinds(&trait_vars), + ); + let merged = pred.instantiate_supertrait(tcx, trait_ref).as_projection_clause().unwrap(); + + // [T, TE] and [P, PE] become [T, P, TE, PE], not [T, TE, P, PE]. + // The predicate's N is an early parameter and must also be substituted in PE. + let mut expected_vars = vec![ordinary, ordinary]; + let mut evidence_args = vec![]; + let mut expected_clauses = vec![]; + let (merged_term, instantiated_term) = if trait_has_evidence { + expected_vars.push(evidence_entry(trait_evidence_ref)); + evidence_args.push(evidence); + expected_clauses.push(trait_clause(evidence.trait_ref)); + (projection_ty(bound_evidence(tcx, trait_evidence_ref, 0, 2)), projection_ty(evidence)) + } else { + (bound_ty(tcx, 0, 0), tcx.types.unit) + }; + let pred_evidence_index = if trait_has_evidence { 3 } else { 2 }; + expected_vars.push(evidence_entry(merged_pred_ref)); + evidence_args.push(bool_evidence); + expected_clauses.push(trait_clause(bool_ref)); + assert_eq!(merged.bound_vars(), tcx.mk_bound_variable_kinds(&expected_vars)); + assert_eq!(merged.ordinary_bound_var_count(), 2); + assert_eq!( + merged.skip_binder(), + ty::ProjectionClause { + projection_term: project(bound_evidence( + tcx, + merged_pred_ref, + 0, + pred_evidence_index, + )) + .into(), + term: merged_term.into(), + }, + ); + + let args = tcx.mk_args(&[tcx.types.unit.into(), tcx.types.bool.into()]); + let (instantiated, clauses) = merged + .instantiate_with_args_and_evidence_and_telescope_clauses( + tcx, + args, + tcx.mk_trait_evidences(&evidence_args), + ); + assert_eq!( + instantiated, + ty::ProjectionClause { + projection_term: project(bool_evidence).into(), + term: instantiated_term.into(), + }, + ); + assert_eq!(clauses.len(), expected_clauses.len()); + for (index, (clause, expected)) in clauses.iter().zip(expected_clauses).enumerate() { + assert_eq!(clause.index, index as u32 + 2); + assert_eq!(clause.clause.kind(), ty::Binder::dummy(expected)); + assert_eq!(clause.instantiation, args); + } + } +} + +fn test_nested_evidence<'tcx>(tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>) { + let vars = tcx.mk_bound_variable_kinds(&[evidence_entry(trait_ref)]); + let at = |depth| bound_evidence(tcx, trait_ref, depth, 0); + let inner = ty::Binder::bind_with_vars((at(0), at(1), at(2)), vars); + let shifted = ty::shift_vars(tcx, inner, 1); + assert_eq!(shifted.bound_vars(), vars); + assert_eq!(shifted.skip_binder(), (at(0), at(2), at(3))); + + // Removing the outer binder shifts the replacement beneath the surviving inner binder. + let outer = ty::Binder::bind_with_vars(inner, vars); + let result = outer.instantiate_with_args_and_evidence( + tcx, + tcx.mk_args(&[]), + tcx.mk_trait_evidences(&[at(1)]), + ); + assert_eq!(result.bound_vars(), vars); + assert_eq!(result.skip_binder(), (at(0), at(2), at(1))); + let result = result.instantiate_with_args_and_evidence( + tcx, + tcx.mk_args(&[]), + tcx.mk_trait_evidences(&[at(0)]), + ); + assert_eq!(result, (at(0), at(1), at(0))); +} + +fn test_dependent_nested_evidence<'tcx>(tcx: TyCtxt<'tcx>, evidence: TraitEvidence<'tcx>) { + let trait_ref = family_ref( + tcx, + evidence.trait_ref.def_id, + bound_ty(tcx, 0, 0), + evidence.trait_ref.args.const_at(1), + ); + let vars = tcx.mk_bound_variable_kinds(&[ + ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon), + evidence_entry(trait_ref), + ]); + let inner = ty::Binder::bind_with_vars( + bound_evidence(tcx, ty::shift_vars(tcx, trait_ref, 1), 1, 1), + tcx.mk_bound_variable_kinds(&[]), + ); + let _ = inner.visit_with(&mut ty::ValidateBoundVars::new(vars)); + let outer = ty::Binder::bind_with_vars(inner, vars); + let result = outer.instantiate_with_args_and_evidence( + tcx, + tcx.mk_args(&[tcx.types.unit.into()]), + tcx.mk_trait_evidences(&[evidence]), + ); + assert_eq!(result.skip_binder(), evidence); + + let wrong_ref = + family_ref(tcx, trait_ref.def_id, tcx.types.bool, evidence.trait_ref.args.const_at(1)); + assert_rejected(|| { + let wrong_inner = ty::Binder::bind_with_vars( + bound_evidence(tcx, wrong_ref, 1, 1), + tcx.mk_bound_variable_kinds(&[]), + ); + ty::Binder::bind_with_vars(wrong_inner, vars).instantiate_with_args_and_evidence( + tcx, + tcx.mk_args(&[tcx.types.unit.into()]), + tcx.mk_trait_evidences(&[evidence]), + ); + }); +} + +fn test_contract_substitution<'tcx>(tcx: TyCtxt<'tcx>, item: DefId, evidence: TraitEvidence<'tcx>) { + let ordinary_args = tcx.mk_args(&[tcx.types.unit.into()]); + let trait_ref = family_ref( + tcx, + evidence.trait_ref.def_id, + bound_ty(tcx, 0, 0), + evidence.trait_ref.args.const_at(1), + ); + let principal: ty::Clause<'tcx> = ty::Binder::bind_with_vars( + trait_clause(ty::shift_vars(tcx, trait_ref, 1)), + tcx.mk_bound_variable_kinds(&[]), + ) + .upcast(tcx); + let equality: ty::Clause<'tcx> = ty::Binder::bind_with_vars( + ty::ClauseKind::Projection(ty::ProjectionClause { + projection_term: ty::AliasTy::new_from_args( + tcx, + ty::Projection { def_id: item }, + ty::shift_vars(tcx, trait_ref.args, 1), + ) + .into(), + term: tcx.types.bool.into(), + }), + tcx.mk_bound_variable_kinds(&[]), + ) + .upcast(tcx); + let data = ty::BoundRequiredContractData { + identity: ty::solve::InstantiatedItemContract { + key: ty::solve::ItemContractKey { owner: trait_ref.def_id, hir_local_id: 1 }, + complete_early_args: tcx.mk_args(&[bound_ty(tcx, 0, 0).into()]), + }, + clauses: tcx.mk_clauses(&[principal, equality]), + principal_index: 0, + ordinary_args: None, + }; + let make_binder = |data| { + ty::Binder::bind_with_vars( + bound_evidence(tcx, trait_ref, 0, 1), + tcx.mk_bound_variable_kinds(&[ + ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon), + ty::BoundVariableKind::Evidence(ty::EvidenceVariable { + clause: trait_clause(trait_ref), + required_contract: Some(tcx.mk_bound_required_contract(data)), + }), + ]), + ) + }; + let binder = make_binder(data); + let evidence_args = tcx.mk_trait_evidences(&[evidence]); + assert_rejected(|| { + binder.instantiate_with_args_and_evidence(tcx, ordinary_args, evidence_args); + }); + let (result, clauses) = binder.instantiate_with_args_and_evidence_and_telescope_clauses( + tcx, + ordinary_args, + evidence_args, + ); + assert_eq!(result, evidence); + assert_eq!(clauses.len(), 1); + let contract = clauses[0].required_contract.unwrap(); + assert_eq!(contract.principal_clause(), clauses[0].clause); + assert_eq!(contract.clauses.len(), 2); + assert_eq!(contract.identity.complete_early_args, ordinary_args); + assert_eq!(contract.ordinary_args, Some(ordinary_args)); + let equality = contract.clauses[1].as_projection_clause().unwrap().no_bound_vars().unwrap(); + assert_eq!(equality.projection_term.full_args(tcx), evidence.trait_ref.args); + assert_eq!(equality.term, tcx.types.bool.into()); + + for malformed in [ + ty::BoundRequiredContractData { clauses: tcx.mk_clauses(&[]), ..data }, + ty::BoundRequiredContractData { principal_index: 2, ..data }, + ty::BoundRequiredContractData { principal_index: 1, ..data }, + ] { + assert_rejected(|| { + tcx.mk_bound_required_contract(malformed); + }); + } + let wrong_principal: ty::Clause<'tcx> = + ty::Binder::dummy(trait_clause(evidence.trait_ref)).upcast(tcx); + assert_rejected(|| { + make_binder(ty::BoundRequiredContractData { + clauses: tcx.mk_clauses(&[wrong_principal]), + ..data + }) + .instantiate_with_args_and_evidence_and_telescope_clauses( + tcx, + ordinary_args, + evidence_args, + ); + }); + let contract = tcx.mk_bound_required_contract(data); + assert_eq!(contract.try_fold_with(&mut RejectBool(tcx)), Err(())); +} + +struct RejectBool<'tcx>(TyCtxt<'tcx>); + +impl<'tcx> FallibleTypeFolder> for RejectBool<'tcx> { + type Error = (); + + fn cx(&self) -> TyCtxt<'tcx> { + self.0 + } + + fn try_fold_ty(&mut self, ty: ty::Ty<'tcx>) -> Result, ()> { + if ty.is_bool() { Err(()) } else { Ok(ty) } + } +} + +fn test_malformed_telescopes<'tcx>(tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>) { + for (entries, args) in [ + ( + vec![evidence_entry(trait_ref), ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon)], + tcx.mk_args(&[]), + ), + ( + vec![ + ty::BoundVariableKind::Const(Some(bound_ty(tcx, 0, 1))), + ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon), + ], + tcx.mk_args(&[ty::Const::from_bool(tcx, true).into(), tcx.types.bool.into()]), + ), + ] { + assert_rejected(|| { + ty::Binder::bind_with_vars(tcx.types.unit, tcx.mk_bound_variable_kinds(&entries)) + .instantiate_with_args_and_telescope_clauses(tcx, args); + }); + } +} + +fn test_proof_relation<'tcx>(tcx: TyCtxt<'tcx>, recipe: CandidateEvidence>) { + let trait_ref = recipe.root_node().trait_ref; + let original = tcx.mk_trait_evidence(recipe.clone()); + let mut shortened = recipe.clone(); + let CandidateEvidenceSource::Impl { impl_def_id, args } = shortened.root_source() else { + panic!() + }; + assert!(!args.is_empty()); + shortened.nodes[0].source = CandidateEvidenceSource::Impl { + impl_def_id, args: tcx.mk_args(&[]), + }; + assert!(EraseRegions::new(tcx).evidences(tcx.mk_trait_evidence(shortened), original).is_err()); + let key = ty::solve::CoherenceKey { trait_ref }; + let different = CandidateEvidence::new( + trait_ref, + CandidateEvidenceSource::ParamEnv { + source: ty::solve::ParamEnvSource::NonGlobal, + origin: ty::solve::ParamEnvAssumption::CallerBound { index: 0 }, + }, + [], + ); + let a = tcx.mk_trait_evidence(recipe.into_unique(key)); + let b = tcx.mk_trait_evidence(different.clone().into_unique(key)); + assert!(EraseRegions::new(tcx).evidences(a, b).is_err()); + assert!(EraseRegions::new(tcx).evidences(b, tcx.mk_trait_evidence(different)).is_err()); + let object_bound: ty::Clause<'tcx> = ty::Binder::bind_with_vars( + trait_clause(trait_ref), + tcx.mk_bound_variable_kinds(&[ty::BoundVariableKind::Region(ty::BoundRegionKind::Anon)]), + ) + .upcast(tcx); + let dyn_evidence = |instantiation| { + tcx.mk_trait_evidence(CandidateEvidence::new( + trait_ref, + CandidateEvidenceSource::Dyn { + object_bound, + instantiation, + vtable_slot: None, + operation: None, + }, + [], + )) + }; + let uninstantiated = dyn_evidence(None); + let instantiated = dyn_evidence(Some(tcx.mk_args(&[tcx.lifetimes.re_static.into()]))); + assert!(EraseRegions::new(tcx).evidences(uninstantiated, instantiated).is_err()); +} + +fn test_unused_telescope<'tcx>(tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>) { + for entry in [ty::BoundVariableKind::Const(Some(tcx.types.bool)), evidence_entry(trait_ref)] { + let binder = + ty::Binder::bind_with_vars(tcx.types.unit, tcx.mk_bound_variable_kinds(&[entry])); + assert!(binder.no_bound_vars().is_none()); + assert_rejected(|| { + tcx.instantiate_bound_regions_with_erased(binder); + }); + assert_rejected(|| { + tcx.replace_bound_vars_uncached( + binder, + ty::FnMutDelegate { + regions: &mut |_| unreachable!(), + types: &mut |_| unreachable!(), + consts: &mut |_| unreachable!(), + }, + ); + }); + } + let plain = ty::Binder::bind_with_vars( + tcx.types.unit, + tcx.mk_bound_variable_kinds(&[ty::BoundVariableKind::Const(None)]), + ); + assert_eq!(plain.no_bound_vars(), Some(tcx.types.unit)); +} + +fn test_contract_flags<'tcx>(tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>) { + let clause: ty::Clause<'tcx> = ty::Binder::dummy(trait_clause(trait_ref)).upcast(tcx); + let contract = tcx.mk_bound_required_contract(ty::BoundRequiredContractData { + identity: ty::solve::InstantiatedItemContract { + key: ty::solve::ItemContractKey { owner: trait_ref.def_id, hir_local_id: 0 }, + complete_early_args: tcx.mk_args(&[]), + }, + clauses: tcx.mk_clauses(&[clause]), + principal_index: 0, + ordinary_args: Some(tcx.mk_args(&[bound_ty(tcx, 1, 0).into()])), + }); + let inner = ty::Binder::bind_with_vars( + tcx.types.unit, + tcx.mk_bound_variable_kinds(&[ty::BoundVariableKind::Evidence(ty::EvidenceVariable { + clause: trait_clause(trait_ref), + required_contract: Some(contract), + })]), + ); + // The only outer reference is in contract metadata, behind an interned type's flags. + let inner_ty = ty::Ty::new_unsafe_binder(tcx, inner); + assert!(inner_ty.has_escaping_bound_vars()); + let outer = ty::Binder::bind_with_vars( + inner_ty, + tcx.mk_bound_variable_kinds(&[ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon)]), + ); + let result = outer.instantiate_with_args(tcx, tcx.mk_args(&[tcx.types.bool.into()])); + assert!(!result.has_escaping_bound_vars()); + let ty::UnsafeBinder(inner) = *result.kind() else { panic!() }; + let ty::BoundVariableKind::Evidence(entry) = inner.bound_vars()[0] else { panic!() }; + assert_eq!(entry.required_contract.unwrap().ordinary_args.unwrap().type_at(0), tcx.types.bool); +} + +fn test_outlives_components<'tcx>(tcx: TyCtxt<'tcx>, item: DefId, trait_ref: ty::TraitRef<'tcx>) { + use rustc_type_ir::outlives::{Component, push_outlives_components}; + + let projection = tcx.mk_evidence_projection(ty::EvidenceProjectionData { + item_def_id: item, + evidence: bound_evidence(tcx, trait_ref, 0, 0), + }); + let alias = + ty::AliasTy::new_from_args(tcx, ty::EvidenceProjection { projection }, tcx.mk_args(&[])); + assert!(alias.has_escaping_bound_vars()); + assert!(!alias.full_args(tcx).has_escaping_bound_vars()); + let projected_ty = ty::Ty::new_alias(tcx, ty::IsRigid::Yes, alias); + let mut components = Default::default(); + push_outlives_components(tcx, projected_ty, &mut components); + assert!(matches!(components.as_slice(), [Component::EscapingAlias(_)])); +} + +fn test_const_evidence_relation<'tcx>(tcx: TyCtxt<'tcx>) { + let borrowing = find_item(tcx, "Borrowing"); + let item = associated_item(tcx, borrowing); + let impl_def_id = tcx.all_impls(borrowing).next().unwrap(); + let alias = |region: ty::Region<'tcx>| { + let trait_ref = ty::TraitRef::new_from_args( + tcx, + borrowing, + tcx.mk_args(&[tcx.types.unit.into(), region.into()]), + ); + let evidence = tcx.mk_trait_evidence(CandidateEvidence::new( + trait_ref, + CandidateEvidenceSource::Impl { impl_def_id, args: tcx.mk_args(&[region.into()]) }, + [], + )); + let projection = + tcx.mk_evidence_projection(ty::EvidenceProjectionData { item_def_id: item, evidence }); + ty::AliasConst::new( + tcx, + ty::AliasConstKind::EvidenceProjection { projection }, + tcx.mk_args(&[]), + ) + }; + let a = alias(tcx.lifetimes.re_static); + let b = alias(tcx.lifetimes.re_erased); + assert_ne!(a, b); + // Distinct interned proofs can relate when their ordinary regions relate. + assert_eq!(EraseRegions::new(tcx).relate(a, b).unwrap(), b); + let const_ty = ty::Const::new_alias(tcx, ty::IsRigid::Yes, a); + let walked: Vec<_> = const_ty.walk().collect(); + assert!(walked.contains(&tcx.types.unit.into())); + assert!(walked.contains(&tcx.lifetimes.re_static.into())); + assert!(matches!( + tcx.const_eval_resolve_for_typeck( + ty::TypingEnv::fully_monomorphized(), + a, + rustc_span::DUMMY_SP, + ), + Err(rustc_middle::mir::interpret::ErrorHandled::TooGeneric(_)) + )); + let ty::AliasConstKind::EvidenceProjection { projection } = a.kind else { panic!() }; + let projection = tcx.mk_evidence_projection(ty::EvidenceProjectionData { + item_def_id: item, + evidence: bound_evidence(tcx, projection.trait_ref(), 0, 0), + }); + let unresolved = ty::AliasConst::new( + tcx, + ty::AliasConstKind::EvidenceProjection { projection }, + tcx.mk_args(&[]), + ); + assert!(matches!( + tcx.const_eval_resolve_for_typeck( + ty::TypingEnv::fully_monomorphized(), + unresolved, + rustc_span::DUMMY_SP, + ), + Err(rustc_middle::mir::interpret::ErrorHandled::TooGeneric(_)) + )); +} + +fn test_projection_own_variance(tcx: TyCtxt<'_>) { + let trait_id = find_item(tcx, "ReturnType"); + let method = associated_item(tcx, trait_id); + let item = tcx.associated_types_for_impl_traits_in_associated_fn(method)[0]; + let trait_ref = + ty::TraitRef::new_from_args(tcx, trait_id, tcx.mk_args(&[tcx.types.unit.into()])); + let evidence = tcx.mk_trait_evidence(CandidateEvidence::new( + trait_ref, + CandidateEvidenceSource::ParamEnv { + source: ty::solve::ParamEnvSource::NonGlobal, + origin: ty::solve::ParamEnvAssumption::CallerBound { index: 0 }, + }, + [], + )); + let projection = + tcx.mk_evidence_projection(ty::EvidenceProjectionData { item_def_id: item, evidence }); + let a = ty::AliasTy::new_from_args( + tcx, + ty::EvidenceProjection { projection }, + tcx.mk_args(&[tcx.lifetimes.re_static.into()]), + ); + let b = ty::AliasTy::new_from_args( + tcx, + ty::EvidenceProjection { projection }, + tcx.mk_args(&[tcx.lifetimes.re_erased.into()]), + ); + let variances = tcx.variances_of(item); + assert_ne!(variances[0], variances[trait_ref.args.len()]); + let mut relation = EraseRegions::new(tcx); + assert_eq!(relation.relate(a, b).unwrap(), b); + assert_eq!(relation.variances, [variances[trait_ref.args.len()]]); +} + +struct EraseRegions<'tcx> { + tcx: TyCtxt<'tcx>, + variances: Vec, +} + +impl<'tcx> EraseRegions<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { tcx, variances: Vec::new() } + } +} + +impl<'tcx> TypeRelation> for EraseRegions<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn tys( + &mut self, + a: ty::Ty<'tcx>, + b: ty::Ty<'tcx>, + ) -> RelateResult, ty::Ty<'tcx>> { + relate::structurally_relate_tys(self, a, b) + } + + fn regions( + &mut self, + _: ty::Region<'tcx>, + _: ty::Region<'tcx>, + ) -> RelateResult, ty::Region<'tcx>> { + Ok(self.tcx.lifetimes.re_erased) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult, ty::Const<'tcx>> { + relate::structurally_relate_consts(self, a, b) + } + + fn relate_with_variance>>( + &mut self, + variance: ty::Variance, + _: VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult, T> { + self.variances.push(variance); + self.relate(a, b) + } + + fn relate_ty_args( + &mut self, + _: ty::Ty<'tcx>, + _: ty::Ty<'tcx>, + _: DefId, + a: ty::GenericArgsRef<'tcx>, + b: ty::GenericArgsRef<'tcx>, + mk: impl FnOnce(ty::GenericArgsRef<'tcx>) -> ty::Ty<'tcx>, + ) -> RelateResult, ty::Ty<'tcx>> { + Ok(mk(relate::relate_args_invariantly(self, a, b)?)) + } + + fn binders>>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult, ty::Binder<'tcx, T>> { + if a == b { Ok(a) } else { Err(ty::error::TypeError::Mismatch) } + } +} + +fn test_constructor_validation<'tcx>( + tcx: TyCtxt<'tcx>, + other_item: DefId, + recipe: CandidateEvidence>, + evidence: TraitEvidence<'tcx>, +) { + let mut out_of_bounds = recipe.clone(); + out_of_bounds.nodes[0].nested.push(1); + assert_rejected(|| { + tcx.mk_trait_evidence(out_of_bounds); + }); + + let mut cyclic = recipe; + cyclic.nodes[0].nested.push(0); + assert_rejected(|| { + tcx.mk_trait_evidence(cyclic); + }); + + assert_rejected(|| { + tcx.mk_evidence_projection(ty::EvidenceProjectionData { + item_def_id: other_item, + evidence, + }); + }); +} + +fn assert_rejected(f: impl FnOnce()) { + // These constructors assert internal invariants before interning their input. + let hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(|_| {})); + let result = catch_unwind(AssertUnwindSafe(f)); + std::panic::set_hook(hook); + assert!(result.is_err()); +} diff --git a/tests/ui-fulldeps/dependent-evidence-codec.rs b/tests/ui-fulldeps/dependent-evidence-codec.rs new file mode 100644 index 0000000000000..445d859655202 --- /dev/null +++ b/tests/ui-fulldeps/dependent-evidence-codec.rs @@ -0,0 +1,593 @@ +//@ edition: 2021 +//@ run-pass +// ignore-tidy-linelength +//@ run-flags: --sysroot {{sysroot-base}} {{src-base}}/auxiliary/dependent-evidence-codec-input.rs +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-stage1 (requires matching sysroot built with in-tree compiler) + +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_data_structures; +extern crate rustc_driver; +extern crate rustc_errors; +extern crate rustc_hir; +extern crate rustc_interface; +extern crate rustc_middle; +extern crate rustc_serialize; +extern crate rustc_span; +extern crate rustc_type_ir; + +use std::cell::Cell; +use std::fmt::Debug; +use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::process::ExitCode; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::stable_hash::{ + RawDefId, RawSpan, StableHash, StableHashControls, StableHashCtxt, StableHasher, +}; +use rustc_driver::{Callbacks, Compilation}; +use rustc_errors::DiagCtxt; +use rustc_errors::emitter::SilentEmitter; +use rustc_hir::def_id::{CrateNum, DefId, DefIndex}; +use rustc_interface::interface::Compiler; +use rustc_middle::ich::StableHashState; +use rustc_middle::mir::interpret::AllocId; +use rustc_middle::traits::solve::{ + BoundRequiredContract, CandidateEvidence, CandidateEvidenceSource, CandidateEvidenceUse, + EvidenceProjection, TraitEvidence, TraitEvidenceData, TraitEvidenceKind, +}; +use rustc_middle::ty::codec::{SHORTHAND_OFFSET, TyDecoder, TyEncoder}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast}; +use rustc_serialize::opaque::mem_encoder::MemEncoder; +use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder}; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use rustc_span::{ + BlobDecoder, ByteSymbol, ExpnId, Span, SpanDecoder, SpanEncoder, Symbol, SyntaxContext, +}; + +fn main() -> ExitCode { + rustc_driver::catch_with_exit_code(|| { + rustc_driver::run_compiler(&std::env::args().collect::>(), &mut Check); + }) +} + +struct Check; + +impl Callbacks for Check { + fn after_analysis<'tcx>(&mut self, _: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation { + tcx.dcx().abort_if_errors(); + let family = find_item(tcx, "Family"); + let item = associated_item(tcx, family); + let other_item = associated_item(tcx, find_item(tcx, "Other")); + let mut impls = tcx.all_impls(family); + let first = impls.next().unwrap(); + let second = impls.next().unwrap(); + let (leaf_impl, pair_impl) = + if tcx.type_of(first).instantiate_identity().skip_norm_wip() == tcx.types.bool { + (first, second) + } else { + (second, first) + }; + let trait_ref = ty::TraitRef::new(tcx, family, [tcx.types.bool]); + let leaf = tcx.mk_trait_evidence(CandidateEvidence::new( + trait_ref, + CandidateEvidenceSource::Impl { impl_def_id: leaf_impl, args: tcx.mk_args(&[]) }, + [], + )); + test_states(tcx, item, leaf); + test_shared_dag(tcx, item, pair_impl, leaf); + test_decode_validation(tcx, other_item, leaf); + Compilation::Stop + } +} + +fn find_item(tcx: TyCtxt<'_>, name: &str) -> DefId { + tcx.hir_crate_items(()) + .free_items() + .map(|item| item.owner_id.to_def_id()) + .find(|&id| tcx.opt_item_name(id).is_some_and(|item| item.as_str() == name)) + .unwrap() +} + +fn associated_item(tcx: TyCtxt<'_>, trait_id: DefId) -> DefId { + tcx.associated_items(trait_id).in_definition_order().next().unwrap().def_id +} + +fn projection<'tcx>( + tcx: TyCtxt<'tcx>, + item: DefId, + evidence: TraitEvidence<'tcx>, +) -> EvidenceProjection<'tcx> { + tcx.mk_evidence_projection(ty::EvidenceProjectionData { item_def_id: item, evidence }) +} + +fn test_states<'tcx>(tcx: TyCtxt<'tcx>, item: DefId, selected: TraitEvidence<'tcx>) { + round_trip(tcx, selected); + let trait_ref = selected.trait_ref; + let bound = tcx.mk_trait_evidence_kind( + trait_ref, + TraitEvidenceKind::Bound( + ty::BoundVarIndexKind::Bound(ty::DebruijnIndex::from_u32(1)), + ty::BoundEvidence::new(ty::BoundVar::from_u32(0)), + ), + ); + let clause = ty::ClauseKind::Trait(ty::TraitClause { + trait_ref, + polarity: ty::ClausePolarity::Positive, + }); + let contract = tcx.mk_bound_required_contract(ty::BoundRequiredContractData { + identity: ty::solve::InstantiatedItemContract { + key: ty::solve::ItemContractKey { owner: trait_ref.def_id, hir_local_id: 0 }, + complete_early_args: tcx.mk_args(&[]), + }, + clauses: tcx.mk_clauses(&[ty::Binder::dummy(clause).upcast(tcx)]), + principal_index: 0, + ordinary_args: None, + }); + let vars = + tcx.mk_bound_variable_kinds(&[ty::BoundVariableKind::Evidence(ty::EvidenceVariable { + clause, + required_contract: Some(contract), + })]); + let inner = + ty::Binder::bind_with_vars(projection(tcx, item, bound), tcx.mk_bound_variable_kinds(&[])); + let outer = ty::Binder::bind_with_vars(inner, vars); + assert!(inner.has_escaping_bound_vars()); + assert!(!outer.has_escaping_bound_vars()); + // Fragment decoding preserves a reference to an outer binder until it is decoded too. + round_trip(tcx, (outer, outer)); + round_trip(tcx, contract); + + let placeholder = tcx.mk_trait_evidence_kind( + trait_ref, + TraitEvidenceKind::Placeholder(ty::PlaceholderEvidence::new_anon( + ty::UniverseIndex::from_u32(3), + ty::BoundVar::from_u32(2), + )), + ); + assert!(placeholder.has_placeholders()); + assert!(!placeholder.has_escaping_bound_vars()); + round_trip(tcx, projection(tcx, item, placeholder)); + + // Error evidence carries a real emission guarantee, and follows its codec rejection. + let dcx = DiagCtxt::new(Box::new(SilentEmitter)); + let guar = dcx.handle().err("error evidence serialization test"); + let error = tcx.mk_trait_evidence_kind(trait_ref, TraitEvidenceKind::Error(guar)); + assert!(error.references_error()); + let mut encoder = TestEncoder::new(); + assert_panics("should never serialize an `ErrorGuaranteed`", || error.encode(&mut encoder)); + let bytes = encoder.finish(); + assert_panics("`ErrorGuaranteed` should never have been serialized", || { + let _: TraitEvidence<'_> = Decodable::decode(&mut TestDecoder::new(tcx, &bytes)); + }); +} + +fn test_shared_dag<'tcx>( + tcx: TyCtxt<'tcx>, + item: DefId, + pair_impl: DefId, + leaf: TraitEvidence<'tcx>, +) { + let mut evidence = leaf; + let mut halfway_size = 0; + const DEPTH: usize = 32; + for depth in 1..=DEPTH { + let child_ty = evidence.trait_ref.self_ty(); + let self_ty = Ty::new_tup(tcx, &[child_ty, child_ty]); + evidence = tcx.mk_trait_evidence(CandidateEvidence::new( + ty::TraitRef::new(tcx, leaf.trait_ref.def_id, [self_ty]), + CandidateEvidenceSource::Impl { + impl_def_id: pair_impl, + args: tcx.mk_args(&[child_ty.into()]), + }, + [CandidateEvidenceUse::Instantiated(evidence); 2], + )); + if depth == DEPTH / 2 { + let mut encoder = TestEncoder::new(); + evidence.encode(&mut encoder); + halfway_size = encoder.position(); + } + } + + let projected = projection(tcx, item, evidence); + let alias = ty::AliasTy::new_from_args( + tcx, + ty::EvidenceProjection { projection: projected }, + tcx.mk_args(&[]), + ); + // Computing cached flags must not expand the 33 proof handles into 2^32 paths. + let projected_ty = Ty::new_alias(tcx, ty::IsRigid::No, alias); + assert!(projected_ty.has_evidence_projections()); + assert!(!projected_ty.has_escaping_bound_vars()); + + let hash = tcx.with_stable_hashing_context(|inner| { + let mut hcx = CountingHashCtxt { inner, def_ids: Cell::new(0) }; + let mut hasher = StableHasher::new(); + evidence.stable_hash(&mut hcx, &mut hasher); + assert!(hcx.def_ids.get() <= 10 * (DEPTH + 1)); + hasher.finish::() + }); + assert_ne!(hash, fingerprint(tcx, leaf)); + + let mut encoder = TestEncoder::new(); + evidence.encode(&mut encoder); + let size = encoder.position(); + assert!(size <= 3 * halfway_size); + assert_eq!(encoder.evidence.len(), DEPTH + 1); + evidence.encode(&mut encoder); + assert!(encoder.position() - size <= 10); + let bytes = encoder.finish(); + let mut decoder = TestDecoder::new(tcx, &bytes); + let decoded: TraitEvidence<'_> = Decodable::decode(&mut decoder); + assert_eq!(decoded, evidence); + assert_eq!(decoder.evidence.len(), DEPTH + 1); + assert_eq!(fingerprint(tcx, decoded), hash); + assert_eq!(TraitEvidence::decode(&mut decoder), evidence); + assert_eq!(decoder.position(), bytes.len() - MAGIC_END_BYTES.len()); + let mut current = decoded; + for _ in 0..DEPTH { + let TraitEvidenceKind::Selected(recipe) = ¤t.kind else { panic!() }; + let children = &recipe.root_node().nested_evidence; + assert_eq!(children[0], children[1]); + let CandidateEvidenceUse::Instantiated(child) = children[0]; + current = child; + } + assert_eq!(current, leaf); +} + +fn test_decode_validation<'tcx>( + tcx: TyCtxt<'tcx>, + other_item: DefId, + evidence: TraitEvidence<'tcx>, +) { + let TraitEvidenceKind::Selected(recipe) = &evidence.kind else { panic!() }; + for (edge, expected) in [ + (1, "nested trait proof node is out of bounds"), + (0, "non-productive cycle in trait proof DAG"), + ] { + let mut recipe = recipe.clone(); + recipe.nodes[0].nested.push(edge); + let data = TraitEvidenceData { + trait_ref: evidence.trait_ref, + kind: TraitEvidenceKind::Selected(recipe), + }; + let mut encoder = TestEncoder::new(); + encoder.emit_usize(0); + data.encode(&mut encoder); + let bytes = encoder.finish(); + assert_panics(expected, || { + let _: TraitEvidence<'_> = Decodable::decode(&mut TestDecoder::new(tcx, &bytes)); + }); + } + + let mut encoder = TestEncoder::new(); + ty::EvidenceProjectionData::> { item_def_id: other_item, evidence } + .encode(&mut encoder); + let bytes = encoder.finish(); + assert_panics("is not owned by evidence trait", || { + let _: EvidenceProjection<'_> = Decodable::decode(&mut TestDecoder::new(tcx, &bytes)); + }); + + let mut encoder = TestEncoder::new(); + encoder.emit_usize(SHORTHAND_OFFSET); + let bytes = encoder.finish(); + assert_panics("trait evidence shorthand must refer to earlier data", || { + let _: TraitEvidence<'_> = Decodable::decode(&mut TestDecoder::new(tcx, &bytes)); + }); + + // A backward reference can still be cyclic if its inline ancestor is unfinished. + let parent = tcx.mk_trait_evidence(CandidateEvidence::new( + evidence.trait_ref, + recipe.root_source(), + [CandidateEvidenceUse::Instantiated(evidence)], + )); + let mut encoder = TestEncoder::new(); + parent.encode(&mut encoder); + let child_position = encoder.evidence[&evidence] - SHORTHAND_OFFSET; + encoder.opaque.data.truncate(child_position); + encoder.emit_usize(SHORTHAND_OFFSET); + encoder.types.clear(); + encoder.predicates.clear(); + encoder.evidence.clear(); + let valid_position = encoder.position(); + evidence.encode(&mut encoder); + let bytes = encoder.finish(); + let mut decoder = TestDecoder::new(tcx, &bytes); + assert_panics("cycle in trait evidence shorthands", || { + let _: TraitEvidence<'_> = Decodable::decode(&mut decoder); + }); + assert!(decoder.in_progress.is_empty()); + assert_eq!(decoder.with_position(valid_position, TraitEvidence::decode), evidence); + + let invalid_contract: ty::BoundRequiredContractData> = + ty::BoundRequiredContractData { + identity: ty::solve::InstantiatedItemContract { + key: ty::solve::ItemContractKey { + owner: evidence.trait_ref.def_id, + hir_local_id: 0, + }, + complete_early_args: tcx.mk_args(&[]), + }, + clauses: tcx.mk_clauses(&[]), + principal_index: 0, + ordinary_args: None, + }; + let mut encoder = TestEncoder::new(); + invalid_contract.encode(&mut encoder); + let bytes = encoder.finish(); + assert_panics("required-contract principal must be a trait clause", || { + let _: BoundRequiredContract<'_> = Decodable::decode(&mut TestDecoder::new(tcx, &bytes)); + }); +} + +fn round_trip<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) +where + T: Debug + PartialEq + StableHash + Encodable>, + T: for<'a> Decodable>, +{ + let mut encoder = TestEncoder::new(); + value.encode(&mut encoder); + let bytes = encoder.finish(); + let mut decoder = TestDecoder::new(tcx, &bytes); + let decoded: T = Decodable::decode(&mut decoder); + assert_eq!(value, decoded); + assert_eq!(fingerprint(tcx, &value), fingerprint(tcx, &decoded)); + assert_eq!(decoder.position(), bytes.len() - MAGIC_END_BYTES.len()); +} + +fn fingerprint(tcx: TyCtxt<'_>, value: impl StableHash) -> Fingerprint { + tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + value.stable_hash(&mut hcx, &mut hasher); + hasher.finish() + }) +} + +struct CountingHashCtxt<'a> { + inner: StableHashState<'a>, + def_ids: Cell, +} + +impl StableHashCtxt for CountingHashCtxt<'_> { + fn stable_hash_span(&mut self, span: RawSpan, hasher: &mut StableHasher) { + self.inner.stable_hash_span(span, hasher); + } + fn def_path_hash(&self, def_id: RawDefId) -> Fingerprint { + self.def_ids.set(self.def_ids.get() + 1); + self.inner.def_path_hash(def_id) + } + fn stable_hash_controls(&self) -> StableHashControls { + self.inner.stable_hash_controls() + } + fn assert_default_stable_hash_controls(&self, message: &str) { + self.inner.assert_default_stable_hash_controls(message); + } +} + +fn assert_panics(expected: &str, f: impl FnOnce()) { + let hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(|_| {})); + let result = catch_unwind(AssertUnwindSafe(f)); + std::panic::set_hook(hook); + let error = result.expect_err("invalid representation was accepted"); + let message = error + .downcast_ref::() + .map(String::as_str) + .or_else(|| error.downcast_ref::<&str>().copied()) + .unwrap(); + assert!(message.contains(expected), "unexpected panic: {message}"); +} + +// These adapters use the production type codecs and a real TyCtxt. DefIds remain +// local to this test's compiler invocation; the opaque codecs handle primitives. +struct TestEncoder<'tcx> { + opaque: MemEncoder, + types: FxHashMap, usize>, + predicates: FxHashMap, usize>, + evidence: FxHashMap, usize>, +} + +impl<'tcx> TestEncoder<'tcx> { + fn new() -> Self { + Self { + opaque: MemEncoder::new(), + types: Default::default(), + predicates: Default::default(), + evidence: Default::default(), + } + } + fn finish(self) -> Vec { + let mut bytes = self.opaque.finish(); + bytes.extend_from_slice(MAGIC_END_BYTES); + bytes + } +} + +macro_rules! encoder_methods { + ($($name:ident($ty:ty);)*) => { + $(fn $name(&mut self, value: $ty) { self.opaque.$name(value); })* + }; +} + +impl Encoder for TestEncoder<'_> { + encoder_methods! { + emit_usize(usize); + emit_u128(u128); + emit_u64(u64); + emit_u32(u32); + emit_u16(u16); + emit_u8(u8); + emit_isize(isize); + emit_i128(i128); + emit_i64(i64); + emit_i32(i32); + emit_i16(i16); + } + fn emit_raw_bytes(&mut self, value: &[u8]) { + self.opaque.emit_raw_bytes(value); + } +} + +impl SpanEncoder for TestEncoder<'_> { + fn encode_span(&mut self, span: Span) { + self.opaque.encode_span(span); + } + fn encode_symbol(&mut self, symbol: Symbol) { + self.opaque.encode_symbol(symbol); + } + fn encode_byte_symbol(&mut self, symbol: ByteSymbol) { + self.opaque.encode_byte_symbol(symbol); + } + fn encode_expn_id(&mut self, id: ExpnId) { + self.opaque.encode_expn_id(id); + } + fn encode_syntax_context(&mut self, context: SyntaxContext) { + self.opaque.encode_syntax_context(context); + } + fn encode_crate_num(&mut self, cnum: CrateNum) { + self.opaque.encode_crate_num(cnum); + } + fn encode_def_index(&mut self, index: DefIndex) { + self.emit_u32(index.as_u32()); + } + fn encode_def_id(&mut self, id: DefId) { + self.encode_crate_num(id.krate); + self.encode_def_index(id.index); + } +} + +impl<'tcx> TyEncoder<'tcx> for TestEncoder<'tcx> { + const CLEAR_CROSS_CRATE: bool = false; + fn position(&self) -> usize { + self.opaque.position() + } + fn type_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.types + } + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.predicates + } + fn trait_evidence_shorthands(&mut self) -> &mut FxHashMap, usize> { + &mut self.evidence + } + fn encode_alloc_id(&mut self, _: &AllocId) { + panic!("the test contains no allocations"); + } +} + +struct TestDecoder<'a, 'tcx> { + opaque: MemDecoder<'a>, + tcx: TyCtxt<'tcx>, + types: FxHashMap>, + evidence: FxHashMap>, + in_progress: FxHashSet, +} + +impl<'a, 'tcx> TestDecoder<'a, 'tcx> { + fn new(tcx: TyCtxt<'tcx>, bytes: &'a [u8]) -> Self { + Self { + opaque: MemDecoder::new(bytes, 0).unwrap(), + tcx, + types: Default::default(), + evidence: Default::default(), + in_progress: Default::default(), + } + } +} + +rustc_middle::implement_ty_decoder!(TestDecoder<'a, 'tcx>); + +impl BlobDecoder for TestDecoder<'_, '_> { + fn decode_symbol(&mut self) -> Symbol { + self.opaque.decode_symbol() + } + fn decode_byte_symbol(&mut self) -> ByteSymbol { + self.opaque.decode_byte_symbol() + } + fn decode_def_index(&mut self) -> DefIndex { + DefIndex::from_u32(self.read_u32()) + } +} + +impl SpanDecoder for TestDecoder<'_, '_> { + fn decode_span(&mut self) -> Span { + self.opaque.decode_span() + } + fn decode_expn_id(&mut self) -> ExpnId { + self.opaque.decode_expn_id() + } + fn decode_syntax_context(&mut self) -> SyntaxContext { + self.opaque.decode_syntax_context() + } + fn decode_crate_num(&mut self) -> CrateNum { + self.opaque.decode_crate_num() + } + fn decode_def_id(&mut self) -> DefId { + DefId { krate: self.decode_crate_num(), index: self.decode_def_index() } + } + fn decode_attr_id(&mut self) -> rustc_ast::AttrId { + panic!("the test contains no attributes"); + } +} + +impl<'tcx> ty::InternerDecoder for TestDecoder<'_, 'tcx> { + type Interner = TyCtxt<'tcx>; + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } +} + +impl<'tcx> TyDecoder<'tcx> for TestDecoder<'_, 'tcx> { + const CLEAR_CROSS_CRATE: bool = false; + + fn cached_ty_for_shorthand(&mut self, shorthand: usize, f: F) -> Ty<'tcx> + where + F: FnOnce(&mut Self) -> Ty<'tcx>, + { + if let Some(&ty) = self.types.get(&shorthand) { + return ty; + } + let ty = f(self); + self.types.insert(shorthand, ty); + ty + } + + fn cached_trait_evidence_for_shorthand( + &mut self, + shorthand: usize, + f: F, + ) -> TraitEvidence<'tcx> + where + F: FnOnce(&mut Self) -> TraitEvidence<'tcx>, + { + if let Some(&evidence) = self.evidence.get(&shorthand) { + return evidence; + } + let evidence = f(self); + self.evidence.insert(shorthand, evidence); + evidence + } + + fn trait_evidence_in_progress(&mut self) -> &mut FxHashSet { + &mut self.in_progress + } + + fn with_position(&mut self, position: usize, f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + let decoder = self.opaque.split_at(position); + let previous = std::mem::replace(&mut self.opaque, decoder); + let result = f(self); + self.opaque = previous; + result + } + + fn decode_alloc_id(&mut self) -> AllocId { + panic!("the test contains no allocations"); + } +}