From 8010641d1470ee1817e6181fa08ae53d73d672df Mon Sep 17 00:00:00 2001 From: hongde Date: Mon, 14 Sep 2026 20:18:13 +0800 Subject: [PATCH] Implement dependent binders and trait evidence representation Represent typed const and evidence declarations in binder telescopes. Keep proof substitutions separate from ordinary generic arguments and return dependent clauses and contracts when opening a binder. Carry evidence-indexed type and const projections through interning, folding, visiting, structural relation, hashing, and serialization. Preserve sharing in proof DAGs and validate declaration, projection, and decoding invariants. Adapt existing consumers and reject unsupported evidence evaluation. Add real-TyCtxt tests for substitution, nested binders, structural proof identity, projection arguments, contract obligations, and shared-DAG codec behavior. --- compiler/rustc_borrowck/src/type_check/mod.rs | 3 + .../rustc_const_eval/src/util/type_name.rs | 5 + .../src/check/compare_impl_item.rs | 2 +- .../src/coherence/inherent_impls.rs | 6 +- .../src/coherence/orphan.rs | 2 +- .../src/collect/item_bounds.rs | 2 +- .../src/collect/resolve_bound_vars.rs | 4 +- .../src/constrained_generic_params.rs | 6 +- .../src/variance/constraints.rs | 8 + compiler/rustc_infer/src/infer/mod.rs | 17 +- .../src/infer/relate/generalize.rs | 2 + .../rustc_lint/src/types/improper_ctypes.rs | 6 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 28 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 7 + compiler/rustc_middle/src/arena.rs | 1 + .../rustc_middle/src/mir/interpret/queries.rs | 3 + compiler/rustc_middle/src/mir/pretty.rs | 3 + compiler/rustc_middle/src/query/keys.rs | 2 + .../rustc_middle/src/query/on_disk_cache.rs | 34 +- compiler/rustc_middle/src/traits/solve.rs | 144 +++ compiler/rustc_middle/src/ty/codec.rs | 112 +- compiler/rustc_middle/src/ty/context.rs | 126 ++- .../src/ty/context/impl_interner.rs | 46 +- compiler/rustc_middle/src/ty/error.rs | 26 +- compiler/rustc_middle/src/ty/fold.rs | 299 +++++- compiler/rustc_middle/src/ty/mod.rs | 12 +- compiler/rustc_middle/src/ty/predicate.rs | 99 +- compiler/rustc_middle/src/ty/print/pretty.rs | 31 +- compiler/rustc_middle/src/ty/sty.rs | 2 + compiler/rustc_middle/src/ty/util.rs | 11 +- compiler/rustc_middle/src/ty/visit.rs | 5 +- .../src/offload/manifest.rs | 34 +- .../src/solve/assembly/mod.rs | 4 + .../src/solve/assembly/structural_traits.rs | 7 +- .../src/solve/effect_goals.rs | 3 + .../src/solve/eval_ctxt/mod.rs | 2 + .../src/solve/project_goals/mod.rs | 4 + .../src/solve/trait_goals.rs | 6 +- compiler/rustc_privacy/src/lib.rs | 26 +- .../src/unstable/convert/internal.rs | 2 +- .../src/unstable/convert/stable/ty.rs | 49 +- compiler/rustc_symbol_mangling/src/legacy.rs | 3 + compiler/rustc_symbol_mangling/src/v0.rs | 3 + .../src/error_reporting/infer/mod.rs | 4 + .../src/error_reporting/infer/region.rs | 2 +- .../traits/fulfillment_errors.rs | 7 +- .../src/traits/normalize.rs | 8 +- .../src/traits/outlives_for_liveness.rs | 9 + .../src/traits/query/normalize.rs | 4 + .../src/traits/select/candidate_assembly.rs | 6 +- .../src/traits/select/mod.rs | 6 +- .../rustc_trait_selection/src/traits/wf.rs | 6 + compiler/rustc_type_ir/src/binder.rs | 989 +++++++++++++++++- compiler/rustc_type_ir/src/const_kind.rs | 24 +- compiler/rustc_type_ir/src/fast_reject.rs | 14 +- compiler/rustc_type_ir/src/flags.rs | 180 +++- compiler/rustc_type_ir/src/fold.rs | 85 ++ compiler/rustc_type_ir/src/interner.rs | 125 ++- compiler/rustc_type_ir/src/outlives.rs | 22 +- .../rustc_type_ir/src/region_constraint.rs | 118 ++- compiler/rustc_type_ir/src/relate.rs | 518 ++++++++- compiler/rustc_type_ir/src/serialize.rs | 47 +- compiler/rustc_type_ir/src/solve/mod.rs | 719 ++++++++++++- compiler/rustc_type_ir/src/term_kind.rs | 126 ++- compiler/rustc_type_ir/src/ty/alias.rs | 25 + compiler/rustc_type_ir/src/ty_kind.rs | 54 +- compiler/rustc_type_ir/src/ty_kind/closure.rs | 12 + compiler/rustc_type_ir/src/universe.rs | 11 + compiler/rustc_type_ir/src/visit.rs | 72 +- compiler/rustc_type_ir/src/walk.rs | 10 + src/librustdoc/clean/mod.rs | 26 +- src/librustdoc/clean/utils.rs | 1 + src/librustdoc/passes/collect_trait_impls.rs | 8 + .../clippy/clippy_lints/src/dereference.rs | 2 +- .../dependent-binder-representation-input.rs | 29 + .../dependent-evidence-codec-input.rs | 17 + .../dependent-binder-representation.rs | 851 +++++++++++++++ tests/ui-fulldeps/dependent-evidence-codec.rs | 593 +++++++++++ 78 files changed, 5671 insertions(+), 256 deletions(-) create mode 100644 tests/ui-fulldeps/auxiliary/dependent-binder-representation-input.rs create mode 100644 tests/ui-fulldeps/auxiliary/dependent-evidence-codec-input.rs create mode 100644 tests/ui-fulldeps/dependent-binder-representation.rs create mode 100644 tests/ui-fulldeps/dependent-evidence-codec.rs 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"); + } +}