From a8eeb11efaf9d093c3f3f192325bba287f3d6405 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Sun, 15 Feb 2026 22:01:32 +0100 Subject: [PATCH 01/70] Trait impl check for simple types --- .../src/encoders/ty/generics/trait_impls.rs | 9 --- .../src/encoders/ty/generics/traits.rs | 63 +++++++++++++++---- 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 46c79cdaa64..3e8f7b28343 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -54,20 +54,11 @@ impl TaskEncoder for TraitImplEnc { let implementing_ty = RustTyDecomposition::from_ty(implementing_ty, *task_key); let implementing_ty = implementing_ty.ty.name(); - let impl_fun = trait_data.impl_fun; let trait_ty_decls = params .ty_decls() .iter() .map(|dec| dec.upcast_ty()) .collect::>(); - let trait_tys = args.get_ty(); - - axs.push( - vcx.mk_domain_axiom( - vir_format_identifier!(vcx, "{}_impl_{idx}_{implementing_ty}", trait_data.trait_name), - vir::expr! {forall ..[trait_ty_decls] :: {[impl_fun(trait_tys)]} [impl_fun(trait_tys)]} - ) - ); tcx.associated_items(*task_key) .in_definition_order() diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 9ced9017b53..30fc9a7fb2f 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -1,9 +1,13 @@ use prusti_rustc_interface::{middle::ty::AssocKind, span::def_id::DefId}; use rustc_hash::FxHashMap; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; -use vir::{FunctionIdn, vir_format_identifier}; +use vir::{CallableIdn, FunctionIdn, vir_format_identifier}; -use crate::encoders::ty::generics::{GParams, GenericParamsEnc}; +use crate::encoders::ty::{ + RustTyDecomposition, + generics::{GParams, GenericParamsEnc}, + lifted::TyConstructorEnc, +}; pub struct TraitEnc; @@ -14,6 +18,12 @@ pub struct TraitData<'vir> { pub impl_fun: FunctionIdn<'vir, vir::ManyTyVal, vir::Bool>, } +#[derive(Debug, Clone)] +pub struct TraitEncOutput<'vir> { + trait_domain: vir::Domain<'vir>, + impl_fun: vir::Function<'vir>, +} + impl TaskEncoder for TraitEnc { task_encoder::encoder_cache!(TraitEnc); @@ -24,11 +34,12 @@ impl TaskEncoder for TraitEnc { type TaskDescription<'vir> = DefId; type OutputFullDependency<'vir> = TraitData<'vir>; - type OutputFullLocal<'vir> = vir::Domain<'vir>; + type OutputFullLocal<'vir> = TraitEncOutput<'vir>; fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { - for dom in TraitEnc::all_outputs_local_no_errors() { - program.add_domain(dom); + for trait_enc in TraitEnc::all_outputs_local_no_errors() { + program.add_domain(trait_enc.trait_domain); + program.add_function(trait_enc.impl_fun); } } @@ -64,17 +75,44 @@ impl TaskEncoder for TraitEnc { ) }) .collect::>(); - let mut funcs = type_did_fun_mapping + let funcs = type_did_fun_mapping .values() .map(|function_idn| vcx.mk_domain_function(*function_idn, false, None)) .collect::>(); - let impl_fun = FunctionIdn::new( + + let impl_fun_idn = FunctionIdn::new( vir_format_identifier!(vcx, "{}_impl", trait_name), vcx.alloc_slice(&(vec![vir::TYPE_TYVAL; params.ty_exprs().len()])), vir::TYPE_BOOL, ); - let impl_fun_data = vcx.mk_domain_function(impl_fun, false, None); - funcs.push(impl_fun_data); + + let impl_fun_body = { + let mut checks = Vec::new(); + for impl_did in tcx.all_impls(*task_key) { + let implementing_ty = tcx.type_of(impl_did).instantiate_identity(); + let implementing_ty = RustTyDecomposition::from_ty(implementing_ty, impl_did); + + let impl_type = deps.require_ref::(implementing_ty.ty)?; + + let type_check = vcx.mk_adt_discriminator_expr( + params.ty_exprs()[0], + impl_type.ty_constructor.name().to_str(), + ); + checks.push(type_check); + } + + vcx.mk_disj(&checks) + }; + + let impl_fun = vcx.mk_function( + impl_fun_idn, + (params.ty_decls(),), + &[], + &[], + None, + Some(impl_fun_body), + ); + let trait_domain = vcx.mk_domain( vir_format_identifier!(vcx, "t_{}", trait_name), &[], @@ -83,11 +121,14 @@ impl TaskEncoder for TraitEnc { None, ); Ok(( - trait_domain, + TraitEncOutput { + trait_domain, + impl_fun, + }, TraitData { trait_name, type_did_fun_mapping, - impl_fun, + impl_fun: impl_fun_idn, }, )) }) From 4a99e61d0a691bb1bb777b03085fe48b57327a68 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Sun, 15 Feb 2026 22:46:17 +0100 Subject: [PATCH 02/70] Trait impl check for unknown types --- .../src/encoders/ty/generics/traits.rs | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 30fc9a7fb2f..783bf4af9a5 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -1,7 +1,7 @@ use prusti_rustc_interface::{middle::ty::AssocKind, span::def_id::DefId}; use rustc_hash::FxHashMap; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; -use vir::{CallableIdn, FunctionIdn, vir_format_identifier}; +use vir::{AdtDestructorWrapper, CallableIdn, FunctionIdn, vir_format_identifier}; use crate::encoders::ty::{ RustTyDecomposition, @@ -22,6 +22,7 @@ pub struct TraitData<'vir> { pub struct TraitEncOutput<'vir> { trait_domain: vir::Domain<'vir>, impl_fun: vir::Function<'vir>, + impl_fun_unknown: vir::Function<'vir>, } impl TaskEncoder for TraitEnc { @@ -40,6 +41,7 @@ impl TaskEncoder for TraitEnc { for trait_enc in TraitEnc::all_outputs_local_no_errors() { program.add_domain(trait_enc.trait_domain); program.add_function(trait_enc.impl_fun); + program.add_function(trait_enc.impl_fun_unknown); } } @@ -65,8 +67,7 @@ impl TaskEncoder for TraitEnc { FunctionIdn::new( vir_format_identifier!( vcx, - "{}_Assoc_{}_func", - trait_name, + "{trait_name}_Assoc_{}_func", tcx.item_name(item.def_id), ), vcx.alloc_slice(&vec![vir::TYPE_TYVAL; params_type.ty_exprs().len()]), // params_type also includes parameters of trait itself @@ -81,13 +82,30 @@ impl TaskEncoder for TraitEnc { .collect::>(); let impl_fun_idn = FunctionIdn::new( - vir_format_identifier!(vcx, "{}_impl", trait_name), + vir_format_identifier!(vcx, "{trait_name}_impl"), vcx.alloc_slice(&(vec![vir::TYPE_TYVAL; params.ty_exprs().len()])), vir::TYPE_BOOL, ); + let impl_fun_unknown_idn: FunctionIdn<'vir, vir::Int, vir::Bool> = FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_impl_unknown"), + vir::TYPE_INT, + vir::TYPE_BOOL, + ); + + let impl_fun_unknown = vcx.mk_function( + impl_fun_unknown_idn, + (vcx.mk_local_decl("non_unit", vir::TYPE_INT),), + &[], + &[], + None, + None, + ); + let impl_fun_body = { - let mut checks = Vec::new(); + let impl_type_expr = params.ty_exprs()[0]; + + let mut trait_impl_checks = Vec::new(); for impl_did in tcx.all_impls(*task_key) { let implementing_ty = tcx.type_of(impl_did).instantiate_identity(); let implementing_ty = RustTyDecomposition::from_ty(implementing_ty, impl_did); @@ -95,13 +113,23 @@ impl TaskEncoder for TraitEnc { let impl_type = deps.require_ref::(implementing_ty.ty)?; let type_check = vcx.mk_adt_discriminator_expr( - params.ty_exprs()[0], + impl_type_expr, impl_type.ty_constructor.name().to_str(), ); - checks.push(type_check); + trait_impl_checks.push(type_check); } - vcx.mk_disj(&checks) + // Check for types outside of the known type enumeration + let unknown_type_check = { + let type_check = vcx.mk_adt_discriminator_expr(impl_type_expr, "Unknown_type"); + let unknown_type_destructor = + vcx.mk_adt_destructor("non_unit", vir::TYPE_TYVAL, vir::TYPE_INT); + let unknown_type_id = unknown_type_destructor.call()(impl_type_expr); + vcx.mk_conj(&[type_check, impl_fun_unknown_idn(unknown_type_id)]) + }; + trait_impl_checks.push(unknown_type_check); + + vcx.mk_disj(&trait_impl_checks) }; let impl_fun = vcx.mk_function( @@ -114,7 +142,7 @@ impl TaskEncoder for TraitEnc { ); let trait_domain = vcx.mk_domain( - vir_format_identifier!(vcx, "t_{}", trait_name), + vir_format_identifier!(vcx, "t_{trait_name}"), &[], &[], vcx.alloc_slice(funcs.as_slice()), @@ -124,6 +152,7 @@ impl TaskEncoder for TraitEnc { TraitEncOutput { trait_domain, impl_fun, + impl_fun_unknown, }, TraitData { trait_name, From 5a591724218195dd4a71e1c6ecc1148b336e0c8a Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Mon, 16 Feb 2026 23:05:00 +0100 Subject: [PATCH 03/70] Change the encoding to use exists clauses --- .../src/encoders/ty/generics/traits.rs | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 783bf4af9a5..c445a30ab71 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -1,13 +1,9 @@ use prusti_rustc_interface::{middle::ty::AssocKind, span::def_id::DefId}; use rustc_hash::FxHashMap; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; -use vir::{AdtDestructorWrapper, CallableIdn, FunctionIdn, vir_format_identifier}; +use vir::{CastType, Dyn, FunctionIdn, vir_format_identifier}; -use crate::encoders::ty::{ - RustTyDecomposition, - generics::{GParams, GenericParamsEnc}, - lifted::TyConstructorEnc, -}; +use crate::encoders::ty::generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc}; pub struct TraitEnc; @@ -103,32 +99,38 @@ impl TaskEncoder for TraitEnc { ); let impl_fun_body = { - let impl_type_expr = params.ty_exprs()[0]; - let mut trait_impl_checks = Vec::new(); - for impl_did in tcx.all_impls(*task_key) { - let implementing_ty = tcx.type_of(impl_did).instantiate_identity(); - let implementing_ty = RustTyDecomposition::from_ty(implementing_ty, impl_did); - - let impl_type = deps.require_ref::(implementing_ty.ty)?; - let type_check = vcx.mk_adt_discriminator_expr( - impl_type_expr, - impl_type.ty_constructor.name().to_str(), + for impl_did in tcx.all_impls(*task_key) { + let impl_ctx = GParams::from(impl_did); + let impl_params = deps.require_dep::(impl_ctx)?; + + let impl_trait_ref = + tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); + let impl_args = + deps.require_dep::(GArgs::new(impl_ctx, impl_trait_ref.args))?; + + let mut conjuncts = Vec::new(); + + for (trait_ty_param, impl_arg_val) in + params.ty_exprs().iter().zip(impl_args.get_ty()) + { + conjuncts.push(vcx.mk_eq_expr(*trait_ty_param, *impl_arg_val)); + } + + // Create an "exists" for each generic of the impl block + let trait_ty_decls = vcx.alloc_slice( + impl_params + .ty_decls() + .iter() + .map(|dec| dec.upcast_ty::()) + .collect::>() + .as_slice(), ); - trait_impl_checks.push(type_check); + let exists = vcx.mk_exists_expr(&trait_ty_decls, &[], vcx.mk_conj(&conjuncts)); + trait_impl_checks.push(exists); } - // Check for types outside of the known type enumeration - let unknown_type_check = { - let type_check = vcx.mk_adt_discriminator_expr(impl_type_expr, "Unknown_type"); - let unknown_type_destructor = - vcx.mk_adt_destructor("non_unit", vir::TYPE_TYVAL, vir::TYPE_INT); - let unknown_type_id = unknown_type_destructor.call()(impl_type_expr); - vcx.mk_conj(&[type_check, impl_fun_unknown_idn(unknown_type_id)]) - }; - trait_impl_checks.push(unknown_type_check); - vcx.mk_disj(&trait_impl_checks) }; From ec81befe873014b474ae9c085754887b0a8a26d4 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 17 Feb 2026 12:39:59 +0100 Subject: [PATCH 04/70] Fix impl_unknown to contain the trait generics --- .../src/encoders/ty/generics/traits.rs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index c445a30ab71..5c812fb5710 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -83,15 +83,21 @@ impl TaskEncoder for TraitEnc { vir::TYPE_BOOL, ); - let impl_fun_unknown_idn: FunctionIdn<'vir, vir::Int, vir::Bool> = FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_impl_unknown"), - vir::TYPE_INT, - vir::TYPE_BOOL, - ); - + let impl_fun_unknown_idn: FunctionIdn<'vir, (vir::ManyTyVal, vir::Int), vir::Bool> = { + // Omit the Self type as it is known to be the "Unknown_type" + let unknown_params = vec![vir::TYPE_TYVAL; params.ty_exprs().len() - 1]; + FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_impl_unknown"), + (vcx.alloc_slice(&unknown_params), vir::TYPE_INT), + vir::TYPE_BOOL, + ) + }; let impl_fun_unknown = vcx.mk_function( impl_fun_unknown_idn, - (vcx.mk_local_decl("non_unit", vir::TYPE_INT),), + ( + vcx.alloc_slice(¶ms.ty_decls()[1..]), + vcx.mk_local_decl("non_unit", vir::TYPE_INT), + ), &[], &[], None, @@ -118,6 +124,8 @@ impl TaskEncoder for TraitEnc { conjuncts.push(vcx.mk_eq_expr(*trait_ty_param, *impl_arg_val)); } + // TODO: Add checks for the trait bounds + // Create an "exists" for each generic of the impl block let trait_ty_decls = vcx.alloc_slice( impl_params @@ -131,6 +139,9 @@ impl TaskEncoder for TraitEnc { trait_impl_checks.push(exists); } + // TODO: Add a check for the unknown type + {} + vcx.mk_disj(&trait_impl_checks) }; From 25655ff864dcceb6691d3264092e379123f5ed28 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 17 Feb 2026 13:17:30 +0100 Subject: [PATCH 05/70] Include the check for the unknown type in the impl function --- .../src/encoders/ty/generics/traits.rs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 5c812fb5710..8607ca9f23e 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -139,8 +139,27 @@ impl TaskEncoder for TraitEnc { trait_impl_checks.push(exists); } - // TODO: Add a check for the unknown type - {} + { + let non_unit_decl = vcx.mk_local_decl("non_unit", vir::TYPE_INT); + let non_unit_ex = vcx.mk_local_ex(non_unit_decl); + let unknown: FunctionIdn<'_, vir::Int, vir::TyVal> = FunctionIdn::new( + vir::vir_format_identifier!(vcx, "Unknown_type"), + vir::TYPE_INT, + vir::TYPE_TYVAL, + ); + let self_is_unknown = + vcx.mk_eq_expr(params.ty_exprs()[0], unknown(non_unit_ex)); + + let unknown_impls = impl_fun_unknown_idn(¶ms.ty_exprs()[1..], non_unit_ex); + + let exists_unknown = vcx.mk_exists_expr( + vcx.alloc_slice(&[non_unit_decl]), + &[], + vcx.mk_conj(&[self_is_unknown, unknown_impls]), + ); + + trait_impl_checks.push(exists_unknown); + } vcx.mk_disj(&trait_impl_checks) }; From 5bd4313965a2b41b1462d127b56ee90faa8872ef Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 17 Feb 2026 15:17:30 +0100 Subject: [PATCH 06/70] Encode trait bound requirements --- .../src/encoders/ty/generics/traits.rs | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 8607ca9f23e..7b23972e4dc 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -1,4 +1,7 @@ -use prusti_rustc_interface::{middle::ty::AssocKind, span::def_id::DefId}; +use prusti_rustc_interface::{ + middle::ty::{self, AssocKind}, + span::def_id::DefId, +}; use rustc_hash::FxHashMap; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; use vir::{CastType, Dyn, FunctionIdn, vir_format_identifier}; @@ -124,7 +127,29 @@ impl TaskEncoder for TraitEnc { conjuncts.push(vcx.mk_eq_expr(*trait_ty_param, *impl_arg_val)); } - // TODO: Add checks for the trait bounds + for trait_pred in impl_ctx + .typing_env() + .param_env + .caller_bounds() + .iter() + .filter_map(ty::Clause::as_trait_clause) + .map(ty::Binder::skip_binder) + { + let required_trait_impl_fun = { + let required_trait = trait_pred.def_id(); + if required_trait == *task_key { + // Avoid recursive calls to the trait encoder for the same trait + impl_fun_idn + } else { + deps.require_dep::(trait_pred.def_id())?.impl_fun + } + }; + let predicate_args = deps.require_dep::(GArgs::new( + impl_ctx, + trait_pred.trait_ref.args, + ))?; + conjuncts.push(required_trait_impl_fun(predicate_args.get_ty())); + } // Create an "exists" for each generic of the impl block let trait_ty_decls = vcx.alloc_slice( @@ -135,7 +160,7 @@ impl TaskEncoder for TraitEnc { .collect::>() .as_slice(), ); - let exists = vcx.mk_exists_expr(&trait_ty_decls, &[], vcx.mk_conj(&conjuncts)); + let exists = vcx.mk_exists_expr(trait_ty_decls, &[], vcx.mk_conj(&conjuncts)); trait_impl_checks.push(exists); } From 7abceddc75b5e2ade5f8f100880c47dca3627bd3 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 17 Feb 2026 15:30:33 +0100 Subject: [PATCH 07/70] Emit early impl_fun ref to avoid dependency cycles --- .../src/encoders/ty/generics/traits.rs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 7b23972e4dc..b0f1268af2c 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -3,7 +3,7 @@ use prusti_rustc_interface::{ span::def_id::DefId, }; use rustc_hash::FxHashMap; -use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; +use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder, TaskEncoderDependencies}; use vir::{CastType, Dyn, FunctionIdn, vir_format_identifier}; use crate::encoders::ty::generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc}; @@ -17,6 +17,12 @@ pub struct TraitData<'vir> { pub impl_fun: FunctionIdn<'vir, vir::ManyTyVal, vir::Bool>, } +#[derive(Debug, Clone)] +pub struct TraitImplRef<'vir> { + pub impl_fun: FunctionIdn<'vir, vir::ManyTyVal, vir::Bool>, +} +impl OutputRefAny for TraitImplRef<'_> {} + #[derive(Debug, Clone)] pub struct TraitEncOutput<'vir> { trait_domain: vir::Domain<'vir>, @@ -34,6 +40,7 @@ impl TaskEncoder for TraitEnc { type TaskDescription<'vir> = DefId; type OutputFullDependency<'vir> = TraitData<'vir>; + type OutputRef<'vir> = TraitImplRef<'vir>; type OutputFullLocal<'vir> = TraitEncOutput<'vir>; fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { @@ -48,7 +55,6 @@ impl TaskEncoder for TraitEnc { task_key: &Self::TaskKey<'vir>, deps: &mut TaskEncoderDependencies<'vir, Self>, ) -> EncodeFullResult<'vir, Self> { - deps.emit_output_ref(*task_key, ())?; vir::with_vcx(|vcx| { let tcx = vcx.tcx(); let params = deps.require_dep::(GParams::from(*task_key))?; @@ -86,6 +92,15 @@ impl TaskEncoder for TraitEnc { vir::TYPE_BOOL, ); + // Emit the impl function reference early, so that it can be used in the trait bounds + // without causing dependency cycles. + deps.emit_output_ref( + *task_key, + TraitImplRef { + impl_fun: impl_fun_idn, + }, + )?; + let impl_fun_unknown_idn: FunctionIdn<'vir, (vir::ManyTyVal, vir::Int), vir::Bool> = { // Omit the Self type as it is known to be the "Unknown_type" let unknown_params = vec![vir::TYPE_TYVAL; params.ty_exprs().len() - 1]; @@ -135,15 +150,8 @@ impl TaskEncoder for TraitEnc { .filter_map(ty::Clause::as_trait_clause) .map(ty::Binder::skip_binder) { - let required_trait_impl_fun = { - let required_trait = trait_pred.def_id(); - if required_trait == *task_key { - // Avoid recursive calls to the trait encoder for the same trait - impl_fun_idn - } else { - deps.require_dep::(trait_pred.def_id())?.impl_fun - } - }; + let required_trait_impl_fun = + deps.require_ref::(trait_pred.def_id())?.impl_fun; let predicate_args = deps.require_dep::(GArgs::new( impl_ctx, trait_pred.trait_ref.args, @@ -165,6 +173,7 @@ impl TaskEncoder for TraitEnc { } { + // Add a case for unknown types that might implement the trait let non_unit_decl = vcx.mk_local_decl("non_unit", vir::TYPE_INT); let non_unit_ex = vcx.mk_local_ex(non_unit_decl); let unknown: FunctionIdn<'_, vir::Int, vir::TyVal> = FunctionIdn::new( From 464843fe5dc5e6c0821034e2d87f62becebd05fe Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 18 Feb 2026 14:56:47 +0100 Subject: [PATCH 08/70] Modify encoding to avoid using existentials --- .../src/encoders/ty/generics/traits.rs | 151 +++++++++++++----- 1 file changed, 107 insertions(+), 44 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index b0f1268af2c..448563414bb 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -4,9 +4,13 @@ use prusti_rustc_interface::{ }; use rustc_hash::FxHashMap; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder, TaskEncoderDependencies}; -use vir::{CastType, Dyn, FunctionIdn, vir_format_identifier}; +use vir::{CallableIdn, CastType, Dyn, FunctionIdn, vir_format_identifier}; -use crate::encoders::ty::generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc}; +use crate::encoders::ty::{ + RustTyDecomposition, + generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc}, + lifted::TyConstructorEnc, +}; pub struct TraitEnc; @@ -126,50 +130,64 @@ impl TaskEncoder for TraitEnc { let mut trait_impl_checks = Vec::new(); for impl_did in tcx.all_impls(*task_key) { - let impl_ctx = GParams::from(impl_did); - let impl_params = deps.require_dep::(impl_ctx)?; - - let impl_trait_ref = - tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); - let impl_args = - deps.require_dep::(GArgs::new(impl_ctx, impl_trait_ref.args))?; - - let mut conjuncts = Vec::new(); - - for (trait_ty_param, impl_arg_val) in - params.ty_exprs().iter().zip(impl_args.get_ty()) - { - conjuncts.push(vcx.mk_eq_expr(*trait_ty_param, *impl_arg_val)); - } - - for trait_pred in impl_ctx - .typing_env() - .param_env - .caller_bounds() - .iter() - .filter_map(ty::Clause::as_trait_clause) - .map(ty::Binder::skip_binder) - { - let required_trait_impl_fun = - deps.require_ref::(trait_pred.def_id())?.impl_fun; - let predicate_args = deps.require_dep::(GArgs::new( - impl_ctx, - trait_pred.trait_ref.args, - ))?; - conjuncts.push(required_trait_impl_fun(predicate_args.get_ty())); - } + // let impl_ctx = GParams::from(impl_did); + // let impl_params = deps.require_dep::(impl_ctx)?; - // Create an "exists" for each generic of the impl block - let trait_ty_decls = vcx.alloc_slice( - impl_params - .ty_decls() - .iter() - .map(|dec| dec.upcast_ty::()) - .collect::>() - .as_slice(), + // let impl_trait_ref = + // tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); + // dbg!(&impl_trait_ref.args); + + let implementing_ty = tcx.type_of(impl_did).instantiate_identity(); + + // Collect the locations of the generic parameters of the impl block from the + // `Self` type and the trait arguments. This will allos us to refer to them + // when encoding the trait bounds of the impl block. + let mut generics_map = HashMap::new(); + + let self_check_expr = encode_type( + vcx, + deps, + &mut generics_map, + impl_did, + params.ty_exprs()[0], // Self type + implementing_ty, ); - let exists = vcx.mk_exists_expr(trait_ty_decls, &[], vcx.mk_conj(&conjuncts)); - trait_impl_checks.push(exists); + + trait_impl_checks.push(self_check_expr); + + // let impl_args = + // deps.require_dep::(GArgs::new(impl_ctx, impl_trait_ref.args))?; + + // let mut conjuncts = Vec::new(); + // + // for trait_pred in impl_ctx + // .typing_env() + // .param_env + // .caller_bounds() + // .iter() + // .filter_map(ty::Clause::as_trait_clause) + // .map(ty::Binder::skip_binder) + // { + // let required_trait_impl_fun = + // deps.require_ref::(trait_pred.def_id())?.impl_fun; + // let predicate_args = deps.require_dep::(GArgs::new( + // impl_ctx, + // trait_pred.trait_ref.args, + // ))?; + // conjuncts.push(required_trait_impl_fun(predicate_args.get_ty())); + // } + + // Create an "exists" for each generic of the impl block + // let trait_ty_decls = vcx.alloc_slice( + // impl_params + // .ty_decls() + // .iter() + // .map(|dec| dec.upcast_ty::()) + // .collect::>() + // .as_slice(), + // ); + // let exists = vcx.mk_exists_expr(trait_ty_decls, &[], vcx.mk_conj(&conjuncts)); + // trait_impl_checks.push(exists); } { @@ -229,3 +247,48 @@ impl TaskEncoder for TraitEnc { }) } } + +fn encode_type<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, + generic_map: &mut HashMap, vir::ExprTyVal<'vir>>, + def_id: DefId, + base: vir::ExprTyVal<'vir>, + ty: ty::Ty<'vir>, +) -> vir::ExprGenBool<'vir, (), !> { + let decomp = RustTyDecomposition::from_ty(ty, def_id); + + if decomp.ty.specifics.is_param() { + let generic_arg = decomp.args.args()[0]; + return if generic_map.contains_key(&generic_arg) { + // If we have already seen this generic parameter, add an equality check to ensure it + // is consistent with previous occurrences + vcx.mk_eq_expr(base, generic_map[&decomp.args.args()[0]]) + } else { + // If this is the first time we see this generic parameter, add it to the map and + // continue encoding + generic_map.insert(decomp.args.args()[0], base); + vir::expr! {vcx; true} + }; + } + let ty_enc = deps.require_ref::(decomp.ty).unwrap(); + + let discr_check = vcx.mk_adt_discriminator_expr(base, ty_enc.ty_constructor.name().to_str()); + + // Walk the type and recursively encode all inner types + let inner_tys = decomp + .args + .args() + .into_iter() + .cloned() + .filter_map(ty::GenericArg::as_type); + + let mut conjuncts = vec![discr_check]; + for (i, inner_ty) in inner_tys.into_iter().enumerate() { + let new_base = ty_enc.ty_param_accessors[i].call()(base); + let inner_ty_check = encode_type(vcx, deps, generic_map, def_id, new_base, inner_ty); + conjuncts.push(inner_ty_check); + } + + vcx.mk_conj(&conjuncts) +} From 16f06fdc31c04a1e8a20a746d50b4e66c16717f0 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 18 Feb 2026 15:33:13 +0100 Subject: [PATCH 09/70] Clean up the type check encoding function --- .../src/encoders/ty/generics/traits.rs | 76 +++++++++++-------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 448563414bb..5db256e3346 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -130,7 +130,7 @@ impl TaskEncoder for TraitEnc { let mut trait_impl_checks = Vec::new(); for impl_did in tcx.all_impls(*task_key) { - // let impl_ctx = GParams::from(impl_did); + let impl_ctx = GParams::from(impl_did); // let impl_params = deps.require_dep::(impl_ctx)?; // let impl_trait_ref = @@ -144,11 +144,11 @@ impl TaskEncoder for TraitEnc { // when encoding the trait bounds of the impl block. let mut generics_map = HashMap::new(); - let self_check_expr = encode_type( + let self_check_expr = encode_type_check( vcx, deps, &mut generics_map, - impl_did, + impl_ctx, params.ty_exprs()[0], // Self type implementing_ty, ); @@ -248,46 +248,62 @@ impl TaskEncoder for TraitEnc { } } -fn encode_type<'vir>( +/// Encode a check that the type expression `expr` is the same as the rust type `ty`. +/// Additionally, collect the generic parameters of the impl block and map them to +/// their occurances in the type expression, such that they can be referred to when +/// encoding the trait bounds of the impl block. +/// +/// For example, for `expr` equal to `(T, i32)`, `T` would be mapped to an accessor +/// expression to the first member of the tuple type - `expr.2_tup.0` +fn encode_type_check<'vir>( vcx: &'vir vir::VirCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, generic_map: &mut HashMap, vir::ExprTyVal<'vir>>, - def_id: DefId, - base: vir::ExprTyVal<'vir>, + ctx: GParams<'vir>, + expr: vir::ExprTyVal<'vir>, ty: ty::Ty<'vir>, ) -> vir::ExprGenBool<'vir, (), !> { - let decomp = RustTyDecomposition::from_ty(ty, def_id); + let decomp = RustTyDecomposition::from_ty(ty, ctx); if decomp.ty.specifics.is_param() { - let generic_arg = decomp.args.args()[0]; - return if generic_map.contains_key(&generic_arg) { - // If we have already seen this generic parameter, add an equality check to ensure it - // is consistent with previous occurrences - vcx.mk_eq_expr(base, generic_map[&decomp.args.args()[0]]) - } else { - // If this is the first time we see this generic parameter, add it to the map and - // continue encoding - generic_map.insert(decomp.args.args()[0], base); - vir::expr! {vcx; true} + let arg = decomp.args.args()[0]; + + use std::collections::hash_map::Entry; + return match generic_map.entry(arg) { + Entry::Occupied(occ) => { + // Already seen this T: ensure this type matches the originally found + vcx.mk_eq_expr(expr, *occ.get()) + } + Entry::Vacant(vac) => { + // First time seeing T: map it to the current accessor path for future references + vac.insert(expr); + vir::expr! { vcx; true } + } }; } - let ty_enc = deps.require_ref::(decomp.ty).unwrap(); - let discr_check = vcx.mk_adt_discriminator_expr(base, ty_enc.ty_constructor.name().to_str()); + let ty_enc = deps + .require_ref::(decomp.ty) + .expect("Type constructor encoder should have encoded this type"); - // Walk the type and recursively encode all inner types - let inner_tys = decomp - .args - .args() - .into_iter() - .cloned() - .filter_map(ty::GenericArg::as_type); + let discr_check = vcx.mk_adt_discriminator_expr(expr, ty_enc.ty_constructor.name().to_str()); let mut conjuncts = vec![discr_check]; - for (i, inner_ty) in inner_tys.into_iter().enumerate() { - let new_base = ty_enc.ty_param_accessors[i].call()(base); - let inner_ty_check = encode_type(vcx, deps, generic_map, def_id, new_base, inner_ty); - conjuncts.push(inner_ty_check); + + // Collect checks for inner types + let inner_types = decomp.args.args().iter().filter_map(|arg| arg.as_type()); + for (i, inner_ty) in inner_types.enumerate() { + let accessor = ty_enc.ty_param_accessors[i]; + + let inner_base = accessor.call()(expr); + conjuncts.push(encode_type_check( + vcx, + deps, + generic_map, + ctx, + inner_base, + inner_ty, + )); } vcx.mk_conj(&conjuncts) From 20ebaace0a8e2b3b315f13d9b885adb4544f0b0c Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 18 Feb 2026 16:03:47 +0100 Subject: [PATCH 10/70] Clean up of the impl_fun_body --- .../src/encoders/ty/generics/traits.rs | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 5db256e3346..8a32542e0a1 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -61,7 +61,8 @@ impl TaskEncoder for TraitEnc { ) -> EncodeFullResult<'vir, Self> { vir::with_vcx(|vcx| { let tcx = vcx.tcx(); - let params = deps.require_dep::(GParams::from(*task_key))?; + let params = GParams::from(*task_key); + let enc_params = deps.require_dep::(params)?; let trait_name = vcx.alloc_str(tcx.item_name(task_key).as_str()); let type_did_fun_mapping = tcx .associated_items(task_key) @@ -92,7 +93,7 @@ impl TaskEncoder for TraitEnc { let impl_fun_idn = FunctionIdn::new( vir_format_identifier!(vcx, "{trait_name}_impl"), - vcx.alloc_slice(&(vec![vir::TYPE_TYVAL; params.ty_exprs().len()])), + vcx.alloc_slice(&(vec![vir::TYPE_TYVAL; enc_params.ty_exprs().len()])), vir::TYPE_BOOL, ); @@ -107,7 +108,7 @@ impl TaskEncoder for TraitEnc { let impl_fun_unknown_idn: FunctionIdn<'vir, (vir::ManyTyVal, vir::Int), vir::Bool> = { // Omit the Self type as it is known to be the "Unknown_type" - let unknown_params = vec![vir::TYPE_TYVAL; params.ty_exprs().len() - 1]; + let unknown_params = vec![vir::TYPE_TYVAL; enc_params.ty_exprs().len() - 1]; FunctionIdn::new( vir_format_identifier!(vcx, "{trait_name}_impl_unknown"), (vcx.alloc_slice(&unknown_params), vir::TYPE_INT), @@ -117,7 +118,7 @@ impl TaskEncoder for TraitEnc { let impl_fun_unknown = vcx.mk_function( impl_fun_unknown_idn, ( - vcx.alloc_slice(¶ms.ty_decls()[1..]), + vcx.alloc_slice(&enc_params.ty_decls()[1..]), vcx.mk_local_decl("non_unit", vir::TYPE_INT), ), &[], @@ -131,35 +132,36 @@ impl TaskEncoder for TraitEnc { for impl_did in tcx.all_impls(*task_key) { let impl_ctx = GParams::from(impl_did); - // let impl_params = deps.require_dep::(impl_ctx)?; - - // let impl_trait_ref = - // tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); - // dbg!(&impl_trait_ref.args); - - let implementing_ty = tcx.type_of(impl_did).instantiate_identity(); // Collect the locations of the generic parameters of the impl block from the // `Self` type and the trait arguments. This will allos us to refer to them // when encoding the trait bounds of the impl block. let mut generics_map = HashMap::new(); - let self_check_expr = encode_type_check( + // Add generics of the trait arguments to the map + generics_map.extend( + params + .rust_params() + .iter() + .zip(enc_params.ty_exprs().iter().copied()) + .skip(1), // Skip the Self type + ); + + let impl_self_ty = tcx.type_of(impl_did).instantiate_identity(); + let self_ty_expr = enc_params.ty_exprs()[0]; + // Encode the Self type check and collect the generic parameters mentioned + let self_ty_check = encode_type_check( vcx, deps, &mut generics_map, impl_ctx, - params.ty_exprs()[0], // Self type - implementing_ty, + self_ty_expr, + impl_self_ty, ); - trait_impl_checks.push(self_check_expr); - - // let impl_args = - // deps.require_dep::(GArgs::new(impl_ctx, impl_trait_ref.args))?; + // Construct the trait bound checks for this impl block + let trait_bound_checks = Vec::new(); - // let mut conjuncts = Vec::new(); - // // for trait_pred in impl_ctx // .typing_env() // .param_env @@ -177,17 +179,15 @@ impl TaskEncoder for TraitEnc { // conjuncts.push(required_trait_impl_fun(predicate_args.get_ty())); // } - // Create an "exists" for each generic of the impl block - // let trait_ty_decls = vcx.alloc_slice( - // impl_params - // .ty_decls() - // .iter() - // .map(|dec| dec.upcast_ty::()) - // .collect::>() - // .as_slice(), - // ); - // let exists = vcx.mk_exists_expr(trait_ty_decls, &[], vcx.mk_conj(&conjuncts)); - // trait_impl_checks.push(exists); + let trait_bound_checks = vcx.mk_conj(&trait_bound_checks); + + trait_impl_checks.push( + vcx.mk_bin_op_expr(vir::BinOpKind::And, self_ty_check, trait_bound_checks) + .downcast_ty(), + ); + + // let impl_args = + // deps.require_dep::(GArgs::new(impl_ctx, impl_trait_ref.args))?; } { @@ -200,9 +200,10 @@ impl TaskEncoder for TraitEnc { vir::TYPE_TYVAL, ); let self_is_unknown = - vcx.mk_eq_expr(params.ty_exprs()[0], unknown(non_unit_ex)); + vcx.mk_eq_expr(enc_params.ty_exprs()[0], unknown(non_unit_ex)); - let unknown_impls = impl_fun_unknown_idn(¶ms.ty_exprs()[1..], non_unit_ex); + let unknown_impls = + impl_fun_unknown_idn(&enc_params.ty_exprs()[1..], non_unit_ex); let exists_unknown = vcx.mk_exists_expr( vcx.alloc_slice(&[non_unit_decl]), @@ -218,7 +219,7 @@ impl TaskEncoder for TraitEnc { let impl_fun = vcx.mk_function( impl_fun_idn, - (params.ty_decls(),), + (enc_params.ty_decls(),), &[], &[], None, From 430abcc0acbd2e74f7f51da898b30aa7dde05a3c Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 18 Feb 2026 17:17:51 +0100 Subject: [PATCH 11/70] Fix wrong encoding of trait generic arguments --- .../src/encoders/ty/generics/traits.rs | 91 +++++++++---------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 8a32542e0a1..b796f6b6165 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -137,54 +137,53 @@ impl TaskEncoder for TraitEnc { // `Self` type and the trait arguments. This will allos us to refer to them // when encoding the trait bounds of the impl block. let mut generics_map = HashMap::new(); - - // Add generics of the trait arguments to the map - generics_map.extend( - params - .rust_params() - .iter() - .zip(enc_params.ty_exprs().iter().copied()) - .skip(1), // Skip the Self type - ); - - let impl_self_ty = tcx.type_of(impl_did).instantiate_identity(); - let self_ty_expr = enc_params.ty_exprs()[0]; - // Encode the Self type check and collect the generic parameters mentioned - let self_ty_check = encode_type_check( - vcx, - deps, - &mut generics_map, - impl_ctx, - self_ty_expr, - impl_self_ty, - ); + let mut checks = Vec::new(); + + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); + let rust_impl_args = trait_ref.args.iter().filter_map(|arg| arg.as_type()); + + for (&ty_expr, rust_ty) in enc_params.ty_exprs().iter().zip(rust_impl_args) { + let check = encode_type_check( + vcx, + deps, + &mut generics_map, + impl_ctx, + ty_expr, + rust_ty, + ); + checks.push(check); + } // Construct the trait bound checks for this impl block - let trait_bound_checks = Vec::new(); - - // for trait_pred in impl_ctx - // .typing_env() - // .param_env - // .caller_bounds() - // .iter() - // .filter_map(ty::Clause::as_trait_clause) - // .map(ty::Binder::skip_binder) - // { - // let required_trait_impl_fun = - // deps.require_ref::(trait_pred.def_id())?.impl_fun; - // let predicate_args = deps.require_dep::(GArgs::new( - // impl_ctx, - // trait_pred.trait_ref.args, - // ))?; - // conjuncts.push(required_trait_impl_fun(predicate_args.get_ty())); - // } - - let trait_bound_checks = vcx.mk_conj(&trait_bound_checks); - - trait_impl_checks.push( - vcx.mk_bin_op_expr(vir::BinOpKind::And, self_ty_check, trait_bound_checks) - .downcast_ty(), - ); + for trait_pred in impl_ctx + .typing_env() + .param_env + .caller_bounds() + .iter() + .filter_map(ty::Clause::as_trait_clause) + .map(ty::Binder::skip_binder) + { + let required_trait_impl_fun = + deps.require_ref::(trait_pred.def_id())?.impl_fun; + let args = trait_pred + .trait_ref + .args + .iter() + .map(|arg| { + generics_map.get(&arg).copied().unwrap_or_else(|| { + todo!("Encode for non-generic arguments in trait bounds") + }) + }) + .collect::>(); + checks.push(required_trait_impl_fun(&args)); + } + + // let trait_bound_checks = vcx.mk_conj(&trait_bound_checks); + + trait_impl_checks.push(vcx.mk_conj(&checks)); + // vcx.mk_bin_op_expr(vir::BinOpKind::And, self_ty_check, trait_bound_checks) + // .downcast_ty(), + // ); // let impl_args = // deps.require_dep::(GArgs::new(impl_ctx, impl_trait_ref.args))?; From 182b9882616ccf41dd4dfdaedc04d5aa00f5ed45 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 18 Feb 2026 17:40:36 +0100 Subject: [PATCH 12/70] Add corrected trait bound checks --- .../src/encoders/ty/generics/traits.rs | 59 ++++++++++++------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index b796f6b6165..7e312fbbc53 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -4,11 +4,11 @@ use prusti_rustc_interface::{ }; use rustc_hash::FxHashMap; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder, TaskEncoderDependencies}; -use vir::{CallableIdn, CastType, Dyn, FunctionIdn, vir_format_identifier}; +use vir::{CallableIdn, FunctionIdn, vir_format_identifier}; use crate::encoders::ty::{ RustTyDecomposition, - generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc}, + generics::{GParams, GenericParamsEnc}, lifted::TyConstructorEnc, }; @@ -137,11 +137,11 @@ impl TaskEncoder for TraitEnc { // `Self` type and the trait arguments. This will allos us to refer to them // when encoding the trait bounds of the impl block. let mut generics_map = HashMap::new(); - let mut checks = Vec::new(); let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); let rust_impl_args = trait_ref.args.iter().filter_map(|arg| arg.as_type()); + let mut checks = Vec::new(); for (&ty_expr, rust_ty) in enc_params.ty_exprs().iter().zip(rust_impl_args) { let check = encode_type_check( vcx, @@ -169,24 +169,13 @@ impl TaskEncoder for TraitEnc { .trait_ref .args .iter() - .map(|arg| { - generics_map.get(&arg).copied().unwrap_or_else(|| { - todo!("Encode for non-generic arguments in trait bounds") - }) - }) + .filter_map(|arg| arg.as_type()) + .map(|arg| assemble_type(vcx, deps, &generics_map, impl_ctx, arg)) .collect::>(); checks.push(required_trait_impl_fun(&args)); } - // let trait_bound_checks = vcx.mk_conj(&trait_bound_checks); - trait_impl_checks.push(vcx.mk_conj(&checks)); - // vcx.mk_bin_op_expr(vir::BinOpKind::And, self_ty_check, trait_bound_checks) - // .downcast_ty(), - // ); - - // let impl_args = - // deps.require_dep::(GArgs::new(impl_ctx, impl_trait_ref.args))?; } { @@ -266,10 +255,10 @@ fn encode_type_check<'vir>( let decomp = RustTyDecomposition::from_ty(ty, ctx); if decomp.ty.specifics.is_param() { - let arg = decomp.args.args()[0]; + let arg = decomp.args.args().first().expect("Param missing arg"); use std::collections::hash_map::Entry; - return match generic_map.entry(arg) { + return match generic_map.entry(*arg) { Entry::Occupied(occ) => { // Already seen this T: ensure this type matches the originally found vcx.mk_eq_expr(expr, *occ.get()) @@ -282,9 +271,7 @@ fn encode_type_check<'vir>( }; } - let ty_enc = deps - .require_ref::(decomp.ty) - .expect("Type constructor encoder should have encoded this type"); + let ty_enc = deps.require_ref::(decomp.ty).unwrap(); let discr_check = vcx.mk_adt_discriminator_expr(expr, ty_enc.ty_constructor.name().to_str()); @@ -308,3 +295,33 @@ fn encode_type_check<'vir>( vcx.mk_conj(&conjuncts) } + +/// Assemble a VIR type using the map of generic parameters we have collected earlier. +fn assemble_type<'vir>( + vcx: &vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, + generics_map: &HashMap, vir::ExprTyVal<'vir>>, + ctx: GParams<'vir>, + ty: ty::Ty<'vir>, +) -> vir::ExprTyVal<'vir> { + let decomp = RustTyDecomposition::from_ty(ty, ctx); + + if decomp.ty.specifics.is_param() { + let arg = decomp.args.args().first().expect("Param missing arg"); + return *generics_map + .get(arg) + .expect("The generic should have been inserted, otherwise the parameter is unbound"); + } + + let ty_enc = deps.require_ref::(decomp.ty).unwrap(); + + let inner_args = decomp + .args + .args() + .iter() + .filter_map(|arg| arg.as_type()) + .map(|inner_ty| assemble_type(vcx, deps, generics_map, ctx, inner_ty)) + .collect::>(); + + (ty_enc.ty_constructor)(&inner_args, &[]) +} From 2386ad045023e951e92abac39baf60f4955e066d Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 18 Feb 2026 17:49:31 +0100 Subject: [PATCH 13/70] Remove existential from unknown type check --- .../src/encoders/ty/generics/traits.rs | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 7e312fbbc53..093381b8c54 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -179,27 +179,21 @@ impl TaskEncoder for TraitEnc { } { - // Add a case for unknown types that might implement the trait - let non_unit_decl = vcx.mk_local_decl("non_unit", vir::TYPE_INT); - let non_unit_ex = vcx.mk_local_ex(non_unit_decl); - let unknown: FunctionIdn<'_, vir::Int, vir::TyVal> = FunctionIdn::new( - vir::vir_format_identifier!(vcx, "Unknown_type"), - vir::TYPE_INT, - vir::TYPE_TYVAL, - ); - let self_is_unknown = - vcx.mk_eq_expr(enc_params.ty_exprs()[0], unknown(non_unit_ex)); + // Case for unknown types + let self_expr = enc_params.ty_exprs()[0]; + + let is_unknown_type = vcx.mk_adt_discriminator_expr(self_expr, "Unknown_type"); + + let unknown_id_destructor = + vcx.mk_adt_destructor("non_unit", vir::TYPE_TYVAL, vir::TYPE_INT); + let extracted_id = unknown_id_destructor.call()(self_expr); let unknown_impls = - impl_fun_unknown_idn(&enc_params.ty_exprs()[1..], non_unit_ex); + impl_fun_unknown_idn(&enc_params.ty_exprs()[1..], extracted_id); - let exists_unknown = vcx.mk_exists_expr( - vcx.alloc_slice(&[non_unit_decl]), - &[], - vcx.mk_conj(&[self_is_unknown, unknown_impls]), - ); + let unknown_check = vcx.mk_conj(&[is_unknown_type, unknown_impls]); - trait_impl_checks.push(exists_unknown); + trait_impl_checks.push(unknown_check); } vcx.mk_disj(&trait_impl_checks) From fbda8353245bd518df34e1516261d49f46bf9b96 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 18 Feb 2026 23:07:17 +0100 Subject: [PATCH 14/70] Add initial special encoding for the `Sized` trait --- .../src/encoders/ty/generics/traits.rs | 4 ++ .../src/encoders/ty/lifted/ty_constructor.rs | 38 +++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 093381b8c54..d064c47a2bd 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -49,6 +49,10 @@ impl TaskEncoder for TraitEnc { fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { for trait_enc in TraitEnc::all_outputs_local_no_errors() { + // Skip `Sized`, as we need a special encoding for its body. Encoded by `TyConstructorEnc` + if trait_enc.trait_domain.name == "t_Sized" { + continue; + } program.add_domain(trait_enc.trait_domain); program.add_function(trait_enc.impl_fun); program.add_function(trait_enc.impl_fun_unknown); diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index bc0e8ce9ddf..24745674f6f 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -48,7 +48,11 @@ impl<'vir> TyConstructorEncOutputRef<'vir> { impl<'vir> OutputRefAny for TyConstructorEncOutputRef<'vir> {} -pub type TyConstructorEncOutput<'vir> = vir::AdtConstructor<'vir>; +#[derive(Debug, Clone)] +pub struct TyConstructorEncOutput<'vir> { + constructor: vir::AdtConstructor<'vir>, + is_sized: bool, +} /// Encodes the lifted representation of a Rust type constructor (e.g. Option, /// Vec, user-defined ADTs). @@ -127,12 +131,21 @@ impl TaskEncoder for TyConstructorEnc { .collect::>>(); let variant = vcx.mk_adt_constructor(type_function_ident.name().to_str(), vcx.alloc_slice(&args)); - Ok((variant, ())) + Ok(( + TyConstructorEncOutput { + constructor: variant, + is_sized: true, + }, + (), + )) }) } fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { - let mut constructors = Self::all_outputs_local_no_errors(); + let (mut constructors, _sized): (Vec<_>, Vec<_>) = Self::all_outputs_local_no_errors() + .into_iter() + .map(|out| (out.constructor, out.is_sized)) + .unzip(); vir::with_vcx(|vcx| { let args = vcx.alloc_array(&[vcx.mk_local_decl("non_unit", vir::TYPE_INT)]); let unknown = vcx.mk_adt_constructor("Unknown_type", args); @@ -143,6 +156,25 @@ impl TaskEncoder for TyConstructorEnc { vcx.alloc_slice(&constructors), ); program.add_adt(adt); + + // Since we know all type constructors now, we can emit the `Sized` trait + // TODO: Correct implementation of `Sized` + let sized_impl_fun_idn: FunctionIdn<'vir, vir::TyVal, vir::Bool> = FunctionIdn::new( + vir::vir_format_identifier!(vcx, "Sized_impl"), + vir::TYPE_TYVAL, + vir::TYPE_BOOL, + ); + let self_decl = vcx.mk_local_decl("Self", vir::TYPE_TYVAL); + let sized_impl_fun = vcx.mk_function( + sized_impl_fun_idn, + (self_decl,), + &[], + &[], + None, + Some(vir::expr! {vcx; true}), + ); + + program.add_function(sized_impl_fun); }) } } From df3b6822086da5b0e39628570c9ae0c0dcb3e981 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 19 Feb 2026 13:29:47 +0100 Subject: [PATCH 15/70] Add const generics to trait impl function defs --- .../src/encoders/ty/generics/traits.rs | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index d064c47a2bd..6953f42c8a3 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -17,13 +17,13 @@ pub struct TraitEnc; #[derive(Debug, Clone)] pub struct TraitData<'vir> { pub trait_name: &'vir str, + pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, pub type_did_fun_mapping: FxHashMap>, - pub impl_fun: FunctionIdn<'vir, vir::ManyTyVal, vir::Bool>, } #[derive(Debug, Clone)] pub struct TraitImplRef<'vir> { - pub impl_fun: FunctionIdn<'vir, vir::ManyTyVal, vir::Bool>, + pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, } impl OutputRefAny for TraitImplRef<'_> {} @@ -97,7 +97,7 @@ impl TaskEncoder for TraitEnc { let impl_fun_idn = FunctionIdn::new( vir_format_identifier!(vcx, "{trait_name}_impl"), - vcx.alloc_slice(&(vec![vir::TYPE_TYVAL; enc_params.ty_exprs().len()])), + (enc_params.ty_args(), enc_params.const_args()), vir::TYPE_BOOL, ); @@ -110,19 +110,24 @@ impl TaskEncoder for TraitEnc { }, )?; - let impl_fun_unknown_idn: FunctionIdn<'vir, (vir::ManyTyVal, vir::Int), vir::Bool> = { + let impl_fun_unknown_idn: FunctionIdn< + 'vir, + (vir::ManyTyVal, vir::ManyCSnap, vir::Int), + vir::Bool, + > = { // Omit the Self type as it is known to be the "Unknown_type" - let unknown_params = vec![vir::TYPE_TYVAL; enc_params.ty_exprs().len() - 1]; + let unknown_args = &enc_params.ty_args()[1..]; FunctionIdn::new( vir_format_identifier!(vcx, "{trait_name}_impl_unknown"), - (vcx.alloc_slice(&unknown_params), vir::TYPE_INT), + (unknown_args, enc_params.const_args(), vir::TYPE_INT), vir::TYPE_BOOL, ) }; let impl_fun_unknown = vcx.mk_function( impl_fun_unknown_idn, ( - vcx.alloc_slice(&enc_params.ty_decls()[1..]), + &enc_params.ty_decls()[1..], + enc_params.const_decls(), vcx.mk_local_decl("non_unit", vir::TYPE_INT), ), &[], @@ -140,7 +145,7 @@ impl TaskEncoder for TraitEnc { // Collect the locations of the generic parameters of the impl block from the // `Self` type and the trait arguments. This will allos us to refer to them // when encoding the trait bounds of the impl block. - let mut generics_map = HashMap::new(); + let mut generics_map = FxHashMap::default(); let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); let rust_impl_args = trait_ref.args.iter().filter_map(|arg| arg.as_type()); @@ -176,7 +181,7 @@ impl TaskEncoder for TraitEnc { .filter_map(|arg| arg.as_type()) .map(|arg| assemble_type(vcx, deps, &generics_map, impl_ctx, arg)) .collect::>(); - checks.push(required_trait_impl_fun(&args)); + checks.push(required_trait_impl_fun(&args, &[])); } trait_impl_checks.push(vcx.mk_conj(&checks)); @@ -192,8 +197,11 @@ impl TaskEncoder for TraitEnc { vcx.mk_adt_destructor("non_unit", vir::TYPE_TYVAL, vir::TYPE_INT); let extracted_id = unknown_id_destructor.call()(self_expr); - let unknown_impls = - impl_fun_unknown_idn(&enc_params.ty_exprs()[1..], extracted_id); + let unknown_impls = impl_fun_unknown_idn( + &enc_params.ty_exprs()[1..], + enc_params.const_exprs(), + extracted_id, + ); let unknown_check = vcx.mk_conj(&[is_unknown_type, unknown_impls]); @@ -205,7 +213,7 @@ impl TaskEncoder for TraitEnc { let impl_fun = vcx.mk_function( impl_fun_idn, - (enc_params.ty_decls(),), + (enc_params.ty_decls(), enc_params.const_decls()), &[], &[], None, @@ -245,7 +253,7 @@ impl TaskEncoder for TraitEnc { fn encode_type_check<'vir>( vcx: &'vir vir::VirCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, - generic_map: &mut HashMap, vir::ExprTyVal<'vir>>, + generic_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, ctx: GParams<'vir>, expr: vir::ExprTyVal<'vir>, ty: ty::Ty<'vir>, @@ -298,7 +306,7 @@ fn encode_type_check<'vir>( fn assemble_type<'vir>( vcx: &vir::VirCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, - generics_map: &HashMap, vir::ExprTyVal<'vir>>, + generics_map: &FxHashMap, vir::ExprTyVal<'vir>>, ctx: GParams<'vir>, ty: ty::Ty<'vir>, ) -> vir::ExprTyVal<'vir> { From c4c962584914f7addeb274e8c483f629a9d981f8 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 19 Feb 2026 14:35:00 +0100 Subject: [PATCH 16/70] Implement handling of const generics in trait impls --- .../src/encoders/ty/generics/traits.rs | 107 +++++++++++++++--- 1 file changed, 90 insertions(+), 17 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 6953f42c8a3..f69b78c2378 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -145,7 +145,8 @@ impl TaskEncoder for TraitEnc { // Collect the locations of the generic parameters of the impl block from the // `Self` type and the trait arguments. This will allos us to refer to them // when encoding the trait bounds of the impl block. - let mut generics_map = FxHashMap::default(); + let mut ty_generics_map = FxHashMap::default(); + let mut const_generics_map = FxHashMap::default(); let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); let rust_impl_args = trait_ref.args.iter().filter_map(|arg| arg.as_type()); @@ -155,7 +156,8 @@ impl TaskEncoder for TraitEnc { let check = encode_type_check( vcx, deps, - &mut generics_map, + &mut ty_generics_map, + &mut const_generics_map, impl_ctx, ty_expr, rust_ty, @@ -163,6 +165,9 @@ impl TaskEncoder for TraitEnc { checks.push(check); } + dbg!(&ty_generics_map); + dbg!(&const_generics_map); + // Construct the trait bound checks for this impl block for trait_pred in impl_ctx .typing_env() @@ -174,14 +179,33 @@ impl TaskEncoder for TraitEnc { { let required_trait_impl_fun = deps.require_ref::(trait_pred.def_id())?.impl_fun; - let args = trait_pred - .trait_ref - .args + let trait_args = trait_pred.trait_ref.args; + let ty_args = trait_args .iter() .filter_map(|arg| arg.as_type()) - .map(|arg| assemble_type(vcx, deps, &generics_map, impl_ctx, arg)) + .map(|arg| { + assemble_type( + vcx, + deps, + &ty_generics_map, + &const_generics_map, + impl_ctx, + arg, + ) + }) + .collect::>(); + + let const_args = trait_args + .iter() + .filter(|arg| arg.as_const().is_some()) + .map(|arg| match const_generics_map.get(&arg) { + Some(mapped_const) => *mapped_const, + None => { + todo!("Const is not generic") + } + }) .collect::>(); - checks.push(required_trait_impl_fun(&args, &[])); + checks.push(required_trait_impl_fun(&ty_args, &const_args)); } trait_impl_checks.push(vcx.mk_conj(&checks)); @@ -253,18 +277,20 @@ impl TaskEncoder for TraitEnc { fn encode_type_check<'vir>( vcx: &'vir vir::VirCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, - generic_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, + ty_generics_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, + const_generics_map: &mut FxHashMap, vir::ExprCSnap<'vir>>, ctx: GParams<'vir>, expr: vir::ExprTyVal<'vir>, ty: ty::Ty<'vir>, ) -> vir::ExprGenBool<'vir, (), !> { let decomp = RustTyDecomposition::from_ty(ty, ctx); + // dbg!(&decomp); if decomp.ty.specifics.is_param() { let arg = decomp.args.args().first().expect("Param missing arg"); use std::collections::hash_map::Entry; - return match generic_map.entry(*arg) { + return match ty_generics_map.entry(*arg) { Entry::Occupied(occ) => { // Already seen this T: ensure this type matches the originally found vcx.mk_eq_expr(expr, *occ.get()) @@ -288,17 +314,41 @@ fn encode_type_check<'vir>( for (i, inner_ty) in inner_types.enumerate() { let accessor = ty_enc.ty_param_accessors[i]; - let inner_base = accessor.call()(expr); + let inner_expr = accessor.call()(expr); conjuncts.push(encode_type_check( vcx, deps, - generic_map, + ty_generics_map, + const_generics_map, ctx, - inner_base, + inner_expr, inner_ty, )); } + // Collect the "locations" of const parameters and assert equality for repeated occurances + let consts = decomp + .args + .args() + .iter() + .filter(|arg| arg.as_const().is_some()); + for (i, const_) in consts.enumerate() { + let accessor = ty_enc.const_param_accessors[i]; + let const_expr = accessor.call()(expr); + + use std::collections::hash_map::Entry; + match const_generics_map.entry(*const_) { + Entry::Occupied(occ) => { + // Already seen this const parameter: ensure this const expression matches the originally found + conjuncts.push(vcx.mk_eq_expr(const_expr, *occ.get())); + } + Entry::Vacant(vac) => { + // First time seeing this const parameter: map it to the current accessor path for future references + vac.insert(const_expr); + } + } + } + vcx.mk_conj(&conjuncts) } @@ -306,7 +356,8 @@ fn encode_type_check<'vir>( fn assemble_type<'vir>( vcx: &vir::VirCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, - generics_map: &FxHashMap, vir::ExprTyVal<'vir>>, + ty_generics_map: &FxHashMap, vir::ExprTyVal<'vir>>, + const_generics_map: &FxHashMap, vir::ExprCSnap<'vir>>, ctx: GParams<'vir>, ty: ty::Ty<'vir>, ) -> vir::ExprTyVal<'vir> { @@ -314,20 +365,42 @@ fn assemble_type<'vir>( if decomp.ty.specifics.is_param() { let arg = decomp.args.args().first().expect("Param missing arg"); - return *generics_map + return *ty_generics_map .get(arg) .expect("The generic should have been inserted, otherwise the parameter is unbound"); } let ty_enc = deps.require_ref::(decomp.ty).unwrap(); - let inner_args = decomp + let inner_ty_args = decomp .args .args() .iter() .filter_map(|arg| arg.as_type()) - .map(|inner_ty| assemble_type(vcx, deps, generics_map, ctx, inner_ty)) + .map(|inner_ty| { + assemble_type( + vcx, + deps, + ty_generics_map, + const_generics_map, + ctx, + inner_ty, + ) + }) + .collect::>(); + + let inner_const_args = decomp + .args + .args() + .iter() + .filter(|arg| arg.as_const().is_some()) + .map(|arg| match const_generics_map.get(arg) { + Some(mapped_const) => *mapped_const, + None => { + todo!("Const is not generic") + } + }) .collect::>(); - (ty_enc.ty_constructor)(&inner_args, &[]) + (ty_enc.ty_constructor)(&inner_ty_args, &inner_const_args) } From b8f07f6c9c02c0aef1ed88519a4543a57d5e92e5 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 19 Feb 2026 17:38:15 +0100 Subject: [PATCH 17/70] Correctly handle concretely valued const generics --- .../src/encoders/ty/generics/traits.rs | 113 +++++++++++------- 1 file changed, 70 insertions(+), 43 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index f69b78c2378..d47e235f1e5 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -1,15 +1,16 @@ -use prusti_rustc_interface::{ - middle::ty::{self, AssocKind}, - span::def_id::DefId, -}; +use prusti_rustc_interface::{middle::ty, span::def_id::DefId}; use rustc_hash::FxHashMap; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder, TaskEncoderDependencies}; use vir::{CallableIdn, FunctionIdn, vir_format_identifier}; -use crate::encoders::ty::{ - RustTyDecomposition, - generics::{GParams, GenericParamsEnc}, - lifted::TyConstructorEnc, +use crate::encoders::{ + ConstEnc, + r#const::ConstEncTask, + ty::{ + RustTyDecomposition, + generics::{GParams, GenericParamsEnc}, + lifted::TyConstructorEnc, + }, }; pub struct TraitEnc; @@ -71,7 +72,7 @@ impl TaskEncoder for TraitEnc { let type_did_fun_mapping = tcx .associated_items(task_key) .in_definition_order() - .filter(|item| matches!(item.kind, AssocKind::Type { data: _ })) + .filter(|item| matches!(item.kind, ty::AssocKind::Type { data: _ })) .map(|item| { let params_type = deps .require_dep::(GParams::from(item.def_id)) @@ -165,9 +166,6 @@ impl TaskEncoder for TraitEnc { checks.push(check); } - dbg!(&ty_generics_map); - dbg!(&const_generics_map); - // Construct the trait bound checks for this impl block for trait_pred in impl_ctx .typing_env() @@ -197,12 +195,23 @@ impl TaskEncoder for TraitEnc { let const_args = trait_args .iter() - .filter(|arg| arg.as_const().is_some()) - .map(|arg| match const_generics_map.get(&arg) { - Some(mapped_const) => *mapped_const, - None => { - todo!("Const is not generic") + .filter_map(|arg| arg.as_const()) + .map(|const_| match const_.kind() { + ty::ConstKind::Param(..) => const_generics_map + .get(&const_.into()) + .copied() + .expect("The const generic should have been bound in the map"), + ty::ConstKind::Value(v) => { + let task = ConstEncTask::Ty { + const_, + ty: v.ty, + context: impl_ctx, + }; + deps.require_dep::(task).unwrap() } + _ => unimplemented!( + "other kinds of const parameters not supported yet" + ), }) .collect::>(); checks.push(required_trait_impl_fun(&ty_args, &const_args)); @@ -285,7 +294,6 @@ fn encode_type_check<'vir>( ) -> vir::ExprGenBool<'vir, (), !> { let decomp = RustTyDecomposition::from_ty(ty, ctx); - // dbg!(&decomp); if decomp.ty.specifics.is_param() { let arg = decomp.args.args().first().expect("Param missing arg"); @@ -309,8 +317,10 @@ fn encode_type_check<'vir>( let mut conjuncts = vec![discr_check]; + let args = decomp.args.args(); + // Collect checks for inner types - let inner_types = decomp.args.args().iter().filter_map(|arg| arg.as_type()); + let inner_types = args.iter().filter_map(|arg| arg.as_type()); for (i, inner_ty) in inner_types.enumerate() { let accessor = ty_enc.ty_param_accessors[i]; @@ -327,25 +337,35 @@ fn encode_type_check<'vir>( } // Collect the "locations" of const parameters and assert equality for repeated occurances - let consts = decomp - .args - .args() - .iter() - .filter(|arg| arg.as_const().is_some()); + let consts = args.iter().filter_map(|arg| arg.as_const()); for (i, const_) in consts.enumerate() { let accessor = ty_enc.const_param_accessors[i]; let const_expr = accessor.call()(expr); - use std::collections::hash_map::Entry; - match const_generics_map.entry(*const_) { - Entry::Occupied(occ) => { - // Already seen this const parameter: ensure this const expression matches the originally found - conjuncts.push(vcx.mk_eq_expr(const_expr, *occ.get())); + match const_.kind() { + ty::ConstKind::Param(..) => { + use std::collections::hash_map::Entry; + match const_generics_map.entry(const_.into()) { + Entry::Occupied(occ) => { + // Already seen this const parameter: ensure this const expression matches the originally found + conjuncts.push(vcx.mk_eq_expr(const_expr, *occ.get())); + } + Entry::Vacant(vac) => { + // First time seeing this const parameter: map it to the current accessor path for future references + vac.insert(const_expr); + } + } } - Entry::Vacant(vac) => { - // First time seeing this const parameter: map it to the current accessor path for future references - vac.insert(const_expr); + ty::ConstKind::Value(val) => { + let task = ConstEncTask::Ty { + const_, + ty: val.ty, + context: ctx, + }; + let const_value = deps.require_dep::(task).unwrap(); + conjuncts.push(vcx.mk_eq_expr(const_expr, const_value)); } + _ => unimplemented!("other kinds of const parameters not supported yet"), } } @@ -372,9 +392,9 @@ fn assemble_type<'vir>( let ty_enc = deps.require_ref::(decomp.ty).unwrap(); - let inner_ty_args = decomp - .args - .args() + let args = decomp.args.args(); + + let inner_ty_args = args .iter() .filter_map(|arg| arg.as_type()) .map(|inner_ty| { @@ -389,16 +409,23 @@ fn assemble_type<'vir>( }) .collect::>(); - let inner_const_args = decomp - .args - .args() + let inner_const_args = args .iter() - .filter(|arg| arg.as_const().is_some()) - .map(|arg| match const_generics_map.get(arg) { - Some(mapped_const) => *mapped_const, - None => { - todo!("Const is not generic") + .filter_map(|arg| arg.as_const()) + .map(|const_| match const_.kind() { + ty::ConstKind::Param(..) => const_generics_map + .get(&const_.into()) + .copied() + .expect("The const generic should have been bound in the map"), + ty::ConstKind::Value(val) => { + let task = ConstEncTask::Ty { + const_: const_, + ty: val.ty, + context: ctx, + }; + deps.require_dep::(task).unwrap() } + _ => unimplemented!("other kinds of const parameters not supported yet"), }) .collect::>(); From bd9383e44b581d7b477afc7fbaef3bef91584e15 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Fri, 20 Feb 2026 18:02:19 +0100 Subject: [PATCH 18/70] Implement the Sized check without opaque and unknown --- .../src/encoders/ty/generics/traits.rs | 2 + .../src/encoders/ty/lifted/ty_constructor.rs | 101 ++++++++++++++++-- prusti-encoder/src/encoders/ty/rust_ty.rs | 70 ++++++++++++ 3 files changed, 163 insertions(+), 10 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index d47e235f1e5..30f7d29c142 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -217,6 +217,8 @@ impl TaskEncoder for TraitEnc { checks.push(required_trait_impl_fun(&ty_args, &const_args)); } + // TODO: Construct the associated type bound checks for this impl block + trait_impl_checks.push(vcx.mk_conj(&checks)); } diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index 24745674f6f..03f30e2252d 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -1,7 +1,12 @@ +use prusti_rustc_interface::middle::ty; + use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder}; use vir::{CallableIdn, CastType, FunctionIdn, HasType}; -use crate::encoders::ty::{RustTy, generics::GenericParamsEnc}; +use crate::encoders::ty::{ + RustTy, RustTyDecomposition, TySpecifics, + generics::{GArgs, GParams, GenericParamsEnc}, +}; use super::r#typeof::{TypeOfEnc, TypeOfEncOutputRef}; @@ -51,7 +56,7 @@ impl<'vir> OutputRefAny for TyConstructorEncOutputRef<'vir> {} #[derive(Debug, Clone)] pub struct TyConstructorEncOutput<'vir> { constructor: vir::AdtConstructor<'vir>, - is_sized: bool, + sized_check: vir::ExprBool<'vir>, } /// Encodes the lifted representation of a Rust type constructor (e.g. Option, @@ -129,12 +134,51 @@ impl TaskEncoder for TyConstructorEnc { .map(|d| vcx.mk_local_decl(d.name, d.ty).upcast_ty()), ) .collect::>>(); - let variant = + let constructor = vcx.mk_adt_constructor(type_function_ident.name().to_str(), vcx.alloc_slice(&args)); + let sized_check = { + // Use a local expression named "Self" to build the function body + let self_decl = vcx.mk_local_decl("Self", vir::TYPE_TYVAL); + let self_expr = vcx.mk_local_ex(self_decl); + let is_this_type = + vcx.mk_adt_discriminator_expr(self_expr, type_function_ident.name().to_str()); + + let sized_impl_fun_idn: FunctionIdn<'vir, vir::TyVal, vir::Bool> = FunctionIdn::new( + vir::vir_format_identifier!(vcx, "Sized_impl"), + vir::TYPE_TYVAL, + vir::TYPE_BOOL, + ); + + let is_sized = { + let identity_args = GArgs::new(task_key.params, task_key.params.rust_params()); + let decomp = RustTyDecomposition { + ty: *task_key, + args: identity_args, + maybe_inhabited: true, + }; + check_sizedness(vcx.tcx(), decomp) + }; + + match is_sized { + Sizedness::Definite(true) => is_this_type, + Sizedness::Definite(false) => vir::expr! {vcx; false }, + Sizedness::ParamDependent(param) => { + let param_idx = task_key + .params + .rust_params() + .iter() + .position(|p| p == param) + .unwrap(); + let param_ty = ty_accessor_functions[param_idx].call()(self_expr); + + vir::expr! { vcx; (is_this_type) == > ([sized_impl_fun_idn](param_ty)) } + } + } + }; Ok(( TyConstructorEncOutput { - constructor: variant, - is_sized: true, + constructor, + sized_check, }, (), )) @@ -142,11 +186,13 @@ impl TaskEncoder for TyConstructorEnc { } fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { - let (mut constructors, _sized): (Vec<_>, Vec<_>) = Self::all_outputs_local_no_errors() - .into_iter() - .map(|out| (out.constructor, out.is_sized)) - .unzip(); + let (mut constructors, sized_checks): (Vec<_>, Vec<_>) = + Self::all_outputs_local_no_errors() + .into_iter() + .map(|out| (out.constructor, out.sized_check)) + .unzip(); vir::with_vcx(|vcx| { + vcx.tcx(); let args = vcx.alloc_array(&[vcx.mk_local_decl("non_unit", vir::TYPE_INT)]); let unknown = vcx.mk_adt_constructor("Unknown_type", args); constructors.push(unknown); @@ -171,10 +217,45 @@ impl TaskEncoder for TyConstructorEnc { &[], &[], None, - Some(vir::expr! {vcx; true}), + Some(vcx.mk_disj(&sized_checks)), ); program.add_function(sized_impl_fun); }) } } + +#[derive(Debug, Clone)] +enum Sizedness<'tcx> { + Definite(bool), + ParamDependent(ty::GenericArg<'tcx>), +} + +fn check_sizedness<'a>(tcx: ty::TyCtxt<'a>, decomp: RustTyDecomposition<'a>) -> Sizedness<'a> { + let ctx = decomp.args.context(); + if decomp + .ty + .rust_ty + .is_some_and(|ty| ty.is_sized(tcx, ctx.typing_env())) + { + return Sizedness::Definite(true); + } + match &decomp.ty.specifics { + TySpecifics::StructLike(data) => { + // Only need to check the last field of the struct for unsizedness + if let Some(last_field) = data.fields.last() { + // The type is not definitely sized. Need to recurse on the field's type + let normalized_decomp = last_field.ty().decompose_normalize(decomp.args); + + check_sizedness(tcx, normalized_decomp) + } else { + Sizedness::Definite(true) // Empty structs are Sized + } + } + TySpecifics::Param(_) => Sizedness::ParamDependent(decomp.args.args()[0]), + TySpecifics::ArrayLike(data) if data.slice => Sizedness::Definite(false), + TySpecifics::Opaque(_) => unimplemented!("Is an opaque type sized?"), + // Builtin, Enums, Primitives, References, and fixed-size Arrays are always Sized. + _ => Sizedness::Definite(true), + } +} diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index 0e7ad26bdc5..270b77c6822 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -73,6 +73,7 @@ impl<'tcx> RustTyDecomposition<'tcx> { let data = RustTyData { name: symbol::Symbol::intern("Real"), params: GParams::empty(), + rust_ty: None, }; let specifics = TySpecifics::Builtin(RustBuiltinData::BuiltinReal); Self { @@ -243,6 +244,7 @@ pub type RustBuiltin<'tcx> = >::BuiltinData; pub struct RustTyData<'tcx> { pub name: symbol::Symbol, pub params: GParams<'tcx>, + pub rust_ty: Option>, } impl<'tcx> RustTyData<'tcx> { @@ -300,6 +302,7 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { let data = RustTyData { name: symbol::Symbol::intern(&name), params, + rust_ty: Some(ErasedTy::new(ty)), }; let specifics = TySpecifics::from_ty(ty); let maybe_inhabited = @@ -318,6 +321,7 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { let data = RustTyData { name: symbol::Symbol::intern(&name), params, + rust_ty: Some(ErasedTy::new(ty)), }; let specifics = TySpecifics::from_prim_ty(ty); RustTyDecomposition { @@ -635,3 +639,69 @@ impl<'tcx> TySpecifics<'tcx, RustTyDatas> { vir::with_vcx(|vcx| ty::Const::new_param(vcx.tcx(), param)) } } + +/// A wrapper around `ty::Ty` that provides a canonical, "erased" version of a type. +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ErasedTy<'tcx>(ty::Ty<'tcx>); + +impl<'tcx> ErasedTy<'tcx> { + fn new(ty: ty::Ty<'tcx>) -> Self { + ErasedTy(to_generic_ty(ty)) + } +} + +impl<'tcx> Deref for ErasedTy<'tcx> { + type Target = ty::Ty<'tcx>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn to_generic_ty<'tcx>(ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { + match ty.kind() { + ty::Adt(adt_def, _) => { + vir::with_vcx(|vcx| vcx.tcx().type_of(adt_def.did()).instantiate_identity()) + } + + ty::Ref(_, inner_ty, mutbl) => { + let generic_inner = to_generic_ty(*inner_ty); + vir::with_vcx(|vcx| { + ty::Ty::new_ref( + vcx.tcx(), + vcx.tcx().lifetimes.re_erased, + generic_inner, + *mutbl, + ) + }) + } + + ty::RawPtr(inner_ty, mutbl) => { + let generic_inner = to_generic_ty(*inner_ty); + vir::with_vcx(|vcx| ty::Ty::new_ptr(vcx.tcx(), generic_inner, *mutbl)) + } + + ty::Param(_) | ty::Alias(..) => { + vir::with_vcx(|vcx| ty::Ty::new_param(vcx.tcx(), 0, symbol::Symbol::intern("T"))) + } + + ty::Slice(inner_ty) => { + let generic_inner = to_generic_ty(*inner_ty); + vir::with_vcx(|vcx| ty::Ty::new_slice(vcx.tcx(), generic_inner)) + } + + ty::Array(inner_ty, _) => { + let generic_inner = to_generic_ty(*inner_ty); + vir::with_vcx(|vcx| ty::Ty::new_array(vcx.tcx(), generic_inner, 0_u64)) + } + + ty::Tuple(tys) => vir::with_vcx(|vcx| { + let generic_tys: Vec<_> = (0..tys.len()) + .map(|i| ty::Ty::new_param(vcx.tcx(), i as u32, symbol::Symbol::intern("T"))) + .collect(); + ty::Ty::new_tup(vcx.tcx(), &generic_tys) + }), + + _ => ty, + } +} From 88e31a4dfceb6b1a4437245a0627b26364668090 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Sat, 21 Feb 2026 01:28:28 +0100 Subject: [PATCH 19/70] Move sizedness resolution to RustTy --- .../src/encoders/ty/lifted/ty_constructor.rs | 85 +++-------- prusti-encoder/src/encoders/ty/rust_ty.rs | 133 +++++++++++------- 2 files changed, 106 insertions(+), 112 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index 03f30e2252d..9415f12f798 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -1,12 +1,7 @@ -use prusti_rustc_interface::middle::ty; - use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder}; use vir::{CallableIdn, CastType, FunctionIdn, HasType}; -use crate::encoders::ty::{ - RustTy, RustTyDecomposition, TySpecifics, - generics::{GArgs, GParams, GenericParamsEnc}, -}; +use crate::encoders::ty::{RustTy, Sizedness, generics::GenericParamsEnc}; use super::r#typeof::{TypeOfEnc, TypeOfEncOutputRef}; @@ -148,31 +143,26 @@ impl TaskEncoder for TyConstructorEnc { vir::TYPE_TYVAL, vir::TYPE_BOOL, ); - - let is_sized = { - let identity_args = GArgs::new(task_key.params, task_key.params.rust_params()); - let decomp = RustTyDecomposition { - ty: *task_key, - args: identity_args, - maybe_inhabited: true, - }; - check_sizedness(vcx.tcx(), decomp) - }; - - match is_sized { - Sizedness::Definite(true) => is_this_type, - Sizedness::Definite(false) => vir::expr! {vcx; false }, - Sizedness::ParamDependent(param) => { - let param_idx = task_key - .params - .rust_params() - .iter() - .position(|p| p == param) - .unwrap(); - let param_ty = ty_accessor_functions[param_idx].call()(self_expr); - - vir::expr! { vcx; (is_this_type) == > ([sized_impl_fun_idn](param_ty)) } + match task_key.sizedness { + Sizedness::Sized => is_this_type, + Sizedness::Unsized => vir::expr! {vcx; false }, + Sizedness::ParamDependent(idx) => { + let destructor = vcx.mk_adt_destructor( + vir::vir_format!( + vcx, + "s_{base_name}_typaram_{}", + params.ty_decls()[idx as usize].name + ), + vir::TYPE_TYVAL, + vir::TYPE_TYVAL, + ); + let param_ty = destructor.call()(self_expr); + vir::expr! { vcx; (is_this_type) && ([sized_impl_fun_idn](param_ty)) } } + Sizedness::Unknown => todo!( + "Handle unknown sizedness for type constructor {}", + task_key.name() + ), } }; Ok(( @@ -224,38 +214,3 @@ impl TaskEncoder for TyConstructorEnc { }) } } - -#[derive(Debug, Clone)] -enum Sizedness<'tcx> { - Definite(bool), - ParamDependent(ty::GenericArg<'tcx>), -} - -fn check_sizedness<'a>(tcx: ty::TyCtxt<'a>, decomp: RustTyDecomposition<'a>) -> Sizedness<'a> { - let ctx = decomp.args.context(); - if decomp - .ty - .rust_ty - .is_some_and(|ty| ty.is_sized(tcx, ctx.typing_env())) - { - return Sizedness::Definite(true); - } - match &decomp.ty.specifics { - TySpecifics::StructLike(data) => { - // Only need to check the last field of the struct for unsizedness - if let Some(last_field) = data.fields.last() { - // The type is not definitely sized. Need to recurse on the field's type - let normalized_decomp = last_field.ty().decompose_normalize(decomp.args); - - check_sizedness(tcx, normalized_decomp) - } else { - Sizedness::Definite(true) // Empty structs are Sized - } - } - TySpecifics::Param(_) => Sizedness::ParamDependent(decomp.args.args()[0]), - TySpecifics::ArrayLike(data) if data.slice => Sizedness::Definite(false), - TySpecifics::Opaque(_) => unimplemented!("Is an opaque type sized?"), - // Builtin, Enums, Primitives, References, and fixed-size Arrays are always Sized. - _ => Sizedness::Definite(true), - } -} diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index 270b77c6822..d2b10fbf779 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -73,7 +73,7 @@ impl<'tcx> RustTyDecomposition<'tcx> { let data = RustTyData { name: symbol::Symbol::intern("Real"), params: GParams::empty(), - rust_ty: None, + sizedness: Sizedness::Sized, }; let specifics = TySpecifics::Builtin(RustBuiltinData::BuiltinReal); Self { @@ -244,7 +244,7 @@ pub type RustBuiltin<'tcx> = >::BuiltinData; pub struct RustTyData<'tcx> { pub name: symbol::Symbol, pub params: GParams<'tcx>, - pub rust_ty: Option>, + pub sizedness: Sizedness, } impl<'tcx> RustTyData<'tcx> { @@ -302,7 +302,7 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { let data = RustTyData { name: symbol::Symbol::intern(&name), params, - rust_ty: Some(ErasedTy::new(ty)), + sizedness: CanonicalTy::from_ty(ty).sizedness(), }; let specifics = TySpecifics::from_ty(ty); let maybe_inhabited = @@ -321,7 +321,7 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { let data = RustTyData { name: symbol::Symbol::intern(&name), params, - rust_ty: Some(ErasedTy::new(ty)), + sizedness: Sizedness::Sized, }; let specifics = TySpecifics::from_prim_ty(ty); RustTyDecomposition { @@ -640,68 +640,107 @@ impl<'tcx> TySpecifics<'tcx, RustTyDatas> { } } -/// A wrapper around `ty::Ty` that provides a canonical, "erased" version of a type. -#[repr(transparent)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ErasedTy<'tcx>(ty::Ty<'tcx>); - -impl<'tcx> ErasedTy<'tcx> { - fn new(ty: ty::Ty<'tcx>) -> Self { - ErasedTy(to_generic_ty(ty)) - } +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub enum Sizedness { + Sized, + Unsized, + Unknown, + ParamDependent(u32), } -impl<'tcx> Deref for ErasedTy<'tcx> { +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct CanonicalTy<'tcx>(ty::Ty<'tcx>); + +impl<'tcx> Deref for CanonicalTy<'tcx> { type Target = ty::Ty<'tcx>; fn deref(&self) -> &Self::Target { &self.0 } } -fn to_generic_ty<'tcx>(ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { - match ty.kind() { - ty::Adt(adt_def, _) => { - vir::with_vcx(|vcx| vcx.tcx().type_of(adt_def.did()).instantiate_identity()) - } +impl<'tcx> CanonicalTy<'tcx> { + fn from_ty(ty: ty::Ty<'tcx>) -> Self { + Self(canonicalize_ty(vir::with_vcx(|vcx| vcx.tcx()), ty)) + } - ty::Ref(_, inner_ty, mutbl) => { - let generic_inner = to_generic_ty(*inner_ty); - vir::with_vcx(|vcx| { - ty::Ty::new_ref( - vcx.tcx(), - vcx.tcx().lifetimes.re_erased, - generic_inner, - *mutbl, - ) - }) - } + fn sizedness(&self) -> Sizedness { + check_sizedness(vir::with_vcx(|vcx| vcx.tcx()), self.0) + } +} +fn canonicalize_ty<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { + match ty.kind() { + ty::Adt(adt_def, _) => tcx.type_of(adt_def.did()).instantiate_identity(), + + ty::Ref(_, inner_ty, mutbl) => ty::Ty::new_ref( + tcx, + tcx.lifetimes.re_erased, + canonicalize_ty(tcx, *inner_ty), + *mutbl, + ), ty::RawPtr(inner_ty, mutbl) => { - let generic_inner = to_generic_ty(*inner_ty); - vir::with_vcx(|vcx| ty::Ty::new_ptr(vcx.tcx(), generic_inner, *mutbl)) + ty::Ty::new_ptr(tcx, canonicalize_ty(tcx, *inner_ty), *mutbl) } - ty::Param(_) | ty::Alias(..) => { - vir::with_vcx(|vcx| ty::Ty::new_param(vcx.tcx(), 0, symbol::Symbol::intern("T"))) - } + ty::Param(_) | ty::Alias(..) => ty::Ty::new_param(tcx, 0, symbol::Symbol::intern("T")), - ty::Slice(inner_ty) => { - let generic_inner = to_generic_ty(*inner_ty); - vir::with_vcx(|vcx| ty::Ty::new_slice(vcx.tcx(), generic_inner)) - } + ty::Slice(inner_ty) => ty::Ty::new_slice(tcx, canonicalize_ty(tcx, *inner_ty)), - ty::Array(inner_ty, _) => { - let generic_inner = to_generic_ty(*inner_ty); - vir::with_vcx(|vcx| ty::Ty::new_array(vcx.tcx(), generic_inner, 0_u64)) - } + ty::Array(inner_ty, _) => ty::Ty::new_array(tcx, canonicalize_ty(tcx, *inner_ty), 0_u64), - ty::Tuple(tys) => vir::with_vcx(|vcx| { + ty::Tuple(tys) => { let generic_tys: Vec<_> = (0..tys.len()) - .map(|i| ty::Ty::new_param(vcx.tcx(), i as u32, symbol::Symbol::intern("T"))) + .map(|i| ty::Ty::new_param(tcx, i as u32, symbol::Symbol::intern("T"))) .collect(); - ty::Ty::new_tup(vcx.tcx(), &generic_tys) - }), + ty::Ty::new_tup(tcx, &generic_tys) + } _ => ty, } } + +fn check_sizedness<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> Sizedness { + match ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::UnsafeBinder(_) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Array(..) + | ty::Pat(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Never + | ty::Error(_) => Sizedness::Sized, + + ty::Str | ty::Slice(_) | ty::Dynamic(..) => Sizedness::Unsized, + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Foreign(..) => Sizedness::Unknown, + + ty::Alias(..) => unimplemented!("sizedness for Alias"), + + ty::Param(param) => Sizedness::ParamDependent(param.index), + + ty::Tuple(tys) => { + // Check last tuple field for sizedness if any + tys.last() + .map_or(Sizedness::Sized, |last| check_sizedness(tcx, *last)) + } + + ty::Adt(adt, _) => { + let sized_constraint = adt.sizedness_constraint(tcx, ty::SizedTraitKind::Sized); + match sized_constraint { + None => Sizedness::Sized, + Some(cons) => check_sizedness(tcx, cons.skip_binder()), + } + } + } +} From 1f57cfbad67c01d13d650389a8c597f93439497d Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Sun, 22 Feb 2026 16:41:23 +0100 Subject: [PATCH 20/70] Add unknown type check for the sized trait --- .../src/encoders/ty/lifted/ty_constructor.rs | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index 9415f12f798..d3b7a0dfb81 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -159,7 +159,7 @@ impl TaskEncoder for TyConstructorEnc { let param_ty = destructor.call()(self_expr); vir::expr! { vcx; (is_this_type) && ([sized_impl_fun_idn](param_ty)) } } - Sizedness::Unknown => todo!( + Sizedness::Unknown => unimplemented!( "Handle unknown sizedness for type constructor {}", task_key.name() ), @@ -176,13 +176,12 @@ impl TaskEncoder for TyConstructorEnc { } fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { - let (mut constructors, sized_checks): (Vec<_>, Vec<_>) = + let (mut constructors, mut sized_checks): (Vec<_>, Vec<_>) = Self::all_outputs_local_no_errors() .into_iter() .map(|out| (out.constructor, out.sized_check)) .unzip(); vir::with_vcx(|vcx| { - vcx.tcx(); let args = vcx.alloc_array(&[vcx.mk_local_decl("non_unit", vir::TYPE_INT)]); let unknown = vcx.mk_adt_constructor("Unknown_type", args); constructors.push(unknown); @@ -194,13 +193,31 @@ impl TaskEncoder for TyConstructorEnc { program.add_adt(adt); // Since we know all type constructors now, we can emit the `Sized` trait - // TODO: Correct implementation of `Sized` let sized_impl_fun_idn: FunctionIdn<'vir, vir::TyVal, vir::Bool> = FunctionIdn::new( vir::vir_format_identifier!(vcx, "Sized_impl"), vir::TYPE_TYVAL, vir::TYPE_BOOL, ); + let sized_impl_unknown_fun_idn: FunctionIdn<'vir, vir::Int, vir::Bool> = + FunctionIdn::new( + vir::vir_format_identifier!(vcx, "Sized_impl_unknown"), + vir::TYPE_INT, + vir::TYPE_BOOL, + ); + let self_decl = vcx.mk_local_decl("Self", vir::TYPE_TYVAL); + let unknown_type_check = { + let self_expr = vcx.mk_local_ex(self_decl); + let is_unknown_type = vcx.mk_adt_discriminator_expr(self_expr, "Unknown_type"); + + let unknown_id_destructor = + vcx.mk_adt_destructor("non_unit", vir::TYPE_TYVAL, vir::TYPE_INT); + let extracted_id = unknown_id_destructor.call()(self_expr); + + vir::expr! {vcx; (is_unknown_type) && ([sized_impl_unknown_fun_idn](extracted_id)) } + }; + + sized_checks.push(unknown_type_check); let sized_impl_fun = vcx.mk_function( sized_impl_fun_idn, (self_decl,), @@ -211,6 +228,16 @@ impl TaskEncoder for TyConstructorEnc { ); program.add_function(sized_impl_fun); + + let sized_impl_unknown_fun = vcx.mk_function( + sized_impl_unknown_fun_idn, + (vcx.mk_local_decl("non_unit", vir::TYPE_INT),), + &[], + &[], + None, + None, + ); + program.add_function(sized_impl_unknown_fun); }) } } From 36119b0e813154a15125953d9104b24d98387596 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Sun, 22 Feb 2026 17:37:38 +0100 Subject: [PATCH 21/70] Make sizedness check work in general case --- prusti-encoder/src/encoders/ty/rust_ty.rs | 84 +++++++++++++---------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index d2b10fbf779..1b4e21a346b 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -642,12 +642,25 @@ impl<'tcx> TySpecifics<'tcx, RustTyDatas> { #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub enum Sizedness { + /// A type is definitely `Sized` Sized, + /// A type is definitely not `Sized` Unsized, - Unknown, + /// The sizedness of the type depends on the sizedness of the n-th generic parameter the type + /// defines ParamDependent(u32), + Unknown, } +/// A canonicalized version of a type representing its structural identity. +/// +/// This erases all monomorphization information and external context. +/// +/// ### Examples: +/// * **Tuples**: `(U/#2, i32, V/#3)` becomes `(T/#0, T/#1, T/#2)` +/// * **ADTs**: `Box` and `Box>` both become `Box` +/// * **Arrays**: `[u8; 32]` and `[bool; 64]` both become `[T/#0; 0]` +/// * **Pointers**: `&'a mut i32` becomes `&erased mut T/#0` #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] struct CanonicalTy<'tcx>(ty::Ty<'tcx>); @@ -660,45 +673,43 @@ impl<'tcx> Deref for CanonicalTy<'tcx> { impl<'tcx> CanonicalTy<'tcx> { fn from_ty(ty: ty::Ty<'tcx>) -> Self { - Self(canonicalize_ty(vir::with_vcx(|vcx| vcx.tcx()), ty)) - } + let tcx = vir::with_vcx(|vcx| vcx.tcx()); + let default_param = ty::Ty::new_param(tcx, 0, symbol::Symbol::intern("T")); + Self(match ty.kind() { + ty::Adt(adt_def, _) => tcx.type_of(adt_def.did()).instantiate_identity(), - fn sizedness(&self) -> Sizedness { - check_sizedness(vir::with_vcx(|vcx| vcx.tcx()), self.0) - } -} + ty::Ref(_, _, mutbl) => { + ty::Ty::new_ref(tcx, tcx.lifetimes.re_erased, default_param, *mutbl) + } + ty::RawPtr(_, mutbl) => ty::Ty::new_ptr(tcx, default_param, *mutbl), -fn canonicalize_ty<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> { - match ty.kind() { - ty::Adt(adt_def, _) => tcx.type_of(adt_def.did()).instantiate_identity(), - - ty::Ref(_, inner_ty, mutbl) => ty::Ty::new_ref( - tcx, - tcx.lifetimes.re_erased, - canonicalize_ty(tcx, *inner_ty), - *mutbl, - ), - ty::RawPtr(inner_ty, mutbl) => { - ty::Ty::new_ptr(tcx, canonicalize_ty(tcx, *inner_ty), *mutbl) - } + ty::Param(_) | ty::Alias(..) => default_param, - ty::Param(_) | ty::Alias(..) => ty::Ty::new_param(tcx, 0, symbol::Symbol::intern("T")), + ty::Slice(_) => ty::Ty::new_slice(tcx, default_param), - ty::Slice(inner_ty) => ty::Ty::new_slice(tcx, canonicalize_ty(tcx, *inner_ty)), + ty::Array(_, _) => ty::Ty::new_array(tcx, default_param, 0_u64), - ty::Array(inner_ty, _) => ty::Ty::new_array(tcx, canonicalize_ty(tcx, *inner_ty), 0_u64), + ty::Tuple(tys) => { + let generic_tys: Vec<_> = (0..tys.len()) + .map(|i| ty::Ty::new_param(tcx, i as u32, symbol::Symbol::intern("T"))) + .collect(); + ty::Ty::new_tup(tcx, &generic_tys) + } - ty::Tuple(tys) => { - let generic_tys: Vec<_> = (0..tys.len()) - .map(|i| ty::Ty::new_param(tcx, i as u32, symbol::Symbol::intern("T"))) - .collect(); - ty::Ty::new_tup(tcx, &generic_tys) - } + _ => ty, + }) + } - _ => ty, + fn sizedness(&self) -> Sizedness { + check_sizedness(vir::with_vcx(|vcx| vcx.tcx()), self.0) } } +/// Computes whether a type is Sized. +/// +/// For generic definitions, the result may indicate that sizedness depends on +/// the instantiation of a specific type parameter. For example, +/// `struct MyStruct(T)` is `Sized` only if `T` is `Sized`. fn check_sizedness<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> Sizedness { match ty.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) @@ -723,9 +734,9 @@ fn check_sizedness<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> Sizedness { ty::Str | ty::Slice(_) | ty::Dynamic(..) => Sizedness::Unsized, - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Foreign(..) => Sizedness::Unknown, - - ty::Alias(..) => unimplemented!("sizedness for Alias"), + ty::Alias(..) | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Foreign(..) => { + Sizedness::Unknown + } ty::Param(param) => Sizedness::ParamDependent(param.index), @@ -735,11 +746,14 @@ fn check_sizedness<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> Sizedness { .map_or(Sizedness::Sized, |last| check_sizedness(tcx, *last)) } - ty::Adt(adt, _) => { + ty::Adt(adt, args) => { let sized_constraint = adt.sizedness_constraint(tcx, ty::SizedTraitKind::Sized); match sized_constraint { None => Sizedness::Sized, - Some(cons) => check_sizedness(tcx, cons.skip_binder()), + Some(cons) => { + let substituted_ty = cons.instantiate(tcx, args); + check_sizedness(tcx, substituted_ty) + } } } } From f9366a3610ad2e83f2e7c6d72209f07717ef8d0b Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Sun, 22 Feb 2026 19:32:20 +0100 Subject: [PATCH 22/70] Initial trait associated type predicates --- .../src/encoders/ty/generics/traits.rs | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 30f7d29c142..972526887e7 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -24,6 +24,7 @@ pub struct TraitData<'vir> { #[derive(Debug, Clone)] pub struct TraitImplRef<'vir> { + pub type_did_fun_mapping: HashMap>, pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, } impl OutputRefAny for TraitImplRef<'_> {} @@ -107,6 +108,7 @@ impl TaskEncoder for TraitEnc { deps.emit_output_ref( *task_key, TraitImplRef { + type_did_fun_mapping: type_did_fun_mapping.clone(), impl_fun: impl_fun_idn, }, )?; @@ -166,6 +168,7 @@ impl TaskEncoder for TraitEnc { checks.push(check); } + let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); // Construct the trait bound checks for this impl block for trait_pred in impl_ctx .typing_env() @@ -217,7 +220,50 @@ impl TaskEncoder for TraitEnc { checks.push(required_trait_impl_fun(&ty_args, &const_args)); } - // TODO: Construct the associated type bound checks for this impl block + for projection_pred in caller_bounds + .iter() + .filter_map(ty::Clause::as_projection_clause) + .map(ty::Binder::skip_binder) + { + let projection_did = projection_pred.def_id(); + let required_trait_did = projection_pred.trait_def_id(tcx); + let required_trait = deps.require_ref::(required_trait_did)?; + let projection_fun = required_trait + .type_did_fun_mapping + .get(&projection_did) + .expect("Projection did should be in the mapping"); + + let proj_src_args = projection_pred + .projection_term + .args + .iter() + .filter_map(|arg| arg.as_type()); + let proj_src_arg_exprs = proj_src_args + .map(|ty| { + assemble_type( + vcx, + deps, + &ty_generics_map, + &const_generics_map, + impl_ctx, + ty, + ) + }) + .collect::>(); + let tgt_ty = projection_pred.term.expect_type(); + let tgt_ty_expr = assemble_type( + vcx, + deps, + &ty_generics_map, + &const_generics_map, + impl_ctx, + tgt_ty, + ); + let projection = projection_fun(&proj_src_arg_exprs); + + let projection_check = vir::expr! {vcx; (projection) == (tgt_ty_expr)}; + checks.push(projection_check); + } trait_impl_checks.push(vcx.mk_conj(&checks)); } From ce008f018c9bc5415ca68f6d54c2167f0960a960 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Sun, 22 Feb 2026 20:42:11 +0100 Subject: [PATCH 23/70] Const generics in trait impls --- .../src/encoders/ty/generics/params.rs | 3 +- .../src/encoders/ty/generics/trait_impls.rs | 158 ++++++++++-------- .../src/encoders/ty/generics/traits.rs | 34 ++-- vir/src/macros.rs | 8 +- 4 files changed, 117 insertions(+), 86 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/params.rs b/prusti-encoder/src/encoders/ty/generics/params.rs index a2f589520b2..e9a662c6405 100644 --- a/prusti-encoder/src/encoders/ty/generics/params.rs +++ b/prusti-encoder/src/encoders/ty/generics/params.rs @@ -277,7 +277,8 @@ impl<'vir> GenericParams<'vir> { ), }) .collect::>(); - (trait_data.type_did_fun_mapping.get(&a.def_id).unwrap())(tys) + // TODO: We also need to handle const args here + (trait_data.assoc_types.get(&a.def_id).unwrap())(tys, &[]) }), }; } diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 3e8f7b28343..b3345c6d615 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -36,8 +36,10 @@ impl TaskEncoder for TraitImplEnc { vir::with_vcx(|vcx| { let tcx = vcx.tcx(); - let all_impls = tcx.trait_impls_in_crate(task_key.krate); - let idx = all_impls.iter().position(|did| did == task_key).unwrap(); + let impl_idx = { + let all_impls = tcx.trait_impls_in_crate(task_key.krate); + all_impls.iter().position(|did| did == task_key).unwrap() + }; let ctx = GParams::from(*task_key); let params = deps.require_dep::(ctx)?; @@ -45,81 +47,101 @@ impl TaskEncoder for TraitImplEnc { let trait_ref = tcx.impl_trait_ref(task_key).unwrap().instantiate_identity(); let trait_did = trait_ref.def_id; let trait_data = deps.require_dep::(trait_did)?; + let trait_name = trait_data.trait_name; let args = deps.require_dep::(GArgs::new(ctx, trait_ref.args))?; - let mut axs = Vec::new(); - - let implementing_ty = tcx.type_of(task_key).instantiate_identity(); - let implementing_ty = RustTyDecomposition::from_ty(implementing_ty, *task_key); - let implementing_ty = implementing_ty.ty.name(); - - let trait_ty_decls = params - .ty_decls() - .iter() - .map(|dec| dec.upcast_ty()) - .collect::>(); - - tcx.associated_items(*task_key) - .in_definition_order() - .filter(|item| matches!(item.kind, AssocKind::Type { data: _ })) - .for_each(|impl_item| { - let assoc_fun = trait_data.type_did_fun_mapping.get(&impl_item.trait_item_def_id.unwrap()).unwrap(); - // construct arguments for assoc_item function - // parameters of the trait are substituted - // by the arguments used in the impl - // parameters of the associated type are kept - - // parameters of assoc item include already substituted arguments - let assoc_params = deps - .require_dep::(GParams::from(impl_item.def_id)) - .unwrap(); - - // the type we want to resolve the type alias to - let assoc_type_expr = assoc_params.ty_expr( - deps, - RustTyDecomposition::from_ty( - tcx.type_of(impl_item.def_id).instantiate_identity(), - GParams::from(impl_item.def_id), - ), - ); - let assoc_decls = assoc_params - .ty_decls() - .iter() - .map(|dec| dec.upcast_ty()) - .collect::>(); - - // Combine substituted trait ty decls with the decls of the associated type - let mut trait_ty_decls = trait_ty_decls.clone(); - trait_ty_decls.extend_from_slice(&assoc_decls[params.ty_exprs().len()..]); - - // Combine substituted trait params with the params of the associated type - let trait_tys = vcx.alloc_slice(&iter::empty().chain(args.get_ty().to_owned()).chain(assoc_params.ty_exprs()[params.ty_exprs().len()..].to_owned()).collect::>()); - axs.push(vcx.mk_domain_axiom( + let mut axioms = Vec::new(); + + let impl_ty = { + let implementing_ty = tcx.type_of(task_key).instantiate_identity(); + let implementing_ty = RustTyDecomposition::from_ty(implementing_ty, *task_key); + implementing_ty.ty.name() + }; + + let trait_ty_decls = params.ty_decls(); + + let trait_const_decls = params.const_decls(); + + for impl_item in tcx.associated_items(*task_key).in_definition_order() { + let trait_item_def_id = impl_item.trait_item_def_id.unwrap(); + let impl_item_def_id = impl_item.def_id; + let item_name = tcx.item_name(impl_item_def_id); + + // construct arguments for assoc_item function + // parameters of the trait are substituted + // by the arguments used in the impl + // parameters of the associated type are kept + + // parameters of assoc item include already substituted arguments + let impl_item_params = GParams::from(impl_item_def_id); + let assoc_params = deps + .require_dep::(impl_item_params) + .unwrap(); + + let assoc_ty_decls = assoc_params.ty_decls(); + let assoc_const_decls = assoc_params.const_decls(); + + // Combine substituted trait ty decls with the decls of the associated type + let trait_ty_decls = + [&trait_ty_decls, &assoc_ty_decls[params.ty_exprs().len()..]].concat(); + let trait_const_decls = [ + &trait_const_decls, + &assoc_const_decls[params.const_exprs().len()..], + ] + .concat(); + + // Combine substituted trait params with the params of the associated type + let trait_tys = &[ + args.get_ty(), + &assoc_params.ty_exprs()[params.ty_exprs().len()..], + ] + .concat(); + let trait_consts = &[ + args.get_const(), + &assoc_params.const_exprs()[params.const_exprs().len()..], + ] + .concat(); + + match impl_item.kind { + AssocKind::Type { .. } => { + let assoc_type = trait_data.assoc_types.get(&trait_item_def_id).unwrap(); + + // the type we want to resolve the type alias to + let assoc_type_expr = assoc_params.ty_expr( + deps, + RustTyDecomposition::from_ty( + tcx.type_of(impl_item_def_id).instantiate_identity(), + impl_item_params, + ), + ); + axioms.push(vcx.mk_domain_axiom( + vir_format_identifier!(vcx, "{trait_name}_impl_{impl_ty}_{impl_idx}_assoc_type_{item_name}"), + vir::expr! {forall ..[trait_ty_decls], ..[trait_const_decls] :: {[assoc_type(trait_tys, trait_consts)]} ([assoc_type(trait_tys, trait_consts)]) == (assoc_type_expr)}, + )); + } + _ => { + // unimplemented + } + } + } + + Ok( + ( + vcx.mk_domain( vir_format_identifier!( vcx, - "{}_Assoc_{idx}_{}_{implementing_ty}", + "t_{impl_idx}_{}_{impl_ty}", trait_data.trait_name, - tcx.item_name(impl_item.def_id), ), - vir::expr! {forall ..[trait_ty_decls] :: {[assoc_fun(trait_tys)]} ([assoc_fun(trait_tys)]) == (assoc_type_expr)} - )); - }); - - Ok(( - vcx.mk_domain( - vir_format_identifier!( - vcx, - "t_{idx}_{}_{implementing_ty}", - trait_data.trait_name, + &[], + vcx.alloc_slice(&axioms), + &[], + None, ), - &[], - vcx.alloc_slice(&axs), - &[], - None, + (), ), - (), - )) + ) }) } } diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 972526887e7..2b77f56fbf5 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -15,16 +15,20 @@ use crate::encoders::{ pub struct TraitEnc; +type TraitArgs = (vir::ManyTyVal, vir::ManyCSnap); + #[derive(Debug, Clone)] pub struct TraitData<'vir> { pub trait_name: &'vir str, - pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, - pub type_did_fun_mapping: FxHashMap>, + pub assoc_types: FxHashMap>, + pub assoc_consts: FxHashMap>, + pub impl_fun: FunctionIdn<'vir, TraitArgs, vir::Bool>, } #[derive(Debug, Clone)] pub struct TraitImplRef<'vir> { - pub type_did_fun_mapping: HashMap>, + pub assoc_types: FxHashMap>, + pub assoc_consts: FxHashMap>, pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, } impl OutputRefAny for TraitImplRef<'_> {} @@ -73,20 +77,17 @@ impl TaskEncoder for TraitEnc { let type_did_fun_mapping = tcx .associated_items(task_key) .in_definition_order() - .filter(|item| matches!(item.kind, ty::AssocKind::Type { data: _ })) - .map(|item| { - let params_type = deps - .require_dep::(GParams::from(item.def_id)) - .unwrap(); + .filter(|item| matches!(item.kind, ty::AssocKind::Type { .. })) + .map(|assoc_ty| { ( - item.def_id, + assoc_ty.def_id, FunctionIdn::new( vir_format_identifier!( vcx, "{trait_name}_Assoc_{}_func", - tcx.item_name(item.def_id), + tcx.item_name(assoc_ty.def_id), ), - vcx.alloc_slice(&vec![vir::TYPE_TYVAL; params_type.ty_exprs().len()]), // params_type also includes parameters of trait itself + (enc_params.ty_args(), enc_params.const_args()), vir::TYPE_TYVAL, ), ) @@ -108,7 +109,8 @@ impl TaskEncoder for TraitEnc { deps.emit_output_ref( *task_key, TraitImplRef { - type_did_fun_mapping: type_did_fun_mapping.clone(), + assoc_types: type_did_fun_mapping.clone(), + assoc_consts: Default::default(), // No associated consts supported impl_fun: impl_fun_idn, }, )?; @@ -229,7 +231,7 @@ impl TaskEncoder for TraitEnc { let required_trait_did = projection_pred.trait_def_id(tcx); let required_trait = deps.require_ref::(required_trait_did)?; let projection_fun = required_trait - .type_did_fun_mapping + .assoc_types .get(&projection_did) .expect("Projection did should be in the mapping"); @@ -259,7 +261,8 @@ impl TaskEncoder for TraitEnc { impl_ctx, tgt_ty, ); - let projection = projection_fun(&proj_src_arg_exprs); + // TODO: Include const generics + let projection = projection_fun(&proj_src_arg_exprs, &[]); let projection_check = vir::expr! {vcx; (projection) == (tgt_ty_expr)}; checks.push(projection_check); @@ -316,7 +319,8 @@ impl TaskEncoder for TraitEnc { }, TraitData { trait_name, - type_did_fun_mapping, + assoc_types: type_did_fun_mapping, + assoc_consts: Default::default(), // No associated consts supported impl_fun: impl_fun_idn, }, )) diff --git a/vir/src/macros.rs b/vir/src/macros.rs index 14289897539..95c926a4308 100644 --- a/vir/src/macros.rs +++ b/vir/src/macros.rs @@ -410,8 +410,12 @@ macro_rules! expr_inner { ) }; (@forall_qvars($qvars:ident); :: $($tokens:tt)*) => { compile_error!(concat!("VIR missing triggers or body: `" , stringify!($($tokens)*), "`")) }; - (@forall_qvars($qvars:ident); , ..[$outer_decls:ident] $($tokens:tt)*) => { { - $qvars.extend($outer_decls.clone()); + (@forall_qvars($qvars:ident); , ..[$outer_decls:expr] $($tokens:tt)*) => { { + $qvars.extend($outer_decls.iter().map(|local| $crate::CastType::as_dyn(local.clone()))); + $crate::expr_inner!(@forall_qvars($qvars); $($tokens)*) + } }; + (@forall_qvars($qvars:ident); , [$outer_decl:expr] $($tokens:tt)*) => { { + $qvars.push($crate::CastType::as_dyn($outer_decl)); $crate::expr_inner!(@forall_qvars($qvars); $($tokens)*) } }; (@forall_qvars($qvars:ident); , $qvar:ident : $qtype:tt $($tokens:tt)* ) => { { From 85ac4ec9558cbfc12efe6e4613d675158147a4e0 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Sun, 22 Feb 2026 20:56:33 +0100 Subject: [PATCH 24/70] Remove full dep output for trait encoder --- .../src/encoders/ty/generics/params.rs | 2 +- .../src/encoders/ty/generics/trait_impls.rs | 2 +- .../src/encoders/ty/generics/traits.rs | 23 ++++++++----------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/params.rs b/prusti-encoder/src/encoders/ty/generics/params.rs index e9a662c6405..a1c8b4facfd 100644 --- a/prusti-encoder/src/encoders/ty/generics/params.rs +++ b/prusti-encoder/src/encoders/ty/generics/params.rs @@ -265,7 +265,7 @@ impl<'vir> GenericParams<'vir> { GParamVariant::Alias(a) => vir::with_vcx(|vcx| { let tcx = vcx.tcx(); let trait_did = tcx.associated_item(a.def_id).container_id(tcx); - let trait_data = deps.require_dep::(trait_did).unwrap(); + let trait_data = deps.require_ref::(trait_did).unwrap(); let tys = &a .args .iter() diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index b3345c6d615..f5c6e6f909d 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -46,7 +46,7 @@ impl TaskEncoder for TraitImplEnc { let trait_ref = tcx.impl_trait_ref(task_key).unwrap().instantiate_identity(); let trait_did = trait_ref.def_id; - let trait_data = deps.require_dep::(trait_did)?; + let trait_data = deps.require_ref::(trait_did)?; let trait_name = trait_data.trait_name; let args = deps.require_dep::(GArgs::new(ctx, trait_ref.args))?; diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 2b77f56fbf5..6587f6ec77a 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -18,7 +18,7 @@ pub struct TraitEnc; type TraitArgs = (vir::ManyTyVal, vir::ManyCSnap); #[derive(Debug, Clone)] -pub struct TraitData<'vir> { +pub struct TraitEncOutputRef<'vir> { pub trait_name: &'vir str, pub assoc_types: FxHashMap>, pub assoc_consts: FxHashMap>, @@ -31,7 +31,7 @@ pub struct TraitImplRef<'vir> { pub assoc_consts: FxHashMap>, pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, } -impl OutputRefAny for TraitImplRef<'_> {} +impl OutputRefAny for TraitEncOutputRef<'_> {} #[derive(Debug, Clone)] pub struct TraitEncOutput<'vir> { @@ -49,8 +49,7 @@ impl TaskEncoder for TraitEnc { type TaskDescription<'vir> = DefId; - type OutputFullDependency<'vir> = TraitData<'vir>; - type OutputRef<'vir> = TraitImplRef<'vir>; + type OutputRef<'vir> = TraitEncOutputRef<'vir>; type OutputFullLocal<'vir> = TraitEncOutput<'vir>; fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { @@ -74,7 +73,7 @@ impl TaskEncoder for TraitEnc { let params = GParams::from(*task_key); let enc_params = deps.require_dep::(params)?; let trait_name = vcx.alloc_str(tcx.item_name(task_key).as_str()); - let type_did_fun_mapping = tcx + let assoc_types = tcx .associated_items(task_key) .in_definition_order() .filter(|item| matches!(item.kind, ty::AssocKind::Type { .. })) @@ -93,7 +92,7 @@ impl TaskEncoder for TraitEnc { ) }) .collect::>(); - let funcs = type_did_fun_mapping + let funcs = assoc_types .values() .map(|function_idn| vcx.mk_domain_function(*function_idn, false, None)) .collect::>(); @@ -108,8 +107,9 @@ impl TaskEncoder for TraitEnc { // without causing dependency cycles. deps.emit_output_ref( *task_key, - TraitImplRef { - assoc_types: type_did_fun_mapping.clone(), + TraitEncOutputRef { + trait_name, + assoc_types, assoc_consts: Default::default(), // No associated consts supported impl_fun: impl_fun_idn, }, @@ -317,12 +317,7 @@ impl TaskEncoder for TraitEnc { impl_fun, impl_fun_unknown, }, - TraitData { - trait_name, - assoc_types: type_did_fun_mapping, - assoc_consts: Default::default(), // No associated consts supported - impl_fun: impl_fun_idn, - }, + (), )) }) } From 94802de85fbedb2cef65443fcd56b3003bd4c29e Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Mon, 23 Feb 2026 15:26:47 +0100 Subject: [PATCH 25/70] Make assoc const Snap + more assoc type bound cases --- prusti-encoder/src/encoders/const.rs | 23 +- .../src/encoders/ty/generics/trait_impls.rs | 6 +- .../src/encoders/ty/generics/traits.rs | 226 +++++++++++++----- 3 files changed, 189 insertions(+), 66 deletions(-) diff --git a/prusti-encoder/src/encoders/const.rs b/prusti-encoder/src/encoders/const.rs index ac26dd2999c..2d88b510048 100644 --- a/prusti-encoder/src/encoders/const.rs +++ b/prusti-encoder/src/encoders/const.rs @@ -16,7 +16,7 @@ use crate::encoders::{ MirPureEnc, MirPureEncTask, PureKind, ty::{ RustTyDecomposition, - generics::{GParams, GenericParamsEnc}, + generics::{GParams, GenericParamsEnc, traits::TraitEnc}, use_pure::TyUsePureEnc, }, }; @@ -52,7 +52,7 @@ impl ConstEnc { ty: ty::Ty<'vir>, context: GParams<'vir>, ) -> Result, EncodeFullError<'vir, Self>> { - match const_.kind() { + match dbg!(const_.kind()) { ty::ConstKind::Param(param) => { let params = deps.require_dep::(context)?; Ok(params.const_expr(param)) @@ -61,6 +61,25 @@ impl ConstEnc { let val = vir::with_vcx(|vcx| vcx.tcx().valtree_to_const_val(val)); Self::encode_const_val(deps, val, ty, context, None) } + ty::ConstKind::Unevaluated(uneval) => { + // NOTE: For now we assume this comes from a const associated item of a trait + vir::with_vcx(|vcx| { + let tcx = vcx.tcx(); + let const_did = uneval.def; + let impl_did = tcx.parent(const_did); + let trait_ref = tcx + .impl_trait_ref(impl_did) + .expect("this to be an impl block for a trait") + .skip_binder(); + + let trait_ = deps.require_ref::(trait_ref.def_id).unwrap(); + dbg!(trait_.assoc_consts.get(&const_did)); + + // let trait_ref = vcx.tcx().impl_trait_ref(const_did).unwrap(); + // trait_ref.def_id + }); + todo!() + } k => todo!("const kind {k:?}"), } } diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index f5c6e6f909d..1ac717c829a 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -1,8 +1,6 @@ -use std::iter; - use prusti_rustc_interface::{middle::ty::AssocKind, span::def_id::DefId}; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; -use vir::{CastType, Domain, vir_format_identifier}; +use vir::{Domain, vir_format_identifier}; use crate::encoders::ty::{ RustTyDecomposition, @@ -49,7 +47,7 @@ impl TaskEncoder for TraitImplEnc { let trait_data = deps.require_ref::(trait_did)?; let trait_name = trait_data.trait_name; - let args = deps.require_dep::(GArgs::new(ctx, trait_ref.args))?; + let args = deps.require_dep::(GArgs::new(trait_did, trait_ref.args))?; let mut axioms = Vec::new(); diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 6587f6ec77a..50a46b03055 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -1,15 +1,16 @@ use prusti_rustc_interface::{middle::ty, span::def_id::DefId}; use rustc_hash::FxHashMap; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder, TaskEncoderDependencies}; -use vir::{CallableIdn, FunctionIdn, vir_format_identifier}; +use vir::{CallableIdn, CastType, FunctionIdn, vir_format_identifier}; use crate::encoders::{ ConstEnc, r#const::ConstEncTask, ty::{ RustTyDecomposition, - generics::{GParams, GenericParamsEnc}, + generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc}, lifted::TyConstructorEnc, + pure::TyPureEnc, }, }; @@ -21,14 +22,14 @@ type TraitArgs = (vir::ManyTyVal, vir::ManyCSnap); pub struct TraitEncOutputRef<'vir> { pub trait_name: &'vir str, pub assoc_types: FxHashMap>, - pub assoc_consts: FxHashMap>, + pub assoc_consts: FxHashMap>, pub impl_fun: FunctionIdn<'vir, TraitArgs, vir::Bool>, } #[derive(Debug, Clone)] pub struct TraitImplRef<'vir> { pub assoc_types: FxHashMap>, - pub assoc_consts: FxHashMap>, + pub assoc_consts: FxHashMap>, pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, } impl OutputRefAny for TraitEncOutputRef<'_> {} @@ -73,29 +74,51 @@ impl TaskEncoder for TraitEnc { let params = GParams::from(*task_key); let enc_params = deps.require_dep::(params)?; let trait_name = vcx.alloc_str(tcx.item_name(task_key).as_str()); - let assoc_types = tcx - .associated_items(task_key) + let trait_items = tcx.associated_items(task_key); + let assoc_types: FxHashMap<_, _> = trait_items .in_definition_order() .filter(|item| matches!(item.kind, ty::AssocKind::Type { .. })) - .map(|assoc_ty| { + .map(|ty| { ( - assoc_ty.def_id, + ty.def_id, + FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_Assoc_{}_func", ty.name(),), + (enc_params.ty_args(), enc_params.const_args()), + vir::TYPE_TYVAL, + ), + ) + }) + .collect(); + let assoc_consts: FxHashMap<_, _> = trait_items + .in_definition_order() + .filter(|item| matches!(item.kind, ty::AssocKind::Const { .. })) + .map(|const_| { + let rust_ty = tcx.type_of(const_.def_id).skip_binder(); + let decomp = RustTyDecomposition::from_ty(rust_ty, params); + let ret_ty = (deps.require_ref::(decomp.ty).unwrap().domain)(); + ( + const_.def_id, FunctionIdn::new( vir_format_identifier!( vcx, "{trait_name}_Assoc_{}_func", - tcx.item_name(assoc_ty.def_id), + const_.name() ), (enc_params.ty_args(), enc_params.const_args()), - vir::TYPE_TYVAL, + ret_ty, ), ) }) - .collect::>(); - let funcs = assoc_types + .collect(); + let funcs: Vec<_> = assoc_types .values() - .map(|function_idn| vcx.mk_domain_function(*function_idn, false, None)) - .collect::>(); + .map(|fun| vcx.mk_domain_function(*fun, false, None)) + .chain( + assoc_consts + .values() + .map(|fun| vcx.mk_domain_function(*fun, false, None)), + ) + .collect(); let impl_fun_idn = FunctionIdn::new( vir_format_identifier!(vcx, "{trait_name}_impl"), @@ -110,7 +133,7 @@ impl TaskEncoder for TraitEnc { TraitEncOutputRef { trait_name, assoc_types, - assoc_consts: Default::default(), // No associated consts supported + assoc_consts, impl_fun: impl_fun_idn, }, )?; @@ -188,7 +211,7 @@ impl TaskEncoder for TraitEnc { .filter_map(|arg| arg.as_type()) .map(|arg| { assemble_type( - vcx, + tcx, deps, &ty_generics_map, &const_generics_map, @@ -205,13 +228,17 @@ impl TaskEncoder for TraitEnc { ty::ConstKind::Param(..) => const_generics_map .get(&const_.into()) .copied() - .expect("The const generic should have been bound in the map"), + .expect("The const generic should have been bound in the map") + .downcast_ty(), + // TODO: we can figure out the type of the const value from the + // context, also make `ConstEnc` to handle unevaluated consts too ty::ConstKind::Value(v) => { let task = ConstEncTask::Ty { const_, ty: v.ty, context: impl_ctx, }; + deps.require_dep::(task).unwrap() } _ => unimplemented!( @@ -230,20 +257,16 @@ impl TaskEncoder for TraitEnc { let projection_did = projection_pred.def_id(); let required_trait_did = projection_pred.trait_def_id(tcx); let required_trait = deps.require_ref::(required_trait_did)?; - let projection_fun = required_trait - .assoc_types - .get(&projection_did) - .expect("Projection did should be in the mapping"); let proj_src_args = projection_pred .projection_term .args .iter() .filter_map(|arg| arg.as_type()); - let proj_src_arg_exprs = proj_src_args + let proj_src_ty_args = proj_src_args .map(|ty| { assemble_type( - vcx, + tcx, deps, &ty_generics_map, &const_generics_map, @@ -252,20 +275,78 @@ impl TaskEncoder for TraitEnc { ) }) .collect::>(); - let tgt_ty = projection_pred.term.expect_type(); - let tgt_ty_expr = assemble_type( - vcx, - deps, - &ty_generics_map, - &const_generics_map, - impl_ctx, - tgt_ty, - ); - // TODO: Include const generics - let projection = projection_fun(&proj_src_arg_exprs, &[]); + let proj_src_const_args = projection_pred + .projection_term + .args + .iter() + .filter_map(|arg| arg.as_const()) + .map(|const_| match const_.kind() { + ty::ConstKind::Param(..) => const_generics_map + .get(&const_.into()) + .copied() + .expect("The const generic should have been bound in the map") + .downcast_ty(), + ty::ConstKind::Value(v) => { + let task = ConstEncTask::Ty { + const_, + ty: v.ty, + context: impl_ctx, + }; + deps.require_dep::(task).unwrap() + } + _ => unimplemented!( + "other kinds of const parameters not supported yet" + ), + }) + .collect::>(); + + match projection_pred.term.kind() { + ty::TermKind::Ty(tgt_ty) => { + let projection_fun = required_trait + .assoc_types + .get(&projection_did) + .expect("Projection did should be in the mapping"); - let projection_check = vir::expr! {vcx; (projection) == (tgt_ty_expr)}; - checks.push(projection_check); + let tgt_ty_expr = assemble_type( + tcx, + deps, + &ty_generics_map, + &const_generics_map, + impl_ctx, + tgt_ty, + ); + let projection = + projection_fun(&proj_src_ty_args, &proj_src_const_args); + checks.push(vir::expr! {vcx; (projection) == (tgt_ty_expr)}); + } + ty::TermKind::Const(const_) => { + let projection_fun = required_trait + .assoc_consts + .get(&projection_did) + .expect("Projection did should be in the mapping"); + let tgt_const_expr = match const_.kind() { + ty::ConstKind::Param(..) => { + const_generics_map.get(&const_.into()).copied().expect( + "The const generic should have been bound in the map", + ) + } + ty::ConstKind::Value(val) => { + let task = ConstEncTask::Ty { + const_: const_, + ty: val.ty, + context: impl_ctx, + }; + deps.require_dep::(task).unwrap().upcast_ty() + } + _ => unimplemented!( + "other kinds of const parameters not supported yet" + ), + }; + let projection = + projection_fun(&proj_src_ty_args, &proj_src_const_args); + checks.push(vir::expr! {vcx; (projection) == (tgt_const_expr) }); + } + }; } trait_impl_checks.push(vcx.mk_conj(&checks)); @@ -334,7 +415,7 @@ fn encode_type_check<'vir>( vcx: &'vir vir::VirCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, ty_generics_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, - const_generics_map: &mut FxHashMap, vir::ExprCSnap<'vir>>, + const_generics_map: &mut FxHashMap, vir::ExprSnap<'vir>>, ctx: GParams<'vir>, expr: vir::ExprTyVal<'vir>, ty: ty::Ty<'vir>, @@ -395,11 +476,11 @@ fn encode_type_check<'vir>( match const_generics_map.entry(const_.into()) { Entry::Occupied(occ) => { // Already seen this const parameter: ensure this const expression matches the originally found - conjuncts.push(vcx.mk_eq_expr(const_expr, *occ.get())); + conjuncts.push(vcx.mk_eq_expr(const_expr.upcast_ty(), *occ.get())); } Entry::Vacant(vac) => { // First time seeing this const parameter: map it to the current accessor path for future references - vac.insert(const_expr); + vac.insert(const_expr.upcast_ty()); } } } @@ -421,10 +502,10 @@ fn encode_type_check<'vir>( /// Assemble a VIR type using the map of generic parameters we have collected earlier. fn assemble_type<'vir>( - vcx: &vir::VirCtxt<'vir>, + tcx: ty::TyCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, ty_generics_map: &FxHashMap, vir::ExprTyVal<'vir>>, - const_generics_map: &FxHashMap, vir::ExprCSnap<'vir>>, + const_generics_map: &FxHashMap, vir::ExprSnap<'vir>>, ctx: GParams<'vir>, ty: ty::Ty<'vir>, ) -> vir::ExprTyVal<'vir> { @@ -432,9 +513,25 @@ fn assemble_type<'vir>( if decomp.ty.specifics.is_param() { let arg = decomp.args.args().first().expect("Param missing arg"); - return *ty_generics_map - .get(arg) - .expect("The generic should have been inserted, otherwise the parameter is unbound"); + return match arg.expect_ty().kind() { + ty::TyKind::Param(param) => *ty_generics_map + .get(arg) + .expect(&format!("generic {param:?} to be mapped")), + ty::TyKind::Alias(ty::AliasTyKind::Projection, alias) => { + let trait_did = tcx.parent(alias.def_id); + let trait_ = deps.require_ref::(trait_did).unwrap(); + + let assoc_ty_fun = trait_ + .assoc_types + .get(&alias.def_id) + .expect("associated type to be in the mapping"); + + let gargs = GArgs::new(trait_did, alias.args); + let gargs = deps.require_dep::(gargs).unwrap(); + assoc_ty_fun(gargs.get_ty(), gargs.get_const()) + } + _ => unimplemented!("unsupported kind of generic parameter in type position"), + }; } let ty_enc = deps.require_ref::(decomp.ty).unwrap(); @@ -446,7 +543,7 @@ fn assemble_type<'vir>( .filter_map(|arg| arg.as_type()) .map(|inner_ty| { assemble_type( - vcx, + tcx, deps, ty_generics_map, const_generics_map, @@ -459,22 +556,31 @@ fn assemble_type<'vir>( let inner_const_args = args .iter() .filter_map(|arg| arg.as_const()) - .map(|const_| match const_.kind() { - ty::ConstKind::Param(..) => const_generics_map - .get(&const_.into()) - .copied() - .expect("The const generic should have been bound in the map"), - ty::ConstKind::Value(val) => { - let task = ConstEncTask::Ty { - const_: const_, - ty: val.ty, - context: ctx, - }; - deps.require_dep::(task).unwrap() - } - _ => unimplemented!("other kinds of const parameters not supported yet"), - }) + .map(|const_| get_const_or_encode(deps, ctx, const_generics_map, const_).downcast_ty()) .collect::>(); (ty_enc.ty_constructor)(&inner_ty_args, &inner_const_args) } + +fn get_const_or_encode<'vir>( + deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, + ctx: GParams<'vir>, + const_generics_map: &FxHashMap, vir::ExprSnap<'vir>>, + const_: ty::Const<'vir>, +) -> vir::ExprSnap<'vir> { + match const_.kind() { + ty::ConstKind::Param(..) => const_generics_map + .get(&const_.into()) + .copied() + .expect("The const generic should have been bound in the map"), + ty::ConstKind::Value(val) => { + let task = ConstEncTask::Ty { + const_: const_, + ty: val.ty, + context: ctx, + }; + deps.require_dep::(task).unwrap().upcast_ty() + } + _ => unimplemented!("other kinds of const parameters not supported yet"), + } +} From 8cbb1a6b1afba40f605391a416a2966ad1f7bf7a Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Mon, 23 Feb 2026 18:33:51 +0100 Subject: [PATCH 26/70] Discover generics inside projection terms --- .../src/encoders/ty/generics/traits.rs | 271 ++++++++++-------- 1 file changed, 155 insertions(+), 116 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 50a46b03055..70fd0cdc1ff 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -1,5 +1,6 @@ use prusti_rustc_interface::{middle::ty, span::def_id::DefId}; use rustc_hash::FxHashMap; +use std::collections::VecDeque; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder, TaskEncoderDependencies}; use vir::{CallableIdn, CastType, FunctionIdn, vir_format_identifier}; @@ -194,11 +195,24 @@ impl TaskEncoder for TraitEnc { } let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); + + // Process the projection predicates first as they might introduce new bindings + // for generic parameters + let projections = caller_bounds + .iter() + .filter_map(ty::Clause::as_projection_clause) + .map(ty::Binder::skip_binder); + checks.push(process_projections( + vcx, + deps, + &mut ty_generics_map, + &mut const_generics_map, + impl_ctx, + projections, + )); + // Construct the trait bound checks for this impl block - for trait_pred in impl_ctx - .typing_env() - .param_env - .caller_bounds() + for trait_pred in caller_bounds .iter() .filter_map(ty::Clause::as_trait_clause) .map(ty::Binder::skip_binder) @@ -248,107 +262,6 @@ impl TaskEncoder for TraitEnc { .collect::>(); checks.push(required_trait_impl_fun(&ty_args, &const_args)); } - - for projection_pred in caller_bounds - .iter() - .filter_map(ty::Clause::as_projection_clause) - .map(ty::Binder::skip_binder) - { - let projection_did = projection_pred.def_id(); - let required_trait_did = projection_pred.trait_def_id(tcx); - let required_trait = deps.require_ref::(required_trait_did)?; - - let proj_src_args = projection_pred - .projection_term - .args - .iter() - .filter_map(|arg| arg.as_type()); - let proj_src_ty_args = proj_src_args - .map(|ty| { - assemble_type( - tcx, - deps, - &ty_generics_map, - &const_generics_map, - impl_ctx, - ty, - ) - }) - .collect::>(); - let proj_src_const_args = projection_pred - .projection_term - .args - .iter() - .filter_map(|arg| arg.as_const()) - .map(|const_| match const_.kind() { - ty::ConstKind::Param(..) => const_generics_map - .get(&const_.into()) - .copied() - .expect("The const generic should have been bound in the map") - .downcast_ty(), - ty::ConstKind::Value(v) => { - let task = ConstEncTask::Ty { - const_, - ty: v.ty, - context: impl_ctx, - }; - deps.require_dep::(task).unwrap() - } - _ => unimplemented!( - "other kinds of const parameters not supported yet" - ), - }) - .collect::>(); - - match projection_pred.term.kind() { - ty::TermKind::Ty(tgt_ty) => { - let projection_fun = required_trait - .assoc_types - .get(&projection_did) - .expect("Projection did should be in the mapping"); - - let tgt_ty_expr = assemble_type( - tcx, - deps, - &ty_generics_map, - &const_generics_map, - impl_ctx, - tgt_ty, - ); - let projection = - projection_fun(&proj_src_ty_args, &proj_src_const_args); - checks.push(vir::expr! {vcx; (projection) == (tgt_ty_expr)}); - } - ty::TermKind::Const(const_) => { - let projection_fun = required_trait - .assoc_consts - .get(&projection_did) - .expect("Projection did should be in the mapping"); - let tgt_const_expr = match const_.kind() { - ty::ConstKind::Param(..) => { - const_generics_map.get(&const_.into()).copied().expect( - "The const generic should have been bound in the map", - ) - } - ty::ConstKind::Value(val) => { - let task = ConstEncTask::Ty { - const_: const_, - ty: val.ty, - context: impl_ctx, - }; - deps.require_dep::(task).unwrap().upcast_ty() - } - _ => unimplemented!( - "other kinds of const parameters not supported yet" - ), - }; - let projection = - projection_fun(&proj_src_ty_args, &proj_src_const_args); - checks.push(vir::expr! {vcx; (projection) == (tgt_const_expr) }); - } - }; - } - trait_impl_checks.push(vcx.mk_conj(&checks)); } @@ -414,8 +327,8 @@ impl TaskEncoder for TraitEnc { fn encode_type_check<'vir>( vcx: &'vir vir::VirCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, - ty_generics_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, - const_generics_map: &mut FxHashMap, vir::ExprSnap<'vir>>, + ty_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, + const_map: &mut FxHashMap, vir::ExprSnap<'vir>>, ctx: GParams<'vir>, expr: vir::ExprTyVal<'vir>, ty: ty::Ty<'vir>, @@ -426,7 +339,7 @@ fn encode_type_check<'vir>( let arg = decomp.args.args().first().expect("Param missing arg"); use std::collections::hash_map::Entry; - return match ty_generics_map.entry(*arg) { + return match ty_map.entry(*arg) { Entry::Occupied(occ) => { // Already seen this T: ensure this type matches the originally found vcx.mk_eq_expr(expr, *occ.get()) @@ -454,13 +367,7 @@ fn encode_type_check<'vir>( let inner_expr = accessor.call()(expr); conjuncts.push(encode_type_check( - vcx, - deps, - ty_generics_map, - const_generics_map, - ctx, - inner_expr, - inner_ty, + vcx, deps, ty_map, const_map, ctx, inner_expr, inner_ty, )); } @@ -473,7 +380,7 @@ fn encode_type_check<'vir>( match const_.kind() { ty::ConstKind::Param(..) => { use std::collections::hash_map::Entry; - match const_generics_map.entry(const_.into()) { + match const_map.entry(const_.into()) { Entry::Occupied(occ) => { // Already seen this const parameter: ensure this const expression matches the originally found conjuncts.push(vcx.mk_eq_expr(const_expr.upcast_ty(), *occ.get())); @@ -584,3 +491,135 @@ fn get_const_or_encode<'vir>( _ => unimplemented!("other kinds of const parameters not supported yet"), } } + +/// Process the projection predicates in a topological order, such that when processing a +/// projection predicate, the projection term is already mapped to a VIR expression. This is needed +/// to handle cases like `T::Item: Trait` where we need to refer to `T::Item` when encoding the +/// trait bound check for `Trait`. +fn process_projections<'a>( + vcx: &'a vir::VirCtxt<'a>, + deps: &mut TaskEncoderDependencies<'a, TraitEnc>, + ty_map: &mut FxHashMap, vir::ExprTyVal<'a>>, + const_map: &mut FxHashMap, vir::ExprSnap<'a>>, + ctx: GParams<'a>, + projections: impl Iterator>, +) -> vir::ExprGenBool<'a, (), !> { + let mut worklist: VecDeque<_> = projections.collect(); + let mut conjuncts = Vec::new(); + while let Some(proj) = worklist.pop_front() { + if is_alias_ready(&proj.projection_term, ty_map, const_map) { + conjuncts.push(process_projection(vcx, deps, ty_map, const_map, ctx, proj)); + } else { + worklist.push_back(proj); + } + } + vcx.mk_conj(&conjuncts) +} + +fn process_projection<'a>( + vcx: &'a vir::VirCtxt<'a>, + deps: &mut TaskEncoderDependencies<'a, TraitEnc>, + ty_map: &mut FxHashMap, vir::ExprTyVal<'a>>, + const_map: &mut FxHashMap, vir::ExprSnap<'a>>, + ctx: GParams<'a>, + projection: ty::ProjectionPredicate<'a>, +) -> vir::ExprGenBool<'a, (), !> { + let tcx = vcx.tcx(); + let proj_did = projection.def_id(); + let trait_did = projection.trait_def_id(tcx); + let trait_ = deps.require_ref::(trait_did).unwrap(); + + let proj_args = projection.projection_term.args; + + let proj_ty_args: Vec<_> = proj_args + .iter() + .filter_map(|arg| arg.as_type()) + .map(|ty| assemble_type(tcx, deps, ty_map, const_map, ctx, ty)) + .collect(); + let proj_const_args: Vec<_> = proj_args + .iter() + .filter_map(|arg| arg.as_const()) + .map(|const_| match const_.kind() { + ty::ConstKind::Param(..) => const_map + .get(&const_.into()) + .copied() + .expect("The const generic should have been bound in the map") + .downcast_ty(), + ty::ConstKind::Value(v) => { + let task = ConstEncTask::Ty { + const_, + ty: v.ty, + context: ctx, + }; + deps.require_dep::(task).unwrap() + } + _ => unimplemented!("other kinds of const parameters not supported yet"), + }) + .collect(); + + match projection.term.kind() { + ty::TermKind::Ty(tgt_ty) => { + let projection_fun = trait_ + .assoc_types + .get(&proj_did) + .expect("Projection did should be in the mapping"); + + let projection = projection_fun(&proj_ty_args, &proj_const_args); + + encode_type_check(vcx, deps, ty_map, const_map, ctx, projection, tgt_ty) + } + ty::TermKind::Const(const_) => { + let projection_fun = trait_ + .assoc_consts + .get(&proj_did) + .expect("Projection did should be in the mapping"); + let term = match const_.kind() { + ty::ConstKind::Param(..) => const_map + .get(&const_.into()) + .copied() + .expect("The const generic should have been bound in the map"), + // TODO: There could be new const generics introduced here + ty::ConstKind::Value(val) => { + let task = ConstEncTask::Ty { + const_: const_, + ty: val.ty, + context: ctx, + }; + deps.require_dep::(task).unwrap().upcast_ty() + } + _ => unimplemented!("other kinds of const parameters not supported yet"), + }; + let projection = projection_fun(&proj_ty_args, &proj_const_args); + + vir::expr! {vcx; (projection) == (term) } + } + } +} + +/// Check whether all generic parameters of the given alias term have alredy been mapped +fn is_alias_ready<'vir>( + term: &ty::AliasTerm<'vir>, + ty_map: &FxHashMap, vir::ExprTyVal<'vir>>, + const_map: &FxHashMap, vir::ExprSnap<'vir>>, +) -> bool { + for arg in term.args { + for arg in arg.walk() { + match arg.kind() { + ty::GenericArgKind::Type(ty) => { + if let ty::TyKind::Param(_) = ty.kind() { + if !ty_map.contains_key(&arg) { + return false; + } + } + } + ty::GenericArgKind::Const(_) => { + if !const_map.contains_key(&arg) { + return false; + } + } + _ => {} + } + } + } + true +} From d26a9ff2c76afef02be6124c566a4a14632a772e Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 24 Feb 2026 13:55:56 +0100 Subject: [PATCH 27/70] Fix GATs --- .../src/encoders/ty/generics/params.rs | 8 ++ .../src/encoders/ty/generics/trait_impls.rs | 76 ++++++++----------- .../src/encoders/ty/generics/traits.rs | 55 ++++++++------ 3 files changed, 73 insertions(+), 66 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/params.rs b/prusti-encoder/src/encoders/ty/generics/params.rs index a1c8b4facfd..8de4e4c573c 100644 --- a/prusti-encoder/src/encoders/ty/generics/params.rs +++ b/prusti-encoder/src/encoders/ty/generics/params.rs @@ -202,6 +202,14 @@ pub struct GenericParams<'vir> { } impl<'vir> GenericParams<'vir> { + pub fn ty_count(&self) -> usize { + self.ty_decls.len() + } + + pub fn const_count(&self) -> usize { + self.const_decls.len() + } + pub fn ty_args(&self) -> &'vir [vir::TypeTyVal<'vir>] { self.ty_args } diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 1ac717c829a..8dd7fb32cbc 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -39,32 +39,33 @@ impl TaskEncoder for TraitImplEnc { all_impls.iter().position(|did| did == task_key).unwrap() }; - let ctx = GParams::from(*task_key); - let params = deps.require_dep::(ctx)?; - - let trait_ref = tcx.impl_trait_ref(task_key).unwrap().instantiate_identity(); - let trait_did = trait_ref.def_id; - let trait_data = deps.require_ref::(trait_did)?; - let trait_name = trait_data.trait_name; - - let args = deps.require_dep::(GArgs::new(trait_did, trait_ref.args))?; - - let mut axioms = Vec::new(); - let impl_ty = { let implementing_ty = tcx.type_of(task_key).instantiate_identity(); let implementing_ty = RustTyDecomposition::from_ty(implementing_ty, *task_key); implementing_ty.ty.name() }; - let trait_ty_decls = params.ty_decls(); + let trait_ref = tcx.impl_trait_ref(task_key).unwrap().instantiate_identity(); + let trait_did = trait_ref.def_id; + let trait_data = deps.require_ref::(trait_did)?; + let trait_name = trait_data.trait_name; + let ctx = GParams::from(*task_key); + let params = deps.require_dep::(ctx)?; + let trait_ty_decls = params.ty_decls(); let trait_const_decls = params.const_decls(); + let ty_cnt = params.ty_count(); + let const_cnt = params.const_count(); + let trait_args = deps.require_dep::(GArgs::new(ctx, trait_ref.args))?; + let trait_ty_args = trait_args.get_ty(); + let trait_const_args = trait_args.get_const(); + + let mut axioms = Vec::new(); for impl_item in tcx.associated_items(*task_key).in_definition_order() { - let trait_item_def_id = impl_item.trait_item_def_id.unwrap(); - let impl_item_def_id = impl_item.def_id; - let item_name = tcx.item_name(impl_item_def_id); + let trait_item_did = impl_item.trait_item_def_id.unwrap(); + let item_did = impl_item.def_id; + let item_name = tcx.item_name(item_did); // construct arguments for assoc_item function // parameters of the trait are substituted @@ -72,50 +73,37 @@ impl TaskEncoder for TraitImplEnc { // parameters of the associated type are kept // parameters of assoc item include already substituted arguments - let impl_item_params = GParams::from(impl_item_def_id); - let assoc_params = deps - .require_dep::(impl_item_params) - .unwrap(); + let item_ctx = GParams::from(item_did); + let item_params = deps.require_dep::(item_ctx).unwrap(); - let assoc_ty_decls = assoc_params.ty_decls(); - let assoc_const_decls = assoc_params.const_decls(); + let item_ty_decls = item_params.ty_decls(); + let item_const_decls = item_params.const_decls(); + let item_ty_args = item_params.ty_exprs(); + let item_const_args = item_params.const_exprs(); // Combine substituted trait ty decls with the decls of the associated type - let trait_ty_decls = - [&trait_ty_decls, &assoc_ty_decls[params.ty_exprs().len()..]].concat(); - let trait_const_decls = [ - &trait_const_decls, - &assoc_const_decls[params.const_exprs().len()..], - ] - .concat(); + let ty_decls = [&trait_ty_decls, &item_ty_decls[ty_cnt..]].concat(); + let const_decls = [&trait_const_decls, &item_const_decls[const_cnt..]].concat(); // Combine substituted trait params with the params of the associated type - let trait_tys = &[ - args.get_ty(), - &assoc_params.ty_exprs()[params.ty_exprs().len()..], - ] - .concat(); - let trait_consts = &[ - args.get_const(), - &assoc_params.const_exprs()[params.const_exprs().len()..], - ] - .concat(); + let ty_args = &[trait_ty_args, &item_ty_args[ty_cnt..]].concat(); + let const_args = &[trait_const_args, &item_const_args[const_cnt..]].concat(); match impl_item.kind { AssocKind::Type { .. } => { - let assoc_type = trait_data.assoc_types.get(&trait_item_def_id).unwrap(); + let assoc_type = trait_data.assoc_types.get(&trait_item_did).unwrap(); // the type we want to resolve the type alias to - let assoc_type_expr = assoc_params.ty_expr( + let assoc_type_expr = item_params.ty_expr( deps, RustTyDecomposition::from_ty( - tcx.type_of(impl_item_def_id).instantiate_identity(), - impl_item_params, + tcx.type_of(item_did).instantiate_identity(), + item_ctx, ), ); axioms.push(vcx.mk_domain_axiom( vir_format_identifier!(vcx, "{trait_name}_impl_{impl_ty}_{impl_idx}_assoc_type_{item_name}"), - vir::expr! {forall ..[trait_ty_decls], ..[trait_const_decls] :: {[assoc_type(trait_tys, trait_consts)]} ([assoc_type(trait_tys, trait_consts)]) == (assoc_type_expr)}, + vir::expr! {forall ..[ty_decls], ..[const_decls] :: {[assoc_type(ty_args, const_args)]} ([assoc_type(ty_args, const_args)]) == (assoc_type_expr)}, )); } _ => { diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 70fd0cdc1ff..3a79c235a25 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -72,19 +72,27 @@ impl TaskEncoder for TraitEnc { ) -> EncodeFullResult<'vir, Self> { vir::with_vcx(|vcx| { let tcx = vcx.tcx(); - let params = GParams::from(*task_key); - let enc_params = deps.require_dep::(params)?; + let params = deps.require_dep::(GParams::from(*task_key))?; let trait_name = vcx.alloc_str(tcx.item_name(task_key).as_str()); let trait_items = tcx.associated_items(task_key); let assoc_types: FxHashMap<_, _> = trait_items .in_definition_order() .filter(|item| matches!(item.kind, ty::AssocKind::Type { .. })) - .map(|ty| { + .map(|assoc_ty| { + let assoc_did = assoc_ty.def_id; + // Associated generics can have their own params (GATs) + let assoc_params = deps + .require_dep::(GParams::from(assoc_did)) + .unwrap(); ( - ty.def_id, + assoc_did, FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_Assoc_{}_func", ty.name(),), - (enc_params.ty_args(), enc_params.const_args()), + vir_format_identifier!( + vcx, + "{trait_name}_Assoc_{}_func", + assoc_ty.name(), + ), + (assoc_params.ty_args(), assoc_params.const_args()), vir::TYPE_TYVAL, ), ) @@ -93,19 +101,22 @@ impl TaskEncoder for TraitEnc { let assoc_consts: FxHashMap<_, _> = trait_items .in_definition_order() .filter(|item| matches!(item.kind, ty::AssocKind::Const { .. })) - .map(|const_| { - let rust_ty = tcx.type_of(const_.def_id).skip_binder(); - let decomp = RustTyDecomposition::from_ty(rust_ty, params); + .map(|assoc_const| { + let assoc_did = assoc_const.def_id; + // Unlike associated types, associated constants cannot have their own generic + // parameters + let rust_ty = tcx.type_of(assoc_did).skip_binder(); + let decomp = RustTyDecomposition::from_ty(rust_ty, assoc_did); let ret_ty = (deps.require_ref::(decomp.ty).unwrap().domain)(); ( - const_.def_id, + assoc_did, FunctionIdn::new( vir_format_identifier!( vcx, "{trait_name}_Assoc_{}_func", - const_.name() + assoc_const.name() ), - (enc_params.ty_args(), enc_params.const_args()), + (params.ty_args(), params.const_args()), ret_ty, ), ) @@ -123,7 +134,7 @@ impl TaskEncoder for TraitEnc { let impl_fun_idn = FunctionIdn::new( vir_format_identifier!(vcx, "{trait_name}_impl"), - (enc_params.ty_args(), enc_params.const_args()), + (params.ty_args(), params.const_args()), vir::TYPE_BOOL, ); @@ -145,18 +156,18 @@ impl TaskEncoder for TraitEnc { vir::Bool, > = { // Omit the Self type as it is known to be the "Unknown_type" - let unknown_args = &enc_params.ty_args()[1..]; + let unknown_args = ¶ms.ty_args()[1..]; FunctionIdn::new( vir_format_identifier!(vcx, "{trait_name}_impl_unknown"), - (unknown_args, enc_params.const_args(), vir::TYPE_INT), + (unknown_args, params.const_args(), vir::TYPE_INT), vir::TYPE_BOOL, ) }; let impl_fun_unknown = vcx.mk_function( impl_fun_unknown_idn, ( - &enc_params.ty_decls()[1..], - enc_params.const_decls(), + ¶ms.ty_decls()[1..], + params.const_decls(), vcx.mk_local_decl("non_unit", vir::TYPE_INT), ), &[], @@ -181,7 +192,7 @@ impl TaskEncoder for TraitEnc { let rust_impl_args = trait_ref.args.iter().filter_map(|arg| arg.as_type()); let mut checks = Vec::new(); - for (&ty_expr, rust_ty) in enc_params.ty_exprs().iter().zip(rust_impl_args) { + for (&ty_expr, rust_ty) in params.ty_exprs().iter().zip(rust_impl_args) { let check = encode_type_check( vcx, deps, @@ -267,7 +278,7 @@ impl TaskEncoder for TraitEnc { { // Case for unknown types - let self_expr = enc_params.ty_exprs()[0]; + let self_expr = params.ty_exprs()[0]; let is_unknown_type = vcx.mk_adt_discriminator_expr(self_expr, "Unknown_type"); @@ -276,8 +287,8 @@ impl TaskEncoder for TraitEnc { let extracted_id = unknown_id_destructor.call()(self_expr); let unknown_impls = impl_fun_unknown_idn( - &enc_params.ty_exprs()[1..], - enc_params.const_exprs(), + ¶ms.ty_exprs()[1..], + params.const_exprs(), extracted_id, ); @@ -291,7 +302,7 @@ impl TaskEncoder for TraitEnc { let impl_fun = vcx.mk_function( impl_fun_idn, - (enc_params.ty_decls(), enc_params.const_decls()), + (params.ty_decls(), params.const_decls()), &[], &[], None, From 07989fe2349f0735321d833c59143942950ac53e Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 24 Feb 2026 16:30:23 +0100 Subject: [PATCH 28/70] Clean up the implementation --- prusti-encoder/src/encoders/const.rs | 21 +- .../src/encoders/ty/generics/params.rs | 2 +- .../src/encoders/ty/generics/trait_impls.rs | 2 +- .../src/encoders/ty/generics/traits.rs | 396 +++++++++--------- 4 files changed, 196 insertions(+), 225 deletions(-) diff --git a/prusti-encoder/src/encoders/const.rs b/prusti-encoder/src/encoders/const.rs index 2d88b510048..985d556aceb 100644 --- a/prusti-encoder/src/encoders/const.rs +++ b/prusti-encoder/src/encoders/const.rs @@ -16,7 +16,7 @@ use crate::encoders::{ MirPureEnc, MirPureEncTask, PureKind, ty::{ RustTyDecomposition, - generics::{GParams, GenericParamsEnc, traits::TraitEnc}, + generics::{GParams, GenericParamsEnc}, use_pure::TyUsePureEnc, }, }; @@ -61,25 +61,6 @@ impl ConstEnc { let val = vir::with_vcx(|vcx| vcx.tcx().valtree_to_const_val(val)); Self::encode_const_val(deps, val, ty, context, None) } - ty::ConstKind::Unevaluated(uneval) => { - // NOTE: For now we assume this comes from a const associated item of a trait - vir::with_vcx(|vcx| { - let tcx = vcx.tcx(); - let const_did = uneval.def; - let impl_did = tcx.parent(const_did); - let trait_ref = tcx - .impl_trait_ref(impl_did) - .expect("this to be an impl block for a trait") - .skip_binder(); - - let trait_ = deps.require_ref::(trait_ref.def_id).unwrap(); - dbg!(trait_.assoc_consts.get(&const_did)); - - // let trait_ref = vcx.tcx().impl_trait_ref(const_did).unwrap(); - // trait_ref.def_id - }); - todo!() - } k => todo!("const kind {k:?}"), } } diff --git a/prusti-encoder/src/encoders/ty/generics/params.rs b/prusti-encoder/src/encoders/ty/generics/params.rs index 8de4e4c573c..49aabf5cc19 100644 --- a/prusti-encoder/src/encoders/ty/generics/params.rs +++ b/prusti-encoder/src/encoders/ty/generics/params.rs @@ -286,7 +286,7 @@ impl<'vir> GenericParams<'vir> { }) .collect::>(); // TODO: We also need to handle const args here - (trait_data.assoc_types.get(&a.def_id).unwrap())(tys, &[]) + (trait_data.funs.assoc_types.get(&a.def_id).unwrap())(tys, &[]) }), }; } diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 8dd7fb32cbc..e37d18c9077 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -91,7 +91,7 @@ impl TaskEncoder for TraitImplEnc { match impl_item.kind { AssocKind::Type { .. } => { - let assoc_type = trait_data.assoc_types.get(&trait_item_did).unwrap(); + let assoc_type = trait_data.funs.assoc_types.get(&trait_item_did).unwrap(); // the type we want to resolve the type alias to let assoc_type_expr = item_params.ty_expr( diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 3a79c235a25..e508795a4cb 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -18,20 +18,17 @@ use crate::encoders::{ pub struct TraitEnc; type TraitArgs = (vir::ManyTyVal, vir::ManyCSnap); +type AssocTypeFun<'vir> = FunctionIdn<'vir, TraitArgs, vir::TyVal>; +type AssocConstFun<'vir> = FunctionIdn<'vir, TraitArgs, vir::Snap>; +type ImplFun<'vir> = FunctionIdn<'vir, TraitArgs, vir::Bool>; +type ImplUnknownFun<'vir> = + FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap, vir::Int), vir::Bool>; #[derive(Debug, Clone)] pub struct TraitEncOutputRef<'vir> { pub trait_name: &'vir str, - pub assoc_types: FxHashMap>, - pub assoc_consts: FxHashMap>, - pub impl_fun: FunctionIdn<'vir, TraitArgs, vir::Bool>, -} - -#[derive(Debug, Clone)] -pub struct TraitImplRef<'vir> { - pub assoc_types: FxHashMap>, - pub assoc_consts: FxHashMap>, - pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, + pub funs: TraitFuns<'vir>, + pub impl_fun: ImplFun<'vir>, } impl OutputRefAny for TraitEncOutputRef<'_> {} @@ -39,7 +36,7 @@ impl OutputRefAny for TraitEncOutputRef<'_> {} pub struct TraitEncOutput<'vir> { trait_domain: vir::Domain<'vir>, impl_fun: vir::Function<'vir>, - impl_fun_unknown: vir::Function<'vir>, + impl_unknown_fun: vir::Function<'vir>, } impl TaskEncoder for TraitEnc { @@ -62,7 +59,7 @@ impl TaskEncoder for TraitEnc { } program.add_domain(trait_enc.trait_domain); program.add_function(trait_enc.impl_fun); - program.add_function(trait_enc.impl_fun_unknown); + program.add_function(trait_enc.impl_unknown_fun); } } @@ -74,63 +71,11 @@ impl TaskEncoder for TraitEnc { let tcx = vcx.tcx(); let params = deps.require_dep::(GParams::from(*task_key))?; let trait_name = vcx.alloc_str(tcx.item_name(task_key).as_str()); - let trait_items = tcx.associated_items(task_key); - let assoc_types: FxHashMap<_, _> = trait_items - .in_definition_order() - .filter(|item| matches!(item.kind, ty::AssocKind::Type { .. })) - .map(|assoc_ty| { - let assoc_did = assoc_ty.def_id; - // Associated generics can have their own params (GATs) - let assoc_params = deps - .require_dep::(GParams::from(assoc_did)) - .unwrap(); - ( - assoc_did, - FunctionIdn::new( - vir_format_identifier!( - vcx, - "{trait_name}_Assoc_{}_func", - assoc_ty.name(), - ), - (assoc_params.ty_args(), assoc_params.const_args()), - vir::TYPE_TYVAL, - ), - ) - }) - .collect(); - let assoc_consts: FxHashMap<_, _> = trait_items - .in_definition_order() - .filter(|item| matches!(item.kind, ty::AssocKind::Const { .. })) - .map(|assoc_const| { - let assoc_did = assoc_const.def_id; - // Unlike associated types, associated constants cannot have their own generic - // parameters - let rust_ty = tcx.type_of(assoc_did).skip_binder(); - let decomp = RustTyDecomposition::from_ty(rust_ty, assoc_did); - let ret_ty = (deps.require_ref::(decomp.ty).unwrap().domain)(); - ( - assoc_did, - FunctionIdn::new( - vir_format_identifier!( - vcx, - "{trait_name}_Assoc_{}_func", - assoc_const.name() - ), - (params.ty_args(), params.const_args()), - ret_ty, - ), - ) - }) - .collect(); - let funcs: Vec<_> = assoc_types - .values() - .map(|fun| vcx.mk_domain_function(*fun, false, None)) - .chain( - assoc_consts - .values() - .map(|fun| vcx.mk_domain_function(*fun, false, None)), - ) - .collect(); + + let trait_items = tcx.associated_items(task_key).in_definition_order(); + let assoc_funs = associated_items_funs(vcx, deps, trait_name, trait_items); + + let vpr_funs = assoc_funs.mk_domain_functions(vcx); let impl_fun_idn = FunctionIdn::new( vir_format_identifier!(vcx, "{trait_name}_impl"), @@ -138,141 +83,92 @@ impl TaskEncoder for TraitEnc { vir::TYPE_BOOL, ); - // Emit the impl function reference early, so that it can be used in the trait bounds - // without causing dependency cycles. + let impl_unknown_fun_idn: ImplUnknownFun = { + // Omit the `Self` type as it is known to be the "Unknown_type" + let unknown_args = ¶ms.ty_args()[1..]; + FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_unknown_impl"), + (unknown_args, params.const_args(), vir::TYPE_INT), + vir::TYPE_BOOL, + ) + }; + + // Emit the impl function reference early, so that it can be used to encode caller + // bounds without causing dependency cycles. deps.emit_output_ref( *task_key, TraitEncOutputRef { trait_name, - assoc_types, - assoc_consts, + funs: assoc_funs, impl_fun: impl_fun_idn, }, )?; - let impl_fun_unknown_idn: FunctionIdn< - 'vir, - (vir::ManyTyVal, vir::ManyCSnap, vir::Int), - vir::Bool, - > = { - // Omit the Self type as it is known to be the "Unknown_type" - let unknown_args = ¶ms.ty_args()[1..]; - FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_impl_unknown"), - (unknown_args, params.const_args(), vir::TYPE_INT), - vir::TYPE_BOOL, - ) - }; - let impl_fun_unknown = vcx.mk_function( - impl_fun_unknown_idn, - ( - ¶ms.ty_decls()[1..], - params.const_decls(), - vcx.mk_local_decl("non_unit", vir::TYPE_INT), - ), - &[], - &[], - None, - None, - ); - let impl_fun_body = { let mut trait_impl_checks = Vec::new(); for impl_did in tcx.all_impls(*task_key) { let impl_ctx = GParams::from(impl_did); - // Collect the locations of the generic parameters of the impl block from the - // `Self` type and the trait arguments. This will allos us to refer to them - // when encoding the trait bounds of the impl block. - let mut ty_generics_map = FxHashMap::default(); - let mut const_generics_map = FxHashMap::default(); + let mut checks = Vec::new(); + + // Collect the mappings from generic arguments to viper expressions that occur + // in the impl block, such that we can refer to them when encoding the trait + // bounds of the impl block. For example, for an impl like `impl Trait for + // (T, i32)`, we would map `T` to an accessor expression to the first member of + // the tuple type - `self.2_tup.0` + let mut ty_map = FxHashMap::default(); + let mut const_map = FxHashMap::default(); let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); - let rust_impl_args = trait_ref.args.iter().filter_map(|arg| arg.as_type()); + let trait_rust_tys = trait_ref.args.iter().filter_map(|arg| arg.as_type()); - let mut checks = Vec::new(); - for (&ty_expr, rust_ty) in params.ty_exprs().iter().zip(rust_impl_args) { - let check = encode_type_check( + for (&ty_expr, rust_ty) in std::iter::zip(params.ty_exprs(), trait_rust_tys) { + checks.push(encode_type_check( vcx, deps, - &mut ty_generics_map, - &mut const_generics_map, + &mut ty_map, + &mut const_map, impl_ctx, ty_expr, rust_ty, - ); - checks.push(check); + )); } let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); // Process the projection predicates first as they might introduce new bindings // for generic parameters - let projections = caller_bounds + let projection_preds = caller_bounds .iter() .filter_map(ty::Clause::as_projection_clause) .map(ty::Binder::skip_binder); - checks.push(process_projections( + + checks.push(process_projection_predicates( vcx, deps, - &mut ty_generics_map, - &mut const_generics_map, + &mut ty_map, + &mut const_map, impl_ctx, - projections, + projection_preds, )); - // Construct the trait bound checks for this impl block - for trait_pred in caller_bounds + // Process trait predicates last as they cannot introduce new bindings for + // generics + let trait_preds = caller_bounds .iter() .filter_map(ty::Clause::as_trait_clause) - .map(ty::Binder::skip_binder) - { - let required_trait_impl_fun = - deps.require_ref::(trait_pred.def_id())?.impl_fun; - let trait_args = trait_pred.trait_ref.args; - let ty_args = trait_args - .iter() - .filter_map(|arg| arg.as_type()) - .map(|arg| { - assemble_type( - tcx, - deps, - &ty_generics_map, - &const_generics_map, - impl_ctx, - arg, - ) - }) - .collect::>(); - - let const_args = trait_args - .iter() - .filter_map(|arg| arg.as_const()) - .map(|const_| match const_.kind() { - ty::ConstKind::Param(..) => const_generics_map - .get(&const_.into()) - .copied() - .expect("The const generic should have been bound in the map") - .downcast_ty(), - // TODO: we can figure out the type of the const value from the - // context, also make `ConstEnc` to handle unevaluated consts too - ty::ConstKind::Value(v) => { - let task = ConstEncTask::Ty { - const_, - ty: v.ty, - context: impl_ctx, - }; - - deps.require_dep::(task).unwrap() - } - _ => unimplemented!( - "other kinds of const parameters not supported yet" - ), - }) - .collect::>(); - checks.push(required_trait_impl_fun(&ty_args, &const_args)); - } + .map(ty::Binder::skip_binder); + + checks.push(process_trait_predicates( + vcx, + deps, + &ty_map, + &const_map, + impl_ctx, + trait_preds, + )); + trait_impl_checks.push(vcx.mk_conj(&checks)); } @@ -286,7 +182,7 @@ impl TaskEncoder for TraitEnc { vcx.mk_adt_destructor("non_unit", vir::TYPE_TYVAL, vir::TYPE_INT); let extracted_id = unknown_id_destructor.call()(self_expr); - let unknown_impls = impl_fun_unknown_idn( + let unknown_impls = impl_unknown_fun_idn( ¶ms.ty_exprs()[1..], params.const_exprs(), extracted_id, @@ -300,6 +196,19 @@ impl TaskEncoder for TraitEnc { vcx.mk_disj(&trait_impl_checks) }; + let impl_unknown_fun = vcx.mk_function( + impl_unknown_fun_idn, + ( + ¶ms.ty_decls()[1..], + params.const_decls(), + vcx.mk_local_decl("non_unit", vir::TYPE_INT), + ), + &[], + &[], + None, + None, + ); + let impl_fun = vcx.mk_function( impl_fun_idn, (params.ty_decls(), params.const_decls()), @@ -313,14 +222,14 @@ impl TaskEncoder for TraitEnc { vir_format_identifier!(vcx, "t_{trait_name}"), &[], &[], - vcx.alloc_slice(funcs.as_slice()), + vcx.alloc_slice(vpr_funs.as_slice()), None, ); Ok(( TraitEncOutput { trait_domain, impl_fun, - impl_fun_unknown, + impl_unknown_fun, }, (), )) @@ -422,8 +331,8 @@ fn encode_type_check<'vir>( fn assemble_type<'vir>( tcx: ty::TyCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, - ty_generics_map: &FxHashMap, vir::ExprTyVal<'vir>>, - const_generics_map: &FxHashMap, vir::ExprSnap<'vir>>, + ty_map: &FxHashMap, vir::ExprTyVal<'vir>>, + const_map: &FxHashMap, vir::ExprSnap<'vir>>, ctx: GParams<'vir>, ty: ty::Ty<'vir>, ) -> vir::ExprTyVal<'vir> { @@ -432,7 +341,7 @@ fn assemble_type<'vir>( if decomp.ty.specifics.is_param() { let arg = decomp.args.args().first().expect("Param missing arg"); return match arg.expect_ty().kind() { - ty::TyKind::Param(param) => *ty_generics_map + ty::TyKind::Param(param) => *ty_map .get(arg) .expect(&format!("generic {param:?} to be mapped")), ty::TyKind::Alias(ty::AliasTyKind::Projection, alias) => { @@ -440,6 +349,7 @@ fn assemble_type<'vir>( let trait_ = deps.require_ref::(trait_did).unwrap(); let assoc_ty_fun = trait_ + .funs .assoc_types .get(&alias.def_id) .expect("associated type to be in the mapping"); @@ -459,35 +369,26 @@ fn assemble_type<'vir>( let inner_ty_args = args .iter() .filter_map(|arg| arg.as_type()) - .map(|inner_ty| { - assemble_type( - tcx, - deps, - ty_generics_map, - const_generics_map, - ctx, - inner_ty, - ) - }) + .map(|inner_ty| assemble_type(tcx, deps, ty_map, const_map, ctx, inner_ty)) .collect::>(); let inner_const_args = args .iter() .filter_map(|arg| arg.as_const()) - .map(|const_| get_const_or_encode(deps, ctx, const_generics_map, const_).downcast_ty()) + .map(|const_| assemble_const(deps, ctx, const_map, const_).downcast_ty()) .collect::>(); (ty_enc.ty_constructor)(&inner_ty_args, &inner_const_args) } -fn get_const_or_encode<'vir>( +fn assemble_const<'vir>( deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, ctx: GParams<'vir>, - const_generics_map: &FxHashMap, vir::ExprSnap<'vir>>, + const_map: &FxHashMap, vir::ExprSnap<'vir>>, const_: ty::Const<'vir>, ) -> vir::ExprSnap<'vir> { match const_.kind() { - ty::ConstKind::Param(..) => const_generics_map + ty::ConstKind::Param(..) => const_map .get(&const_.into()) .copied() .expect("The const generic should have been bound in the map"), @@ -507,7 +408,7 @@ fn get_const_or_encode<'vir>( /// projection predicate, the projection term is already mapped to a VIR expression. This is needed /// to handle cases like `T::Item: Trait` where we need to refer to `T::Item` when encoding the /// trait bound check for `Trait`. -fn process_projections<'a>( +fn process_projection_predicates<'a>( vcx: &'a vir::VirCtxt<'a>, deps: &mut TaskEncoderDependencies<'a, TraitEnc>, ty_map: &mut FxHashMap, vir::ExprTyVal<'a>>, @@ -550,27 +451,13 @@ fn process_projection<'a>( let proj_const_args: Vec<_> = proj_args .iter() .filter_map(|arg| arg.as_const()) - .map(|const_| match const_.kind() { - ty::ConstKind::Param(..) => const_map - .get(&const_.into()) - .copied() - .expect("The const generic should have been bound in the map") - .downcast_ty(), - ty::ConstKind::Value(v) => { - let task = ConstEncTask::Ty { - const_, - ty: v.ty, - context: ctx, - }; - deps.require_dep::(task).unwrap() - } - _ => unimplemented!("other kinds of const parameters not supported yet"), - }) + .map(|const_| assemble_const(deps, ctx, const_map, const_).downcast_ty()) .collect(); match projection.term.kind() { ty::TermKind::Ty(tgt_ty) => { let projection_fun = trait_ + .funs .assoc_types .get(&proj_did) .expect("Projection did should be in the mapping"); @@ -581,6 +468,7 @@ fn process_projection<'a>( } ty::TermKind::Const(const_) => { let projection_fun = trait_ + .funs .assoc_consts .get(&proj_did) .expect("Projection did should be in the mapping"); @@ -634,3 +522,105 @@ fn is_alias_ready<'vir>( } true } + +fn process_trait_predicates<'a>( + vcx: &'a vir::VirCtxt<'a>, + deps: &mut TaskEncoderDependencies<'a, TraitEnc>, + ty_map: &FxHashMap, vir::ExprTyVal<'a>>, + const_map: &FxHashMap, vir::ExprSnap<'a>>, + ctx: GParams<'a>, + trait_preds: impl Iterator>, +) -> vir::ExprGenBool<'a, (), !> { + let tcx = vcx.tcx(); + let mut conjuncts = Vec::new(); + for trait_pred in trait_preds { + let required_trait_impl_fun = deps + .require_ref::(trait_pred.def_id()) + .unwrap() + .impl_fun; + + let trait_args = trait_pred.trait_ref.args; + let ty_args: Vec<_> = trait_args + .iter() + .filter_map(|arg| arg.as_type()) + .map(|arg| assemble_type(tcx, deps, &ty_map, &const_map, ctx, arg)) + .collect(); + + let const_args: Vec<_> = trait_args + .iter() + .filter_map(|arg| arg.as_const()) + .map(|const_| assemble_const(deps, ctx, &const_map, const_).downcast_ty()) + .collect(); + + conjuncts.push(required_trait_impl_fun(&ty_args, &const_args)); + } + vcx.mk_conj(&conjuncts) +} + +#[derive(Debug, Clone)] +pub struct TraitFuns<'a> { + pub assoc_types: FxHashMap>, + pub assoc_consts: FxHashMap>, +} + +impl<'a> TraitFuns<'a> { + fn mk_domain_functions(&self, vcx: &'a vir::VirCtxt<'a>) -> Vec> { + self.assoc_types + .values() + .map(|fun| vcx.mk_domain_function(*fun, false, None)) + .chain( + self.assoc_consts + .values() + .map(|fun| vcx.mk_domain_function(*fun, false, None)), + ) + .collect() + } +} + +/// Collect mappings for associated items of a trait to their corresponding VIR functions. +fn associated_items_funs<'a>( + vcx: &'a vir::VirCtxt<'a>, + deps: &mut TaskEncoderDependencies<'a, TraitEnc>, + trait_name: &str, + assoc_items: impl Iterator, +) -> TraitFuns<'a> { + let tcx = vcx.tcx(); + + let mut assoc_types = FxHashMap::default(); + let mut assoc_consts = FxHashMap::default(); + + let mk_identifier = |item_name, item_type| { + vir_format_identifier!(vcx, "{trait_name}_assoc_{item_type}_{item_name}") + }; + + for item in assoc_items { + let assoc_did = item.def_id; + let name = item.name(); + let params = deps + .require_dep::(GParams::from(assoc_did)) + .unwrap(); + let args = (params.ty_args(), params.const_args()); + + match item.kind { + ty::AssocKind::Type { .. } => { + let fun = FunctionIdn::new(mk_identifier(name, "type"), args, vir::TYPE_TYVAL); + assoc_types.insert(assoc_did, fun); + } + ty::AssocKind::Const { .. } => { + let rust_ty = tcx.type_of(assoc_did).skip_binder(); + let decomp = RustTyDecomposition::from_ty(rust_ty, assoc_did); + let ret_ty = (deps.require_ref::(decomp.ty).unwrap().domain)(); + + let fun = FunctionIdn::new(mk_identifier(name, "const"), args, ret_ty); + assoc_consts.insert(assoc_did, fun); + } + ty::AssocKind::Fn { .. } => { + // unimplemented + } + } + } + TraitFuns { + assoc_types, + assoc_consts, + } +} From fa04be0b32db553ee6c1a93e093fdff8ceaf6db3 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 24 Feb 2026 17:10:51 +0100 Subject: [PATCH 29/70] Collect const generics from projections --- .../src/encoders/ty/generics/traits.rs | 122 ++++++++++-------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index e508795a4cb..13373114fd0 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -124,7 +124,7 @@ impl TaskEncoder for TraitEnc { let trait_rust_tys = trait_ref.args.iter().filter_map(|arg| arg.as_type()); for (&ty_expr, rust_ty) in std::iter::zip(params.ty_exprs(), trait_rust_tys) { - checks.push(encode_type_check( + checks.extend(encode_type_check( vcx, deps, &mut ty_map, @@ -144,7 +144,7 @@ impl TaskEncoder for TraitEnc { .filter_map(ty::Clause::as_projection_clause) .map(ty::Binder::skip_binder); - checks.push(process_projection_predicates( + checks.extend(process_projection_predicates( vcx, deps, &mut ty_map, @@ -169,7 +169,9 @@ impl TaskEncoder for TraitEnc { trait_preds, )); - trait_impl_checks.push(vcx.mk_conj(&checks)); + if !checks.is_empty() { + trait_impl_checks.push(vcx.mk_conj(&checks)); + } } { @@ -252,7 +254,7 @@ fn encode_type_check<'vir>( ctx: GParams<'vir>, expr: vir::ExprTyVal<'vir>, ty: ty::Ty<'vir>, -) -> vir::ExprGenBool<'vir, (), !> { +) -> Option> { let decomp = RustTyDecomposition::from_ty(ty, ctx); if decomp.ty.specifics.is_param() { @@ -262,12 +264,12 @@ fn encode_type_check<'vir>( return match ty_map.entry(*arg) { Entry::Occupied(occ) => { // Already seen this T: ensure this type matches the originally found - vcx.mk_eq_expr(expr, *occ.get()) + Some(vcx.mk_eq_expr(expr, *occ.get())) } Entry::Vacant(vac) => { // First time seeing T: map it to the current accessor path for future references vac.insert(expr); - vir::expr! { vcx; true } + None } }; } @@ -285,9 +287,9 @@ fn encode_type_check<'vir>( for (i, inner_ty) in inner_types.enumerate() { let accessor = ty_enc.ty_param_accessors[i]; - let inner_expr = accessor.call()(expr); - conjuncts.push(encode_type_check( - vcx, deps, ty_map, const_map, ctx, inner_expr, inner_ty, + let expr = accessor.call()(expr); + conjuncts.extend(encode_type_check( + vcx, deps, ty_map, const_map, ctx, expr, inner_ty, )); } @@ -295,36 +297,56 @@ fn encode_type_check<'vir>( let consts = args.iter().filter_map(|arg| arg.as_const()); for (i, const_) in consts.enumerate() { let accessor = ty_enc.const_param_accessors[i]; - let const_expr = accessor.call()(expr); - - match const_.kind() { - ty::ConstKind::Param(..) => { - use std::collections::hash_map::Entry; - match const_map.entry(const_.into()) { - Entry::Occupied(occ) => { - // Already seen this const parameter: ensure this const expression matches the originally found - conjuncts.push(vcx.mk_eq_expr(const_expr.upcast_ty(), *occ.get())); - } - Entry::Vacant(vac) => { - // First time seeing this const parameter: map it to the current accessor path for future references - vac.insert(const_expr.upcast_ty()); - } + let expr = accessor.call()(expr); + + conjuncts.extend(encode_const_check( + vcx, + deps, + const_map, + ctx, + expr.upcast_ty(), + const_, + )); + } + + Some(vcx.mk_conj(&conjuncts)) +} + +/// Encode a check that the expression `expr` is the same as the rust const `const_`. +fn encode_const_check<'a>( + vcx: &'a vir::VirCtxt<'a>, + deps: &mut TaskEncoderDependencies<'a, TraitEnc>, + const_map: &mut HashMap, vir::ExprSnap<'a>>, + ctx: GParams<'a>, + expr: vir::ExprSnap<'a>, + const_: ty::Const<'a>, +) -> Option> { + match const_.kind() { + ty::ConstKind::Param(..) => { + use std::collections::hash_map::Entry; + match const_map.entry(const_.into()) { + Entry::Occupied(occ) => { + // Already seen this const parameter: ensure this const expression matches the originally found + Some(vcx.mk_eq_expr(expr, *occ.get())) + } + Entry::Vacant(vac) => { + // First time seeing this const parameter: map it to the current accessor path for future references + vac.insert(expr); + None } } - ty::ConstKind::Value(val) => { - let task = ConstEncTask::Ty { - const_, - ty: val.ty, - context: ctx, - }; - let const_value = deps.require_dep::(task).unwrap(); - conjuncts.push(vcx.mk_eq_expr(const_expr, const_value)); - } - _ => unimplemented!("other kinds of const parameters not supported yet"), } + ty::ConstKind::Value(val) => { + let task = ConstEncTask::Ty { + const_, + ty: val.ty, + context: ctx, + }; + let value = deps.require_dep::(task).unwrap(); + Some(vcx.mk_eq_expr(expr, value.upcast_ty())) + } + _ => unimplemented!("other kinds of const parameters not supported yet"), } - - vcx.mk_conj(&conjuncts) } /// Assemble a VIR type using the map of generic parameters we have collected earlier. @@ -415,17 +437,21 @@ fn process_projection_predicates<'a>( const_map: &mut FxHashMap, vir::ExprSnap<'a>>, ctx: GParams<'a>, projections: impl Iterator>, -) -> vir::ExprGenBool<'a, (), !> { +) -> Option> { let mut worklist: VecDeque<_> = projections.collect(); let mut conjuncts = Vec::new(); while let Some(proj) = worklist.pop_front() { if is_alias_ready(&proj.projection_term, ty_map, const_map) { - conjuncts.push(process_projection(vcx, deps, ty_map, const_map, ctx, proj)); + conjuncts.extend(process_projection(vcx, deps, ty_map, const_map, ctx, proj)); } else { worklist.push_back(proj); } } - vcx.mk_conj(&conjuncts) + if conjuncts.is_empty() { + None + } else { + Some(vcx.mk_conj(&conjuncts)) + } } fn process_projection<'a>( @@ -435,7 +461,7 @@ fn process_projection<'a>( const_map: &mut FxHashMap, vir::ExprSnap<'a>>, ctx: GParams<'a>, projection: ty::ProjectionPredicate<'a>, -) -> vir::ExprGenBool<'a, (), !> { +) -> Option> { let tcx = vcx.tcx(); let proj_did = projection.def_id(); let trait_did = projection.trait_def_id(tcx); @@ -472,25 +498,9 @@ fn process_projection<'a>( .assoc_consts .get(&proj_did) .expect("Projection did should be in the mapping"); - let term = match const_.kind() { - ty::ConstKind::Param(..) => const_map - .get(&const_.into()) - .copied() - .expect("The const generic should have been bound in the map"), - // TODO: There could be new const generics introduced here - ty::ConstKind::Value(val) => { - let task = ConstEncTask::Ty { - const_: const_, - ty: val.ty, - context: ctx, - }; - deps.require_dep::(task).unwrap().upcast_ty() - } - _ => unimplemented!("other kinds of const parameters not supported yet"), - }; let projection = projection_fun(&proj_ty_args, &proj_const_args); - vir::expr! {vcx; (projection) == (term) } + encode_const_check(vcx, deps, const_map, ctx, projection, const_) } } } From 9697d209a83edf44336233beb2bee1c729c6a82e Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 24 Feb 2026 17:13:50 +0100 Subject: [PATCH 30/70] Naming consistency fix --- prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index d3b7a0dfb81..bb1810c7d82 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -200,7 +200,7 @@ impl TaskEncoder for TyConstructorEnc { ); let sized_impl_unknown_fun_idn: FunctionIdn<'vir, vir::Int, vir::Bool> = FunctionIdn::new( - vir::vir_format_identifier!(vcx, "Sized_impl_unknown"), + vir::vir_format_identifier!(vcx, "Sized_unknown_impl"), vir::TYPE_INT, vir::TYPE_BOOL, ); From f38ebd7e681cd4be349a5d5cb0bf2d3587d45dbb Mon Sep 17 00:00:00 2001 From: Jakub Trzykowski <44532716+Trzyq0712@users.noreply.github.com> Date: Wed, 25 Feb 2026 11:24:13 +0100 Subject: [PATCH 31/70] Remove a dbg Co-authored-by: Aurea --- prusti-encoder/src/encoders/const.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prusti-encoder/src/encoders/const.rs b/prusti-encoder/src/encoders/const.rs index 985d556aceb..ac26dd2999c 100644 --- a/prusti-encoder/src/encoders/const.rs +++ b/prusti-encoder/src/encoders/const.rs @@ -52,7 +52,7 @@ impl ConstEnc { ty: ty::Ty<'vir>, context: GParams<'vir>, ) -> Result, EncodeFullError<'vir, Self>> { - match dbg!(const_.kind()) { + match const_.kind() { ty::ConstKind::Param(param) => { let params = deps.require_dep::(context)?; Ok(params.const_expr(param)) From 3725126be9d124de840197d9ca94a5eeaf744c08 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 25 Feb 2026 13:32:12 +0100 Subject: [PATCH 32/70] Integrate context erasure for types into `identity_for_ty` --- .../src/encoders/ty/lifted/ty_constructor.rs | 38 ++- prusti-encoder/src/encoders/ty/rust_ty.rs | 267 +++++++++--------- 2 files changed, 144 insertions(+), 161 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index bb1810c7d82..53fcb95e45d 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -1,3 +1,4 @@ +use prusti_rustc_interface::middle::ty; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder}; use vir::{CallableIdn, CastType, FunctionIdn, HasType}; @@ -51,7 +52,7 @@ impl<'vir> OutputRefAny for TyConstructorEncOutputRef<'vir> {} #[derive(Debug, Clone)] pub struct TyConstructorEncOutput<'vir> { constructor: vir::AdtConstructor<'vir>, - sized_check: vir::ExprBool<'vir>, + sized_check: Option>, } /// Encodes the lifted representation of a Rust type constructor (e.g. Option, @@ -144,25 +145,19 @@ impl TaskEncoder for TyConstructorEnc { vir::TYPE_BOOL, ); match task_key.sizedness { - Sizedness::Sized => is_this_type, - Sizedness::Unsized => vir::expr! {vcx; false }, - Sizedness::ParamDependent(idx) => { - let destructor = vcx.mk_adt_destructor( - vir::vir_format!( - vcx, - "s_{base_name}_typaram_{}", - params.ty_decls()[idx as usize].name - ), - vir::TYPE_TYVAL, - vir::TYPE_TYVAL, - ); - let param_ty = destructor.call()(self_expr); - vir::expr! { vcx; (is_this_type) && ([sized_impl_fun_idn](param_ty)) } - } - Sizedness::Unknown => unimplemented!( - "Handle unknown sizedness for type constructor {}", - task_key.name() - ), + Sizedness::Sized => Some(is_this_type), + Sizedness::Unsized => None, + Sizedness::Dependent(ty) => match ty.kind() { + ty::TyKind::Param(param) => { + let accessor = ty_accessor_functions[param.index as usize]; + let param_ty = accessor.call()(self_expr); + Some( + vir::expr! { vcx; (is_this_type) && ([sized_impl_fun_idn](param_ty)) }, + ) + } + ty::TyKind::Alias(..) => todo!(), + _ => panic!("Dependent sizedness only supported for params and aliases"), + }, } }; Ok(( @@ -176,11 +171,12 @@ impl TaskEncoder for TyConstructorEnc { } fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { - let (mut constructors, mut sized_checks): (Vec<_>, Vec<_>) = + let (mut constructors, sized_checks): (Vec<_>, Vec<_>) = Self::all_outputs_local_no_errors() .into_iter() .map(|out| (out.constructor, out.sized_check)) .unzip(); + let mut sized_checks = sized_checks.into_iter().flatten().collect::>(); vir::with_vcx(|vcx| { let args = vcx.alloc_array(&[vcx.mk_local_decl("non_unit", vir::TYPE_INT)]); let unknown = vcx.mk_adt_constructor("Unknown_type", args); diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index 1b4e21a346b..698b1b379c7 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -244,7 +244,7 @@ pub type RustBuiltin<'tcx> = >::BuiltinData; pub struct RustTyData<'tcx> { pub name: symbol::Symbol, pub params: GParams<'tcx>, - pub sizedness: Sizedness, + pub sizedness: Sizedness<'tcx>, } impl<'tcx> RustTyData<'tcx> { @@ -297,12 +297,12 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { let ty = context.normalize(ty); let name = Self::ty_name(ty); - let (params, args) = Self::identity_for_ty(ty, context.is_trait_extern_spec()); + let (erased_ty, params, args) = Self::identity_for_ty(ty, context.is_trait_extern_spec()); let args = GArgs::new(context, args); let data = RustTyData { name: symbol::Symbol::intern(&name), params, - sizedness: CanonicalTy::from_ty(ty).sizedness(), + sizedness: sizedness_for_ty(vir::with_vcx(|vcx| vcx.tcx()), erased_ty), }; let specifics = TySpecifics::from_ty(ty); let maybe_inhabited = @@ -316,7 +316,7 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { fn from_prim_ty(ty: ty::Ty<'tcx>) -> RustTyDecomposition<'tcx> { let name = Self::prim_ty_name(ty); - let (params, args) = Self::identity_for_prim_ty(ty); + let (_, params, args) = Self::identity_for_prim_ty(ty); let args = GArgs::new(params, args); let data = RustTyData { name: symbol::Symbol::intern(&name), @@ -382,79 +382,108 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { /// For the ty `MyStruct` (with defn /// `struct MyStruct> { ... }`), returns - /// `([>], [i32])`. + /// `(MyStruct, [>], [i32])`. pub(super) fn identity_for_ty( ty: ty::Ty<'tcx>, is_trait_extern_spec: bool, - ) -> (GParams<'tcx>, ty::GenericArgsRef<'tcx>) { - let (params, args) = match *ty.kind() { + ) -> (ty::Ty<'tcx>, GParams<'tcx>, ty::GenericArgsRef<'tcx>) { + let tcx = vir::with_vcx(|vcx| vcx.tcx()); + let (new_ty, params, args) = match *ty.kind() { _ if ty.is_primitive() => return Self::identity_for_prim_ty(ty), - ty::TyKind::Adt(adt, args) => (GParams::from(adt.did()), args), + ty::TyKind::Adt(adt, args) => ( + tcx.type_of(adt.did()).instantiate_identity(), + GParams::from(adt.did()), + args, + ), ty::TyKind::Tuple(tys) => { let gtys = (0..tys.len()).map(|idx| TySpecifics::new_param_ty(idx as u32)); ( + ty::Ty::new_tup(tcx, >ys.clone().collect_vec()), GParams::empty_env(Self::args_from_tys(gtys)), Self::args_from_tys(tys), ) } - ty::TyKind::Array(ty, cst) => vir::with_vcx(|vcx| { - let gcst = TySpecifics::new_param_const(0).into(); - let gty = TySpecifics::new_param_ty(1).into(); - let gparams = Self::args_from_generics([gcst, gty]); - let predicate = - vcx.tcx() - .mk_predicate(ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::ConstArgHasType( - gcst.expect_const(), - vcx.tcx().types.usize, - ), - ))); - let param_env = - ty::ParamEnv::new(vcx.tcx().mk_clauses(&[predicate.expect_clause()])); + ty::TyKind::Array(ty, cst) => { + let gcst = TySpecifics::new_param_const(0); + let gty = TySpecifics::new_param_ty(1); + let gargs = Self::args_from_generics([gcst.into(), gty.into()]); + let predicate = tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstArgHasType( + ty::GenericArg::from(gcst).expect_const(), + tcx.types.usize, + ), + ))); + let param_env = ty::ParamEnv::new(tcx.mk_clauses(&[predicate.expect_clause()])); ( - GParams::new(gparams, param_env, false), + ty::Ty::new_array_with_const_len(tcx, gty, gcst), + GParams::new(gargs, param_env, false), Self::args_from_generics([cst.into(), ty.into()]), ) - }), - ty::TyKind::Slice(ty) | ty::TyKind::RawPtr(ty, _) => { - let gty = Self::args_from_tys([TySpecifics::new_param_ty(0)]); - (GParams::empty_env(gty), Self::args_from_tys([ty])) } - ty::TyKind::Ref(region, ty, _) => { - // TODO: what lifetime should we use here? - let param_region = vir::with_vcx(|vcx| vcx.tcx().lifetimes.re_erased.into()); - let param_ty = TySpecifics::new_param_ty(1).into(); - let gty = Self::args_from_generics([param_region, param_ty]); + ty::TyKind::Slice(ty) => { + let gty = TySpecifics::new_param_ty(0); + let gargs = Self::args_from_tys([gty]); + ( + ty::Ty::new_slice(tcx, gty), + GParams::empty_env(gargs), + Self::args_from_tys([ty]), + ) + } + + ty::TyKind::RawPtr(ty, mutbl) => { + let gty = TySpecifics::new_param_ty(0); + let gargs = Self::args_from_tys([gty]); + ( + ty::Ty::new_ptr(tcx, gty, mutbl), + GParams::empty_env(gargs), + Self::args_from_tys([ty]), + ) + } + ty::TyKind::Ref(_, ty, mutbl) => { + let region = tcx.lifetimes.re_erased; + let gty = TySpecifics::new_param_ty(1); + let gargs = Self::args_from_generics([region.into(), gty.into()]); ( - GParams::empty_env(gty), + ty::Ty::new_ref(tcx, region, gty, mutbl), + GParams::empty_env(gargs), Self::args_from_generics([region.into(), ty.into()]), ) } - ty::TyKind::Alias(..) | ty::TyKind::Param(_) => { - let gty = Self::args_from_tys([TySpecifics::new_param_ty(0)]); - (GParams::empty_env(gty), Self::args_from_tys([ty])) + ty::TyKind::Alias(_, _) => { + let gty = TySpecifics::new_param_ty(0); + let gargs = Self::args_from_tys([gty]); + // TOOD: What should the erased ty be? + (ty, GParams::empty_env(gargs), Self::args_from_tys([ty])) } - ty::TyKind::Closure(did, args) => vir::with_vcx(|vcx| { - let identity = ty::List::identity_for_item(vcx.tcx(), did); - let gargs = vcx.tcx().mk_args(identity.as_closure().parent_args()); - let args = vcx.tcx().mk_args(args.as_closure().parent_args()); + ty::TyKind::Param(_) => { + let gty = TySpecifics::new_param_ty(0); + let gargs = Self::args_from_tys([gty]); + (gty, GParams::empty_env(gargs), Self::args_from_tys([ty])) + } + ty::TyKind::Closure(did, args) => { + let identity = ty::List::identity_for_item(tcx, did); + let gargs = tcx.mk_args(identity.as_closure().parent_args()); + let args = tcx.mk_args(args.as_closure().parent_args()); ( - GParams::new(gargs, vcx.tcx().param_env(did), is_trait_extern_spec), + ty::Ty::new_closure(tcx, did, identity), + GParams::new(gargs, tcx.param_env(did), is_trait_extern_spec), args, ) - }), + } ty::TyKind::Never | ty::TyKind::Str | ty::TyKind::FnPtr(..) => { - (GParams::empty(), ty::GenericArgs::empty()) + (ty, GParams::empty(), ty::GenericArgs::empty()) } _ => todo!("instantiate_identity_for_type for {:?}", ty), }; params.check(args); - (params, args) + (new_ty, params, args) } - fn identity_for_prim_ty(ty: ty::Ty<'tcx>) -> (GParams<'tcx>, ty::GenericArgsRef<'tcx>) { + fn identity_for_prim_ty( + ty: ty::Ty<'tcx>, + ) -> (ty::Ty<'tcx>, GParams<'tcx>, ty::GenericArgsRef<'tcx>) { assert!(ty.is_primitive()); - (GParams::empty(), ty::GenericArgs::empty()) + (ty, GParams::empty(), ty::GenericArgs::empty()) } fn args_from_tys(tys: impl IntoIterator>) -> ty::GenericArgsRef<'tcx> { @@ -641,120 +670,78 @@ impl<'tcx> TySpecifics<'tcx, RustTyDatas> { } #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub enum Sizedness { +pub enum Sizedness<'tcx> { /// A type is definitely `Sized` Sized, /// A type is definitely not `Sized` Unsized, - /// The sizedness of the type depends on the sizedness of the n-th generic parameter the type - /// defines - ParamDependent(u32), - Unknown, -} - -/// A canonicalized version of a type representing its structural identity. -/// -/// This erases all monomorphization information and external context. -/// -/// ### Examples: -/// * **Tuples**: `(U/#2, i32, V/#3)` becomes `(T/#0, T/#1, T/#2)` -/// * **ADTs**: `Box` and `Box>` both become `Box` -/// * **Arrays**: `[u8; 32]` and `[bool; 64]` both become `[T/#0; 0]` -/// * **Pointers**: `&'a mut i32` becomes `&erased mut T/#0` -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -struct CanonicalTy<'tcx>(ty::Ty<'tcx>); - -impl<'tcx> Deref for CanonicalTy<'tcx> { - type Target = ty::Ty<'tcx>; - fn deref(&self) -> &Self::Target { - &self.0 - } + /// The sizedness of the type depends on the sizedness some other type contained within + Dependent(ty::Ty<'tcx>), } -impl<'tcx> CanonicalTy<'tcx> { - fn from_ty(ty: ty::Ty<'tcx>) -> Self { - let tcx = vir::with_vcx(|vcx| vcx.tcx()); - let default_param = ty::Ty::new_param(tcx, 0, symbol::Symbol::intern("T")); - Self(match ty.kind() { - ty::Adt(adt_def, _) => tcx.type_of(adt_def.did()).instantiate_identity(), - - ty::Ref(_, _, mutbl) => { - ty::Ty::new_ref(tcx, tcx.lifetimes.re_erased, default_param, *mutbl) - } - ty::RawPtr(_, mutbl) => ty::Ty::new_ptr(tcx, default_param, *mutbl), - - ty::Param(_) | ty::Alias(..) => default_param, - - ty::Slice(_) => ty::Ty::new_slice(tcx, default_param), - - ty::Array(_, _) => ty::Ty::new_array(tcx, default_param, 0_u64), - - ty::Tuple(tys) => { - let generic_tys: Vec<_> = (0..tys.len()) - .map(|i| ty::Ty::new_param(tcx, i as u32, symbol::Symbol::intern("T"))) - .collect(); - ty::Ty::new_tup(tcx, &generic_tys) - } - - _ => ty, - }) - } - - fn sizedness(&self) -> Sizedness { - check_sizedness(vir::with_vcx(|vcx| vcx.tcx()), self.0) +impl<'tcx> Sizedness<'tcx> { + fn map(self, f: F) -> Self + where + F: FnOnce(ty::Ty<'tcx>) -> ty::Ty<'tcx>, + { + match self { + Sizedness::Sized => Sizedness::Sized, + Sizedness::Unsized => Sizedness::Unsized, + Sizedness::Dependent(param) => Sizedness::Dependent(f(param)), + } } } -/// Computes whether a type is Sized. -/// -/// For generic definitions, the result may indicate that sizedness depends on -/// the instantiation of a specific type parameter. For example, -/// `struct MyStruct(T)` is `Sized` only if `T` is `Sized`. -fn check_sizedness<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> Sizedness { +/// Modified version of `https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ty_utils/ty/fn.sizedness_constraint_for_ty.html` +fn sizedness_for_ty<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> Sizedness<'tcx> { match ty.kind() { - ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::UnsafeBinder(_) - | ty::RawPtr(..) + // Always `Sized` + ty::Bool | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::RawPtr(..) | ty::Ref(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) + | ty::FnDef(..) + | ty::FnPtr(..) | ty::Array(..) - | ty::Pat(..) | ty::Closure(..) | ty::CoroutineClosure(..) - | ty::Never - | ty::Error(_) => Sizedness::Sized, + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never => Sizedness::Sized, - ty::Str | ty::Slice(_) | ty::Dynamic(..) => Sizedness::Unsized, + ty::Str | ty::Slice(..) | ty::Dynamic(..) => Sizedness::Unsized, - ty::Alias(..) | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Foreign(..) => { - Sizedness::Unknown - } + // Maybe `Sized` + ty::Param(..) | ty::Alias(..) | ty::Error(_) => Sizedness::Dependent(ty), - ty::Param(param) => Sizedness::ParamDependent(param.index), + // We cannot instantiate the binder, so just return the *original* type back, + // but only if the inner type has a sized constraint. Thus we skip the binder, + // but don't actually use the result from `sizedness_for_ty`. + ty::UnsafeBinder(inner_ty) => sizedness_for_ty(tcx, inner_ty.skip_binder()).map(|_| ty), - ty::Tuple(tys) => { - // Check last tuple field for sizedness if any - tys.last() - .map_or(Sizedness::Sized, |last| check_sizedness(tcx, *last)) - } + // Never `Sized` + ty::Foreign(..) => Sizedness::Unsized, - ty::Adt(adt, args) => { - let sized_constraint = adt.sizedness_constraint(tcx, ty::SizedTraitKind::Sized); - match sized_constraint { - None => Sizedness::Sized, - Some(cons) => { - let substituted_ty = cons.instantiate(tcx, args); - check_sizedness(tcx, substituted_ty) - } - } + // Recursive cases + ty::Pat(ty, _) => sizedness_for_ty(tcx, *ty), + + // Empty tuple always `Sized`, otherwise sizedness depends on last field + ty::Tuple(tys) => tys + .last() + .map_or(Sizedness::Sized, |last| sizedness_for_ty(tcx, *last)), + + ty::Adt(adt, args) => adt + .sizedness_constraint(tcx, ty::SizedTraitKind::Sized) + .map_or(Sizedness::Sized, |intermediate| { + let ty = intermediate.instantiate(tcx, args); + sizedness_for_ty(tcx, ty) + }), + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { + panic!("unexpected type `{ty:?}` in `sizedness_for_ty`") } } } From 09b19b142862567a8cb8f8b4658297ff324fb659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Fiala?= Date: Wed, 25 Feb 2026 15:41:51 +0100 Subject: [PATCH 33/70] Resolve some issues and clean up in `rust_ty.rs` --- prusti-encoder/src/encoders/ty/rust_ty.rs | 95 ++++++++--------------- 1 file changed, 34 insertions(+), 61 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index 698b1b379c7..d70ba057692 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -304,7 +304,7 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { params, sizedness: sizedness_for_ty(vir::with_vcx(|vcx| vcx.tcx()), erased_ty), }; - let specifics = TySpecifics::from_ty(ty); + let specifics = TySpecifics::from_ty(erased_ty); let maybe_inhabited = vir::with_vcx(|vcx| !ty.is_privately_uninhabited(vcx.tcx(), context.typing_env())); RustTyDecomposition { @@ -390,15 +390,15 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { let tcx = vir::with_vcx(|vcx| vcx.tcx()); let (new_ty, params, args) = match *ty.kind() { _ if ty.is_primitive() => return Self::identity_for_prim_ty(ty), - ty::TyKind::Adt(adt, args) => ( - tcx.type_of(adt.did()).instantiate_identity(), - GParams::from(adt.did()), - args, - ), + ty::TyKind::Adt(adt, args) => { + let params = GParams::from(adt.did()); + let new_ty = ty::Ty::new_adt(tcx, adt, params.rust_params()); + (new_ty, params, args) + } ty::TyKind::Tuple(tys) => { let gtys = (0..tys.len()).map(|idx| TySpecifics::new_param_ty(idx as u32)); ( - ty::Ty::new_tup(tcx, >ys.clone().collect_vec()), + ty::Ty::new_tup_from_iter(tcx, gtys.clone()), GParams::empty_env(Self::args_from_tys(gtys)), Self::args_from_tys(tys), ) @@ -408,10 +408,7 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { let gty = TySpecifics::new_param_ty(1); let gargs = Self::args_from_generics([gcst.into(), gty.into()]); let predicate = tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::ConstArgHasType( - ty::GenericArg::from(gcst).expect_const(), - tcx.types.usize, - ), + ty::ClauseKind::ConstArgHasType(gcst, tcx.types.usize), ))); let param_env = ty::ParamEnv::new(tcx.mk_clauses(&[predicate.expect_clause()])); ( @@ -422,42 +419,31 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { } ty::TyKind::Slice(ty) => { let gty = TySpecifics::new_param_ty(0); - let gargs = Self::args_from_tys([gty]); - ( - ty::Ty::new_slice(tcx, gty), - GParams::empty_env(gargs), - Self::args_from_tys([ty]), - ) + let params = GParams::empty_env(Self::args_from_tys([gty])); + let new_ty = ty::Ty::new_slice(tcx, gty); + (new_ty, params, Self::args_from_tys([ty])) } - ty::TyKind::RawPtr(ty, mutbl) => { let gty = TySpecifics::new_param_ty(0); - let gargs = Self::args_from_tys([gty]); - ( - ty::Ty::new_ptr(tcx, gty, mutbl), - GParams::empty_env(gargs), - Self::args_from_tys([ty]), - ) + let params = GParams::empty_env(Self::args_from_tys([gty])); + let new_ty = ty::Ty::new_ptr(tcx, gty, mutbl); + (new_ty, params, Self::args_from_tys([ty])) } - ty::TyKind::Ref(_, ty, mutbl) => { - let region = tcx.lifetimes.re_erased; + ty::TyKind::Ref(region, ty, mutbl) => { + // TODO: what lifetime should we use here? + let param_region = tcx.lifetimes.re_erased; let gty = TySpecifics::new_param_ty(1); - let gargs = Self::args_from_generics([region.into(), gty.into()]); ( - ty::Ty::new_ref(tcx, region, gty, mutbl), - GParams::empty_env(gargs), + ty::Ty::new_ref(tcx, param_region, gty, mutbl), + GParams::empty_env(Self::args_from_generics([param_region.into(), gty.into()])), Self::args_from_generics([region.into(), ty.into()]), ) } - ty::TyKind::Alias(_, _) => { - let gty = TySpecifics::new_param_ty(0); - let gargs = Self::args_from_tys([gty]); - // TOOD: What should the erased ty be? - (ty, GParams::empty_env(gargs), Self::args_from_tys([ty])) - } - ty::TyKind::Param(_) => { + ty::TyKind::Alias(_, _) | ty::TyKind::Param(_) => { let gty = TySpecifics::new_param_ty(0); let gargs = Self::args_from_tys([gty]); + // Note: an `Alias` is turned into a `Param` here with the alias + // itself as the type argument. (gty, GParams::empty_env(gargs), Self::args_from_tys([ty])) } ty::TyKind::Closure(did, args) => { @@ -512,29 +498,25 @@ impl<'tcx> TySpecifics<'tcx, RustTyDatas> { let fields = args .iter() .enumerate() - .map(|(i, _)| RustFieldData { + .map(|(i, inner)| RustFieldData { name: symbol::Symbol::intern(&format!("_{i}")), fid: abi::FieldIdx::from_usize(i), - ty: LazyRustTy(Self::new_param_ty(i as u32)), + ty: LazyRustTy(inner), }) .collect::>(); TySpecifics::mk_structlike((), fields) } - ty::TyKind::Array(_, _) => TySpecifics::ArrayLike(ArrayData { + ty::TyKind::Array(inner, _) => TySpecifics::ArrayLike(ArrayData { slice: false, - data: LazyRustTy(Self::new_param_ty(1)), + data: LazyRustTy(*inner), }), - ty::TyKind::Slice(_) => TySpecifics::ArrayLike(ArrayData { + ty::TyKind::Slice(inner) => TySpecifics::ArrayLike(ArrayData { slice: true, - data: LazyRustTy(Self::new_param_ty(0)), + data: LazyRustTy(*inner), }), - ty::TyKind::Ref(_, _, mutability) => match mutability { - ty::Mutability::Mut => { - TySpecifics::mk_mutref(LazyRustTy(TySpecifics::new_param_ty(1))) - } - ty::Mutability::Not => { - TySpecifics::mk_immref(LazyRustTy(TySpecifics::new_param_ty(1))) - } + ty::TyKind::Ref(_, inner, mutability) => match mutability { + ty::Mutability::Mut => TySpecifics::mk_mutref(LazyRustTy(*inner)), + ty::Mutability::Not => TySpecifics::mk_immref(LazyRustTy(*inner)), }, // TODO: add raw pointer support ty::TyKind::RawPtr(..) => TySpecifics::mk_opaque(()), @@ -587,14 +569,8 @@ impl<'tcx> TySpecifics<'tcx, RustTyDatas> { } } else { match adt.adt_kind() { - ty::AdtKind::Struct => { - let data = Self::from_struct(adt.non_enum_variant()); - Self::StructLike(data) - } - ty::AdtKind::Enum => { - let data = Self::from_enum(adt); - Self::EnumLike(data) - } + ty::AdtKind::Struct => Self::StructLike(Self::from_struct(adt.non_enum_variant())), + ty::AdtKind::Enum => Self::EnumLike(Self::from_enum(adt)), ty::AdtKind::Union => { // TODO: add union support Self::mk_opaque(()) @@ -680,10 +656,7 @@ pub enum Sizedness<'tcx> { } impl<'tcx> Sizedness<'tcx> { - fn map(self, f: F) -> Self - where - F: FnOnce(ty::Ty<'tcx>) -> ty::Ty<'tcx>, - { + fn map(self, f: impl FnOnce(ty::Ty<'tcx>) -> ty::Ty<'tcx>) -> Self { match self { Sizedness::Sized => Sizedness::Sized, Sizedness::Unsized => Sizedness::Unsized, From 60f43b0e06f3c8af5fccffc0c44873079d6945b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Fiala?= Date: Wed, 25 Feb 2026 15:42:00 +0100 Subject: [PATCH 34/70] Revert unnecessary change --- vir/src/macros.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/vir/src/macros.rs b/vir/src/macros.rs index 95c926a4308..8d6fcd5dd5c 100644 --- a/vir/src/macros.rs +++ b/vir/src/macros.rs @@ -410,12 +410,8 @@ macro_rules! expr_inner { ) }; (@forall_qvars($qvars:ident); :: $($tokens:tt)*) => { compile_error!(concat!("VIR missing triggers or body: `" , stringify!($($tokens)*), "`")) }; - (@forall_qvars($qvars:ident); , ..[$outer_decls:expr] $($tokens:tt)*) => { { - $qvars.extend($outer_decls.iter().map(|local| $crate::CastType::as_dyn(local.clone()))); - $crate::expr_inner!(@forall_qvars($qvars); $($tokens)*) - } }; - (@forall_qvars($qvars:ident); , [$outer_decl:expr] $($tokens:tt)*) => { { - $qvars.push($crate::CastType::as_dyn($outer_decl)); + (@forall_qvars($qvars:ident); , ..[$outer_decls:ident] $($tokens:tt)*) => { { + $qvars.extend($outer_decls.clone().into_iter().map(|local| $crate::CastType::as_dyn(local))); $crate::expr_inner!(@forall_qvars($qvars); $($tokens)*) } }; (@forall_qvars($qvars:ident); , $qvar:ident : $qtype:tt $($tokens:tt)* ) => { { From 16c529486d39ce147cde27da406529aeb4f0f4de Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 25 Feb 2026 16:38:05 +0100 Subject: [PATCH 35/70] Add support of checking sizedness of aliases --- .../src/encoders/ty/lifted/ty_constructor.rs | 42 ++++++++++++++++--- prusti-encoder/src/encoders/ty/rust_ty.rs | 3 +- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index 53fcb95e45d..2cb2eb42f3f 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -2,7 +2,10 @@ use prusti_rustc_interface::middle::ty; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder}; use vir::{CallableIdn, CastType, FunctionIdn, HasType}; -use crate::encoders::ty::{RustTy, Sizedness, generics::GenericParamsEnc}; +use crate::encoders::ty::{ + RustTy, Sizedness, + generics::{GArgs, GArgsTyEnc, GenericParamsEnc, traits::TraitEnc}, +}; use super::r#typeof::{TypeOfEnc, TypeOfEncOutputRef}; @@ -134,7 +137,7 @@ impl TaskEncoder for TyConstructorEnc { vcx.mk_adt_constructor(type_function_ident.name().to_str(), vcx.alloc_slice(&args)); let sized_check = { // Use a local expression named "Self" to build the function body - let self_decl = vcx.mk_local_decl("Self", vir::TYPE_TYVAL); + let self_decl = vcx.mk_local_decl("Self$0", vir::TYPE_TYVAL); let self_expr = vcx.mk_local_ex(self_decl); let is_this_type = vcx.mk_adt_discriminator_expr(self_expr, type_function_ident.name().to_str()); @@ -155,8 +158,37 @@ impl TaskEncoder for TyConstructorEnc { vir::expr! { vcx; (is_this_type) && ([sized_impl_fun_idn](param_ty)) }, ) } - ty::TyKind::Alias(..) => todo!(), - _ => panic!("Dependent sizedness only supported for params and aliases"), + ty::TyKind::Alias(ty::AliasTyKind::Projection, alias_ty) => { + let alias_did = alias_ty.def_id; + let trait_def = alias_ty.trait_def_id(vcx.tcx()); + let trait_ = deps.require_ref::(trait_def)?; + let projection_fun = trait_.funs.assoc_types[&alias_did]; + let args = deps.require_dep::(GArgs::new( + task_key.params, + alias_ty.args, + ))?; + let inner_expr = vir::expr! { vcx; + (is_this_type) && ([sized_impl_fun_idn]( + [projection_fun]([..[args.get_ty()]], [..[args.get_const()]]) + )) + }; + let with_consts_bound = params.const_decls().iter().enumerate().rfold( + inner_expr, + |expr, (i, decl)| { + let accessor = const_accessor_functions[i]; + vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) + }, + ); + let with_tys_bound = params.ty_decls().iter().enumerate().rfold( + with_consts_bound, + |expr, (i, decl)| { + let accessor = ty_accessor_functions[i]; + vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) + }, + ); + Some(with_tys_bound) + } + _ => panic!("Unsupported dependent sizedness for {ty:?}"), }, } }; @@ -201,7 +233,7 @@ impl TaskEncoder for TyConstructorEnc { vir::TYPE_BOOL, ); - let self_decl = vcx.mk_local_decl("Self", vir::TYPE_TYVAL); + let self_decl = vcx.mk_local_decl("Self$0", vir::TYPE_TYVAL); let unknown_type_check = { let self_expr = vcx.mk_local_ex(self_decl); let is_unknown_type = vcx.mk_adt_discriminator_expr(self_expr, "Unknown_type"); diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index d70ba057692..8c2171e1d42 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -658,9 +658,8 @@ pub enum Sizedness<'tcx> { impl<'tcx> Sizedness<'tcx> { fn map(self, f: impl FnOnce(ty::Ty<'tcx>) -> ty::Ty<'tcx>) -> Self { match self { - Sizedness::Sized => Sizedness::Sized, - Sizedness::Unsized => Sizedness::Unsized, Sizedness::Dependent(param) => Sizedness::Dependent(f(param)), + sizedness => sizedness, } } } From 2377ab6e9c07db889586e2912223e8bc72788f17 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 25 Feb 2026 21:28:13 +0100 Subject: [PATCH 36/70] Move trait impl check to impl encoder --- .../src/encoders/ty/generics/trait_impls.rs | 455 ++++++++++++++++- .../src/encoders/ty/generics/traits.rs | 476 ++---------------- prusti-encoder/src/encoders/ty/rust_ty.rs | 4 +- 3 files changed, 476 insertions(+), 459 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index e37d18c9077..4feb763f2ca 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -1,14 +1,30 @@ -use prusti_rustc_interface::{middle::ty::AssocKind, span::def_id::DefId}; +use rustc_hash::FxHashMap; +use std::collections::VecDeque; + +use prusti_rustc_interface::{ + middle::{ty, ty::AssocKind}, + span::def_id::DefId, +}; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; -use vir::{Domain, vir_format_identifier}; +use vir::{CallableIdn, CastType, Domain, vir_format_identifier}; -use crate::encoders::ty::{ - RustTyDecomposition, - generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc, traits::TraitEnc}, +use crate::encoders::{ + ConstEnc, + r#const::ConstEncTask, + ty::{ + RustTyDecomposition, + generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc, traits::TraitEnc}, + lifted::TyConstructorEnc, + }, }; pub struct TraitImplEnc; +#[derive(Debug, Clone)] +pub struct TraitImplEncOutput<'vir> { + pub impl_condition: vir::ExprBool<'vir>, +} + impl TaskEncoder for TraitImplEnc { task_encoder::encoder_cache!(TraitImplEnc); @@ -24,6 +40,7 @@ impl TaskEncoder for TraitImplEnc { type TaskDescription<'vir> = DefId; type OutputFullLocal<'vir> = Domain<'vir>; + type OutputFullDependency<'vir> = TraitImplEncOutput<'vir>; fn do_encode_full<'vir>( task_key: &Self::TaskKey<'vir>, @@ -112,22 +129,418 @@ impl TaskEncoder for TraitImplEnc { } } - Ok( - ( - vcx.mk_domain( - vir_format_identifier!( - vcx, - "t_{impl_idx}_{}_{impl_ty}", - trait_data.trait_name, - ), - &[], - vcx.alloc_slice(&axioms), - &[], - None, - ), - (), - ), - ) + let domain = vcx.mk_domain( + vir_format_identifier!(vcx, "t_{impl_idx}_{}_{impl_ty}", trait_data.trait_name,), + &[], + vcx.alloc_slice(&axioms), + &[], + None, + ); + + let impl_condition = impl_block_condition(vcx, deps, *task_key); + + Ok((domain, TraitImplEncOutput { impl_condition })) }) } } + +fn impl_block_condition<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, + impl_did: DefId, +) -> vir::ExprBool<'vir> { + let tcx = vcx.tcx(); + let impl_ctx = GParams::from(impl_did); + + let mut checks = Vec::new(); + + // Collect the mappings from generic arguments to viper expressions that occur + // in the impl block, such that we can refer to them when encoding the trait + // bounds of the impl block. For example, for an impl like `impl Trait for + // (T, i32)`, we would map `T` to an accessor expression to the first member of + // the tuple type - `self.2_tup.0` + let mut ty_map = FxHashMap::default(); + let mut const_map = FxHashMap::default(); + + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); + let trait_rust_tys = trait_ref.args.iter().filter_map(|arg| arg.as_type()); + let trait_params = deps + .require_dep::(trait_ref.def_id.into()) + .unwrap(); + + for (&ty_expr, rust_ty) in std::iter::zip(trait_params.ty_exprs(), trait_rust_tys) { + checks.extend(encode_type_check( + vcx, + deps, + &mut ty_map, + &mut const_map, + impl_ctx, + ty_expr, + rust_ty, + )); + } + + let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); + + // Process the projection predicates first as they might introduce new bindings + // for generic parameters + let projection_preds = caller_bounds + .iter() + .filter_map(ty::Clause::as_projection_clause) + .map(ty::Binder::skip_binder); + + checks.extend(process_projection_predicates( + vcx, + deps, + &mut ty_map, + &mut const_map, + impl_ctx, + projection_preds, + )); + + // Process trait predicates last as they cannot introduce new bindings for + // generics + let trait_preds = caller_bounds + .iter() + .filter_map(ty::Clause::as_trait_clause) + .map(ty::Binder::skip_binder); + + checks.push(process_trait_predicates( + vcx, + deps, + &ty_map, + &const_map, + impl_ctx, + trait_preds, + )); + + vcx.mk_conj(&checks) +} + +/// Encode a check that the type expression `expr` is the same as the rust type `ty`. +/// Additionally, collect the generic parameters of the impl block and map them to +/// their occurances in the type expression, such that they can be referred to when +/// encoding the trait bounds of the impl block. +/// +/// For example, for `expr` equal to `(T, i32)`, `T` would be mapped to an accessor +/// expression to the first member of the tuple type - `expr.2_tup.0` +fn encode_type_check<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, + ty_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, + const_map: &mut FxHashMap, vir::ExprSnap<'vir>>, + ctx: GParams<'vir>, + expr: vir::ExprTyVal<'vir>, + ty: ty::Ty<'vir>, +) -> Option> { + let decomp = RustTyDecomposition::from_ty(ty, ctx); + + if decomp.ty.specifics.is_param() { + let arg = decomp.args.args().first().expect("Param missing arg"); + + use std::collections::hash_map::Entry; + return match ty_map.entry(*arg) { + Entry::Occupied(occ) => { + // Already seen this T: ensure this type matches the originally found + Some(vcx.mk_eq_expr(expr, *occ.get())) + } + Entry::Vacant(vac) => { + // First time seeing T: map it to the current accessor path for future references + vac.insert(expr); + None + } + }; + } + + let ty_enc = deps.require_ref::(decomp.ty).unwrap(); + + let discr_check = vcx.mk_adt_discriminator_expr(expr, ty_enc.ty_constructor.name().to_str()); + + let mut conjuncts = vec![discr_check]; + + let args = decomp.args.args(); + + // Collect checks for inner types + let inner_types = args.iter().filter_map(|arg| arg.as_type()); + for (i, inner_ty) in inner_types.enumerate() { + let accessor = ty_enc.ty_param_accessors[i]; + + let expr = accessor.call()(expr); + conjuncts.extend(encode_type_check( + vcx, deps, ty_map, const_map, ctx, expr, inner_ty, + )); + } + + // Collect the "locations" of const parameters and assert equality for repeated occurances + let consts = args.iter().filter_map(|arg| arg.as_const()); + for (i, const_) in consts.enumerate() { + let accessor = ty_enc.const_param_accessors[i]; + let expr = accessor.call()(expr); + + conjuncts.extend(encode_const_check( + vcx, + deps, + const_map, + ctx, + expr.upcast_ty(), + const_, + )); + } + + Some(vcx.mk_conj(&conjuncts)) +} + +/// Encode a check that the expression `expr` is the same as the rust const `const_`. +fn encode_const_check<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, + const_map: &mut FxHashMap, vir::ExprSnap<'vir>>, + ctx: GParams<'vir>, + expr: vir::ExprSnap<'vir>, + const_: ty::Const<'vir>, +) -> Option> { + match const_.kind() { + ty::ConstKind::Param(..) => { + use std::collections::hash_map::Entry; + match const_map.entry(const_.into()) { + Entry::Occupied(occ) => { + // Already seen this const parameter: ensure this const expression matches the originally found + Some(vcx.mk_eq_expr(expr, *occ.get())) + } + Entry::Vacant(vac) => { + // First time seeing this const parameter: map it to the current accessor path for future references + vac.insert(expr); + None + } + } + } + ty::ConstKind::Value(val) => { + let task = ConstEncTask::Ty { + const_, + ty: val.ty, + context: ctx, + }; + let value = deps.require_dep::(task).unwrap(); + Some(vcx.mk_eq_expr(expr, value.upcast_ty())) + } + _ => unimplemented!("other kinds of const parameters not supported yet"), + } +} + +/// Assemble a VIR type using the map of generic parameters we have collected earlier. +fn assemble_type<'vir>( + tcx: ty::TyCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, + ty_map: &FxHashMap, vir::ExprTyVal<'vir>>, + const_map: &FxHashMap, vir::ExprSnap<'vir>>, + ctx: GParams<'vir>, + ty: ty::Ty<'vir>, +) -> vir::ExprTyVal<'vir> { + let decomp = RustTyDecomposition::from_ty(ty, ctx); + + if decomp.ty.specifics.is_param() { + let arg = decomp.args.args().first().expect("Param missing arg"); + return match arg.expect_ty().kind() { + ty::TyKind::Param(param) => *ty_map + .get(arg) + .expect(&format!("generic {param:?} to be mapped")), + ty::TyKind::Alias(ty::AliasTyKind::Projection, alias) => { + let trait_did = tcx.parent(alias.def_id); + let trait_ = deps.require_ref::(trait_did).unwrap(); + + let assoc_ty_fun = trait_ + .funs + .assoc_types + .get(&alias.def_id) + .expect("associated type to be in the mapping"); + + let gargs = GArgs::new(trait_did, alias.args); + let gargs = deps.require_dep::(gargs).unwrap(); + assoc_ty_fun(gargs.get_ty(), gargs.get_const()) + } + _ => unimplemented!("unsupported kind of generic parameter in type position"), + }; + } + + let ty_enc = deps.require_ref::(decomp.ty).unwrap(); + + let args = decomp.args.args(); + + let inner_ty_args = args + .iter() + .filter_map(|arg| arg.as_type()) + .map(|inner_ty| assemble_type(tcx, deps, ty_map, const_map, ctx, inner_ty)) + .collect::>(); + + let inner_const_args = args + .iter() + .filter_map(|arg| arg.as_const()) + .map(|const_| assemble_const(deps, ctx, const_map, const_).downcast_ty()) + .collect::>(); + + (ty_enc.ty_constructor)(&inner_ty_args, &inner_const_args) +} + +fn assemble_const<'vir>( + deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, + ctx: GParams<'vir>, + const_map: &FxHashMap, vir::ExprSnap<'vir>>, + const_: ty::Const<'vir>, +) -> vir::ExprSnap<'vir> { + match const_.kind() { + ty::ConstKind::Param(..) => const_map + .get(&const_.into()) + .copied() + .expect("The const generic should have been bound in the map"), + ty::ConstKind::Value(val) => { + let task = ConstEncTask::Ty { + const_: const_, + ty: val.ty, + context: ctx, + }; + deps.require_dep::(task).unwrap().upcast_ty() + } + _ => unimplemented!("other kinds of const parameters not supported yet"), + } +} + +/// Process the projection predicates in a topological order, such that when processing a +/// projection predicate, the projection term is already mapped to a VIR expression. This is needed +/// to handle cases like `T::Item: Trait` where we need to refer to `T::Item` when encoding the +/// trait bound check for `Trait`. +fn process_projection_predicates<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, + ty_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, + const_map: &mut FxHashMap, vir::ExprSnap<'vir>>, + ctx: GParams<'vir>, + projections: impl Iterator>, +) -> Option> { + let mut worklist: VecDeque<_> = projections.collect(); + let mut conjuncts = Vec::new(); + while let Some(proj) = worklist.pop_front() { + if is_alias_ready(&proj.projection_term, ty_map, const_map) { + conjuncts.extend(process_projection(vcx, deps, ty_map, const_map, ctx, proj)); + } else { + worklist.push_back(proj); + } + } + if conjuncts.is_empty() { + None + } else { + Some(vcx.mk_conj(&conjuncts)) + } +} + +fn process_projection<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, + ty_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, + const_map: &mut FxHashMap, vir::ExprSnap<'vir>>, + ctx: GParams<'vir>, + projection: ty::ProjectionPredicate<'vir>, +) -> Option> { + let tcx = vcx.tcx(); + let proj_did = projection.def_id(); + let trait_did = projection.trait_def_id(tcx); + let trait_ = deps.require_ref::(trait_did).unwrap(); + + let proj_args = projection.projection_term.args; + + let proj_ty_args: Vec<_> = proj_args + .iter() + .filter_map(|arg| arg.as_type()) + .map(|ty| assemble_type(tcx, deps, ty_map, const_map, ctx, ty)) + .collect(); + let proj_const_args: Vec<_> = proj_args + .iter() + .filter_map(|arg| arg.as_const()) + .map(|const_| assemble_const(deps, ctx, const_map, const_).downcast_ty()) + .collect(); + + match projection.term.kind() { + ty::TermKind::Ty(tgt_ty) => { + let projection_fun = trait_ + .funs + .assoc_types + .get(&proj_did) + .expect("Projection did should be in the mapping"); + + let projection = projection_fun(&proj_ty_args, &proj_const_args); + + encode_type_check(vcx, deps, ty_map, const_map, ctx, projection, tgt_ty) + } + ty::TermKind::Const(const_) => { + let projection_fun = trait_ + .funs + .assoc_consts + .get(&proj_did) + .expect("Projection did should be in the mapping"); + let projection = projection_fun(&proj_ty_args, &proj_const_args); + + encode_const_check(vcx, deps, const_map, ctx, projection, const_) + } + } +} + +/// Check whether all generic parameters of the given alias term have alredy been mapped +fn is_alias_ready<'vir>( + term: &ty::AliasTerm<'vir>, + ty_map: &FxHashMap, vir::ExprTyVal<'vir>>, + const_map: &FxHashMap, vir::ExprSnap<'vir>>, +) -> bool { + for arg in term.args { + for arg in arg.walk() { + match arg.kind() { + ty::GenericArgKind::Type(ty) => { + if let ty::TyKind::Param(_) = ty.kind() { + if !ty_map.contains_key(&arg) { + return false; + } + } + } + ty::GenericArgKind::Const(_) => { + if !const_map.contains_key(&arg) { + return false; + } + } + _ => {} + } + } + } + true +} + +fn process_trait_predicates<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, + ty_map: &FxHashMap, vir::ExprTyVal<'vir>>, + const_map: &FxHashMap, vir::ExprSnap<'vir>>, + ctx: GParams<'vir>, + trait_preds: impl Iterator>, +) -> vir::ExprBool<'vir> { + let tcx = vcx.tcx(); + let mut conjuncts = Vec::new(); + for trait_pred in trait_preds { + let required_trait_impl_fun = deps + .require_ref::(trait_pred.def_id()) + .unwrap() + .impl_fun; + + let trait_args = trait_pred.trait_ref.args; + let ty_args: Vec<_> = trait_args + .iter() + .filter_map(|arg| arg.as_type()) + .map(|arg| assemble_type(tcx, deps, &ty_map, &const_map, ctx, arg)) + .collect(); + + let const_args: Vec<_> = trait_args + .iter() + .filter_map(|arg| arg.as_const()) + .map(|const_| assemble_const(deps, ctx, &const_map, const_).downcast_ty()) + .collect(); + + conjuncts.push(required_trait_impl_fun(&ty_args, &const_args)); + } + vcx.mk_conj(&conjuncts) +} diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 13373114fd0..cf33279ac61 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -1,18 +1,12 @@ use prusti_rustc_interface::{middle::ty, span::def_id::DefId}; use rustc_hash::FxHashMap; -use std::collections::VecDeque; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder, TaskEncoderDependencies}; -use vir::{CallableIdn, CastType, FunctionIdn, vir_format_identifier}; - -use crate::encoders::{ - ConstEnc, - r#const::ConstEncTask, - ty::{ - RustTyDecomposition, - generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc}, - lifted::TyConstructorEnc, - pure::TyPureEnc, - }, +use vir::{FunctionIdn, vir_format_identifier}; + +use crate::encoders::ty::{ + RustTyDecomposition, + generics::{GParams, GenericParamsEnc, trait_impls::TraitImplEnc}, + pure::TyPureEnc, }; pub struct TraitEnc; @@ -105,95 +99,33 @@ impl TaskEncoder for TraitEnc { )?; let impl_fun_body = { - let mut trait_impl_checks = Vec::new(); - - for impl_did in tcx.all_impls(*task_key) { - let impl_ctx = GParams::from(impl_did); - - let mut checks = Vec::new(); - - // Collect the mappings from generic arguments to viper expressions that occur - // in the impl block, such that we can refer to them when encoding the trait - // bounds of the impl block. For example, for an impl like `impl Trait for - // (T, i32)`, we would map `T` to an accessor expression to the first member of - // the tuple type - `self.2_tup.0` - let mut ty_map = FxHashMap::default(); - let mut const_map = FxHashMap::default(); - - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); - let trait_rust_tys = trait_ref.args.iter().filter_map(|arg| arg.as_type()); - - for (&ty_expr, rust_ty) in std::iter::zip(params.ty_exprs(), trait_rust_tys) { - checks.extend(encode_type_check( - vcx, - deps, - &mut ty_map, - &mut const_map, - impl_ctx, - ty_expr, - rust_ty, - )); - } - - let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); - - // Process the projection predicates first as they might introduce new bindings - // for generic parameters - let projection_preds = caller_bounds - .iter() - .filter_map(ty::Clause::as_projection_clause) - .map(ty::Binder::skip_binder); - - checks.extend(process_projection_predicates( - vcx, - deps, - &mut ty_map, - &mut const_map, - impl_ctx, - projection_preds, - )); - - // Process trait predicates last as they cannot introduce new bindings for - // generics - let trait_preds = caller_bounds - .iter() - .filter_map(ty::Clause::as_trait_clause) - .map(ty::Binder::skip_binder); - - checks.push(process_trait_predicates( - vcx, - deps, - &ty_map, - &const_map, - impl_ctx, - trait_preds, - )); - - if !checks.is_empty() { - trait_impl_checks.push(vcx.mk_conj(&checks)); - } - } - - { - // Case for unknown types - let self_expr = params.ty_exprs()[0]; - - let is_unknown_type = vcx.mk_adt_discriminator_expr(self_expr, "Unknown_type"); - - let unknown_id_destructor = - vcx.mk_adt_destructor("non_unit", vir::TYPE_TYVAL, vir::TYPE_INT); - let extracted_id = unknown_id_destructor.call()(self_expr); - - let unknown_impls = impl_unknown_fun_idn( - ¶ms.ty_exprs()[1..], - params.const_exprs(), - extracted_id, - ); - - let unknown_check = vcx.mk_conj(&[is_unknown_type, unknown_impls]); - - trait_impl_checks.push(unknown_check); - } + let mut trait_impl_checks: Vec<_> = tcx + .all_impls(*task_key) + .map(|impl_did| { + deps.require_dep::(impl_did) + .unwrap() + .impl_condition + }) + .collect(); + + // Case for unknown types + let self_expr = params.ty_exprs()[0]; + + let is_unknown_type = vcx.mk_adt_discriminator_expr(self_expr, "Unknown_type"); + + let unknown_id_destructor = + vcx.mk_adt_destructor("non_unit", vir::TYPE_TYVAL, vir::TYPE_INT); + let extracted_id = unknown_id_destructor.call()(self_expr); + + let unknown_impls = impl_unknown_fun_idn( + ¶ms.ty_exprs()[1..], + params.const_exprs(), + extracted_id, + ); + + let unknown_check = vcx.mk_conj(&[is_unknown_type, unknown_impls]); + + trait_impl_checks.push(unknown_check); vcx.mk_disj(&trait_impl_checks) }; @@ -239,342 +171,14 @@ impl TaskEncoder for TraitEnc { } } -/// Encode a check that the type expression `expr` is the same as the rust type `ty`. -/// Additionally, collect the generic parameters of the impl block and map them to -/// their occurances in the type expression, such that they can be referred to when -/// encoding the trait bounds of the impl block. -/// -/// For example, for `expr` equal to `(T, i32)`, `T` would be mapped to an accessor -/// expression to the first member of the tuple type - `expr.2_tup.0` -fn encode_type_check<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, - deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, - ty_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, - const_map: &mut FxHashMap, vir::ExprSnap<'vir>>, - ctx: GParams<'vir>, - expr: vir::ExprTyVal<'vir>, - ty: ty::Ty<'vir>, -) -> Option> { - let decomp = RustTyDecomposition::from_ty(ty, ctx); - - if decomp.ty.specifics.is_param() { - let arg = decomp.args.args().first().expect("Param missing arg"); - - use std::collections::hash_map::Entry; - return match ty_map.entry(*arg) { - Entry::Occupied(occ) => { - // Already seen this T: ensure this type matches the originally found - Some(vcx.mk_eq_expr(expr, *occ.get())) - } - Entry::Vacant(vac) => { - // First time seeing T: map it to the current accessor path for future references - vac.insert(expr); - None - } - }; - } - - let ty_enc = deps.require_ref::(decomp.ty).unwrap(); - - let discr_check = vcx.mk_adt_discriminator_expr(expr, ty_enc.ty_constructor.name().to_str()); - - let mut conjuncts = vec![discr_check]; - - let args = decomp.args.args(); - - // Collect checks for inner types - let inner_types = args.iter().filter_map(|arg| arg.as_type()); - for (i, inner_ty) in inner_types.enumerate() { - let accessor = ty_enc.ty_param_accessors[i]; - - let expr = accessor.call()(expr); - conjuncts.extend(encode_type_check( - vcx, deps, ty_map, const_map, ctx, expr, inner_ty, - )); - } - - // Collect the "locations" of const parameters and assert equality for repeated occurances - let consts = args.iter().filter_map(|arg| arg.as_const()); - for (i, const_) in consts.enumerate() { - let accessor = ty_enc.const_param_accessors[i]; - let expr = accessor.call()(expr); - - conjuncts.extend(encode_const_check( - vcx, - deps, - const_map, - ctx, - expr.upcast_ty(), - const_, - )); - } - - Some(vcx.mk_conj(&conjuncts)) -} - -/// Encode a check that the expression `expr` is the same as the rust const `const_`. -fn encode_const_check<'a>( - vcx: &'a vir::VirCtxt<'a>, - deps: &mut TaskEncoderDependencies<'a, TraitEnc>, - const_map: &mut HashMap, vir::ExprSnap<'a>>, - ctx: GParams<'a>, - expr: vir::ExprSnap<'a>, - const_: ty::Const<'a>, -) -> Option> { - match const_.kind() { - ty::ConstKind::Param(..) => { - use std::collections::hash_map::Entry; - match const_map.entry(const_.into()) { - Entry::Occupied(occ) => { - // Already seen this const parameter: ensure this const expression matches the originally found - Some(vcx.mk_eq_expr(expr, *occ.get())) - } - Entry::Vacant(vac) => { - // First time seeing this const parameter: map it to the current accessor path for future references - vac.insert(expr); - None - } - } - } - ty::ConstKind::Value(val) => { - let task = ConstEncTask::Ty { - const_, - ty: val.ty, - context: ctx, - }; - let value = deps.require_dep::(task).unwrap(); - Some(vcx.mk_eq_expr(expr, value.upcast_ty())) - } - _ => unimplemented!("other kinds of const parameters not supported yet"), - } -} - -/// Assemble a VIR type using the map of generic parameters we have collected earlier. -fn assemble_type<'vir>( - tcx: ty::TyCtxt<'vir>, - deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, - ty_map: &FxHashMap, vir::ExprTyVal<'vir>>, - const_map: &FxHashMap, vir::ExprSnap<'vir>>, - ctx: GParams<'vir>, - ty: ty::Ty<'vir>, -) -> vir::ExprTyVal<'vir> { - let decomp = RustTyDecomposition::from_ty(ty, ctx); - - if decomp.ty.specifics.is_param() { - let arg = decomp.args.args().first().expect("Param missing arg"); - return match arg.expect_ty().kind() { - ty::TyKind::Param(param) => *ty_map - .get(arg) - .expect(&format!("generic {param:?} to be mapped")), - ty::TyKind::Alias(ty::AliasTyKind::Projection, alias) => { - let trait_did = tcx.parent(alias.def_id); - let trait_ = deps.require_ref::(trait_did).unwrap(); - - let assoc_ty_fun = trait_ - .funs - .assoc_types - .get(&alias.def_id) - .expect("associated type to be in the mapping"); - - let gargs = GArgs::new(trait_did, alias.args); - let gargs = deps.require_dep::(gargs).unwrap(); - assoc_ty_fun(gargs.get_ty(), gargs.get_const()) - } - _ => unimplemented!("unsupported kind of generic parameter in type position"), - }; - } - - let ty_enc = deps.require_ref::(decomp.ty).unwrap(); - - let args = decomp.args.args(); - - let inner_ty_args = args - .iter() - .filter_map(|arg| arg.as_type()) - .map(|inner_ty| assemble_type(tcx, deps, ty_map, const_map, ctx, inner_ty)) - .collect::>(); - - let inner_const_args = args - .iter() - .filter_map(|arg| arg.as_const()) - .map(|const_| assemble_const(deps, ctx, const_map, const_).downcast_ty()) - .collect::>(); - - (ty_enc.ty_constructor)(&inner_ty_args, &inner_const_args) -} - -fn assemble_const<'vir>( - deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, - ctx: GParams<'vir>, - const_map: &FxHashMap, vir::ExprSnap<'vir>>, - const_: ty::Const<'vir>, -) -> vir::ExprSnap<'vir> { - match const_.kind() { - ty::ConstKind::Param(..) => const_map - .get(&const_.into()) - .copied() - .expect("The const generic should have been bound in the map"), - ty::ConstKind::Value(val) => { - let task = ConstEncTask::Ty { - const_: const_, - ty: val.ty, - context: ctx, - }; - deps.require_dep::(task).unwrap().upcast_ty() - } - _ => unimplemented!("other kinds of const parameters not supported yet"), - } -} - -/// Process the projection predicates in a topological order, such that when processing a -/// projection predicate, the projection term is already mapped to a VIR expression. This is needed -/// to handle cases like `T::Item: Trait` where we need to refer to `T::Item` when encoding the -/// trait bound check for `Trait`. -fn process_projection_predicates<'a>( - vcx: &'a vir::VirCtxt<'a>, - deps: &mut TaskEncoderDependencies<'a, TraitEnc>, - ty_map: &mut FxHashMap, vir::ExprTyVal<'a>>, - const_map: &mut FxHashMap, vir::ExprSnap<'a>>, - ctx: GParams<'a>, - projections: impl Iterator>, -) -> Option> { - let mut worklist: VecDeque<_> = projections.collect(); - let mut conjuncts = Vec::new(); - while let Some(proj) = worklist.pop_front() { - if is_alias_ready(&proj.projection_term, ty_map, const_map) { - conjuncts.extend(process_projection(vcx, deps, ty_map, const_map, ctx, proj)); - } else { - worklist.push_back(proj); - } - } - if conjuncts.is_empty() { - None - } else { - Some(vcx.mk_conj(&conjuncts)) - } -} - -fn process_projection<'a>( - vcx: &'a vir::VirCtxt<'a>, - deps: &mut TaskEncoderDependencies<'a, TraitEnc>, - ty_map: &mut FxHashMap, vir::ExprTyVal<'a>>, - const_map: &mut FxHashMap, vir::ExprSnap<'a>>, - ctx: GParams<'a>, - projection: ty::ProjectionPredicate<'a>, -) -> Option> { - let tcx = vcx.tcx(); - let proj_did = projection.def_id(); - let trait_did = projection.trait_def_id(tcx); - let trait_ = deps.require_ref::(trait_did).unwrap(); - - let proj_args = projection.projection_term.args; - - let proj_ty_args: Vec<_> = proj_args - .iter() - .filter_map(|arg| arg.as_type()) - .map(|ty| assemble_type(tcx, deps, ty_map, const_map, ctx, ty)) - .collect(); - let proj_const_args: Vec<_> = proj_args - .iter() - .filter_map(|arg| arg.as_const()) - .map(|const_| assemble_const(deps, ctx, const_map, const_).downcast_ty()) - .collect(); - - match projection.term.kind() { - ty::TermKind::Ty(tgt_ty) => { - let projection_fun = trait_ - .funs - .assoc_types - .get(&proj_did) - .expect("Projection did should be in the mapping"); - - let projection = projection_fun(&proj_ty_args, &proj_const_args); - - encode_type_check(vcx, deps, ty_map, const_map, ctx, projection, tgt_ty) - } - ty::TermKind::Const(const_) => { - let projection_fun = trait_ - .funs - .assoc_consts - .get(&proj_did) - .expect("Projection did should be in the mapping"); - let projection = projection_fun(&proj_ty_args, &proj_const_args); - - encode_const_check(vcx, deps, const_map, ctx, projection, const_) - } - } -} - -/// Check whether all generic parameters of the given alias term have alredy been mapped -fn is_alias_ready<'vir>( - term: &ty::AliasTerm<'vir>, - ty_map: &FxHashMap, vir::ExprTyVal<'vir>>, - const_map: &FxHashMap, vir::ExprSnap<'vir>>, -) -> bool { - for arg in term.args { - for arg in arg.walk() { - match arg.kind() { - ty::GenericArgKind::Type(ty) => { - if let ty::TyKind::Param(_) = ty.kind() { - if !ty_map.contains_key(&arg) { - return false; - } - } - } - ty::GenericArgKind::Const(_) => { - if !const_map.contains_key(&arg) { - return false; - } - } - _ => {} - } - } - } - true -} - -fn process_trait_predicates<'a>( - vcx: &'a vir::VirCtxt<'a>, - deps: &mut TaskEncoderDependencies<'a, TraitEnc>, - ty_map: &FxHashMap, vir::ExprTyVal<'a>>, - const_map: &FxHashMap, vir::ExprSnap<'a>>, - ctx: GParams<'a>, - trait_preds: impl Iterator>, -) -> vir::ExprGenBool<'a, (), !> { - let tcx = vcx.tcx(); - let mut conjuncts = Vec::new(); - for trait_pred in trait_preds { - let required_trait_impl_fun = deps - .require_ref::(trait_pred.def_id()) - .unwrap() - .impl_fun; - - let trait_args = trait_pred.trait_ref.args; - let ty_args: Vec<_> = trait_args - .iter() - .filter_map(|arg| arg.as_type()) - .map(|arg| assemble_type(tcx, deps, &ty_map, &const_map, ctx, arg)) - .collect(); - - let const_args: Vec<_> = trait_args - .iter() - .filter_map(|arg| arg.as_const()) - .map(|const_| assemble_const(deps, ctx, &const_map, const_).downcast_ty()) - .collect(); - - conjuncts.push(required_trait_impl_fun(&ty_args, &const_args)); - } - vcx.mk_conj(&conjuncts) -} - #[derive(Debug, Clone)] pub struct TraitFuns<'a> { pub assoc_types: FxHashMap>, pub assoc_consts: FxHashMap>, } -impl<'a> TraitFuns<'a> { - fn mk_domain_functions(&self, vcx: &'a vir::VirCtxt<'a>) -> Vec> { +impl<'vir> TraitFuns<'vir> { + fn mk_domain_functions(&self, vcx: &'vir vir::VirCtxt<'vir>) -> Vec> { self.assoc_types .values() .map(|fun| vcx.mk_domain_function(*fun, false, None)) @@ -588,12 +192,12 @@ impl<'a> TraitFuns<'a> { } /// Collect mappings for associated items of a trait to their corresponding VIR functions. -fn associated_items_funs<'a>( - vcx: &'a vir::VirCtxt<'a>, - deps: &mut TaskEncoderDependencies<'a, TraitEnc>, +fn associated_items_funs<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, trait_name: &str, - assoc_items: impl Iterator, -) -> TraitFuns<'a> { + assoc_items: impl Iterator, +) -> TraitFuns<'vir> { let tcx = vcx.tcx(); let mut assoc_types = FxHashMap::default(); diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index 8c2171e1d42..7fbe7c6bfdf 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -658,8 +658,8 @@ pub enum Sizedness<'tcx> { impl<'tcx> Sizedness<'tcx> { fn map(self, f: impl FnOnce(ty::Ty<'tcx>) -> ty::Ty<'tcx>) -> Self { match self { - Sizedness::Dependent(param) => Sizedness::Dependent(f(param)), - sizedness => sizedness, + Sizedness::Dependent(ty) => Sizedness::Dependent(f(ty)), + other => other, } } } From bb78ada569ff25cc182cd72950cec1703ca59ab1 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 26 Feb 2026 13:20:40 +0100 Subject: [PATCH 37/70] Seperate encoder for the sized trait --- prusti-encoder/src/encoders/mir_fn/mod.rs | 9 - .../src/encoders/ty/generics/mod.rs | 1 + .../src/encoders/ty/generics/sized_trait.rs | 164 +++++++++++++++++ .../src/encoders/ty/generics/traits.rs | 112 +++++++----- .../src/encoders/ty/lifted/ty_constructor.rs | 166 ++++-------------- prusti-encoder/src/lib.rs | 5 +- 6 files changed, 279 insertions(+), 178 deletions(-) create mode 100644 prusti-encoder/src/encoders/ty/generics/sized_trait.rs diff --git a/prusti-encoder/src/encoders/mir_fn/mod.rs b/prusti-encoder/src/encoders/mir_fn/mod.rs index 149d344dcd2..fb7586bed75 100644 --- a/prusti-encoder/src/encoders/mir_fn/mod.rs +++ b/prusti-encoder/src/encoders/mir_fn/mod.rs @@ -62,13 +62,4 @@ pub fn encode_all_in_crate<'tcx>(tcx: ty::TyCtxt<'tcx>) { } } } - - // This creates the impl encoding for all traits in the crate - // To iterate over all _visible_ impl blocks, - // use tcx.visible_traits and tcx.all_impls(trait_id) - for def_id in tcx.hir_crate_items(()).definitions() { - if let hir::def::DefKind::Impl { of_trait: true } = tcx.def_kind(def_id) { - TraitImplEnc::encode(def_id.to_def_id(), false).unwrap(); - } - } } diff --git a/prusti-encoder/src/encoders/ty/generics/mod.rs b/prusti-encoder/src/encoders/ty/generics/mod.rs index 626ec23318a..945fb25fc3c 100644 --- a/prusti-encoder/src/encoders/ty/generics/mod.rs +++ b/prusti-encoder/src/encoders/ty/generics/mod.rs @@ -3,6 +3,7 @@ mod params; mod casters; mod args_ty; mod args; +pub mod sized_trait; pub mod traits; pub mod trait_impls; diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs new file mode 100644 index 00000000000..b5a0f1245ad --- /dev/null +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -0,0 +1,164 @@ +use crate::{ + TaskEncoder, + encoders::ty::{ + Sizedness, + generics::{ + GArgs, GArgsTyEnc, GParams, GenericParamsEnc, + traits::{TraitEnc, trait_impl_fun_idn, trait_unknown_impl_fun_idn}, + }, + lifted::ty_constructor::{unknown_type_discriminator, unknown_type_id_accessor}, + }, +}; +use prusti_rustc_interface::middle::ty; + +pub struct SizedTraitEnc; + +const SIZED_TRAIT_NAME: &str = "Sized"; + +#[derive(Copy, Debug, Clone, Hash, Eq, PartialEq)] +pub struct SizedTraitEncTask<'vir> { + pub sizedness: Sizedness<'vir>, + pub discriminator: &'vir str, + pub ty_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::TyVal>], + pub const_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::CSnap>], + pub ty_ctx: GParams<'vir>, +} + +impl TaskEncoder for SizedTraitEnc { + task_encoder::encoder_cache!(SizedTraitEnc); + type TaskDescription<'vir> = SizedTraitEncTask<'vir>; + + type OutputFullLocal<'vir> = Option>; + + fn task_to_key<'vir>(task: &Self::TaskDescription<'vir>) -> Self::TaskKey<'vir> { + *task + } + + fn do_encode_full<'vir>( + task_key: &Self::TaskKey<'vir>, + deps: &mut task_encoder::TaskEncoderDependencies<'vir, Self>, + ) -> task_encoder::EncodeFullResult<'vir, Self> { + deps.emit_output_ref(*task_key, ())?; + let &Self::TaskKey { + sizedness, + discriminator, + ty_accessors, + const_accessors, + ty_ctx, + } = task_key; + vir::with_vcx(|vcx| { + let self_expr = vcx.mk_local_ex(sized_self_decl(vcx)); + + let is_this_type = vcx.mk_adt_discriminator_expr(self_expr, discriminator); + + let sized_impl_fun_idn = + trait_impl_fun_idn(vcx, SIZED_TRAIT_NAME, (&[vir::TYPE_TYVAL], &[])); + let check = match sizedness { + Sizedness::Sized => Some(is_this_type), + Sizedness::Unsized => None, + Sizedness::Dependent(ty) => match ty.kind() { + ty::TyKind::Param(param) => { + let accessor = ty_accessors[param.index as usize]; + let param_ty = accessor.call()(self_expr); + // We know that in reality `Sized` only has the `Self` type parameter + let inner_sized_check = sized_impl_fun_idn.call()(&[param_ty], &[]); + Some(vir::expr! { vcx; (is_this_type) && (inner_sized_check) }) + } + ty::TyKind::Alias(ty::AliasTyKind::Projection, alias_ty) => { + let alias_did = alias_ty.def_id; + let trait_def = alias_ty.trait_def_id(vcx.tcx()); + let trait_ = deps.require_ref::(trait_def)?; + let projection_fun = trait_.funs.assoc_types[&alias_did]; + + let ty_params = deps.require_dep::(ty_ctx)?; + let args = + deps.require_dep::(GArgs::new(ty_ctx, alias_ty.args))?; + + let projection = projection_fun(args.get_ty(), args.get_const()); + let inner_sized_check = sized_impl_fun_idn.call()(&[projection], &[]); + + let inner_expr = vir::expr! { vcx; + (is_this_type) && (inner_sized_check) + }; + + // Introduce let-bindings for the generics of the type + // NOTE: There won't be any name collisions as user defined ADTs cannot + // have a generic called `Self` + let with_consts_bound = ty_params.const_decls().iter().enumerate().rfold( + inner_expr, + |expr, (i, decl)| { + let accessor = const_accessors[i]; + vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) + }, + ); + let with_tys_bound = ty_params.ty_decls().iter().enumerate().rfold( + with_consts_bound, + |expr, (i, decl)| { + let accessor = ty_accessors[i]; + vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) + }, + ); + Some(with_tys_bound) + } + _ => panic!("Unsupported dependent sizedness for {ty:?}"), + }, + }; + + Ok((check, ())) + }) + } + + fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { + vir::with_vcx(|vcx| { + let mut checks: Vec<_> = Self::all_outputs_local_no_errors() + .into_iter() + .flatten() + .collect(); + + let sized_impl_fun_idn = + trait_impl_fun_idn(vcx, SIZED_TRAIT_NAME, (&[vir::TYPE_TYVAL], &[])); + let sized_impl_unknown_fun_idn = + trait_unknown_impl_fun_idn(vcx, SIZED_TRAIT_NAME, (&[vir::TYPE_TYVAL], &[])); + + let self_decl = sized_self_decl(vcx); + let self_expr = vcx.mk_local_ex(self_decl); + + let unknown_check = { + let is_unknown = + vcx.mk_adt_discriminator_expr(self_expr, unknown_type_discriminator()); + let unknown_id = unknown_type_id_accessor(vcx).call()(self_expr); + + let unknown_impls = sized_impl_unknown_fun_idn.call()(unknown_id, &[], &[]); + + vir::expr! {vcx; (is_unknown) && (unknown_impls) } + }; + + checks.push(unknown_check); + + let sized_impl_fun = vcx.mk_function( + sized_impl_fun_idn, + (&[self_decl], &[]), + &[], + &[], + None, + Some(vcx.mk_disj(&checks)), + ); + program.add_function(sized_impl_fun); + + let sized_impl_unknown_fun = vcx.mk_function( + sized_impl_unknown_fun_idn, + (vcx.mk_local_decl("id", vir::TYPE_INT), &[], &[]), + &[], + &[], + None, + None, + ); + + program.add_function(sized_impl_unknown_fun); + }); + } +} + +fn sized_self_decl<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::LocalDecl<'vir, vir::TyVal> { + vcx.mk_local_decl("Self$0", vir::TYPE_TYVAL) +} diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index cf33279ac61..d83e40f4a3c 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -1,11 +1,12 @@ use prusti_rustc_interface::{middle::ty, span::def_id::DefId}; use rustc_hash::FxHashMap; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder, TaskEncoderDependencies}; -use vir::{FunctionIdn, vir_format_identifier}; +use vir::{Arity, FunctionIdn, vir_format_identifier}; use crate::encoders::ty::{ RustTyDecomposition, generics::{GParams, GenericParamsEnc, trait_impls::TraitImplEnc}, + lifted::ty_constructor::{unknown_type_discriminator, unknown_type_id_accessor}, pure::TyPureEnc, }; @@ -33,6 +34,57 @@ pub struct TraitEncOutput<'vir> { impl_unknown_fun: vir::Function<'vir>, } +pub fn trait_impl_fun_idn<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + trait_name: &'a str, + args: ::Tys<'vir>, +) -> FunctionIdn<'vir, TraitArgs, vir::Bool> { + FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_impl"), + args, + vir::TYPE_BOOL, + ) +} + +pub fn trait_unknown_impl_fun_idn<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + trait_name: &'a str, + args: ::Tys<'vir>, +) -> FunctionIdn<'vir, (vir::Int, vir::ManyTyVal, vir::ManyCSnap), vir::Bool> { + // Omit the `Self` type as it is known to be the unknown type + let ty_args = &args.0[1..]; + let const_args = args.1; + FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_unknown_impl"), + (vir::TYPE_INT, ty_args, const_args), + vir::TYPE_BOOL, + ) +} + +pub fn trait_domain_idn<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + trait_name: &'a str, +) -> vir::ViperIdent<'vir> { + vir_format_identifier!(vcx, "t_{trait_name}") +} + +pub fn unknown_impl_decls<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + decls: ( + &'a [vir::LocalDeclTyVal<'vir>], + &'a [vir::LocalDeclCSnap<'vir>], + ), +) -> ( + vir::LocalDeclInt<'vir>, + &'a [vir::LocalDeclTyVal<'vir>], + &'a [vir::LocalDeclCSnap<'vir>], +) { + let (ty_decls, const_decls) = decls; + let ty_decls = &ty_decls[1..]; // Omit the `Self` type declaration + let unknown_id_decl = vcx.mk_local_decl("non_unit", vir::TYPE_INT); + (unknown_id_decl, ty_decls, const_decls) +} + impl TaskEncoder for TraitEnc { task_encoder::encoder_cache!(TraitEnc); @@ -69,23 +121,12 @@ impl TaskEncoder for TraitEnc { let trait_items = tcx.associated_items(task_key).in_definition_order(); let assoc_funs = associated_items_funs(vcx, deps, trait_name, trait_items); - let vpr_funs = assoc_funs.mk_domain_functions(vcx); + let funs = assoc_funs.mk_domain_functions(vcx); - let impl_fun_idn = FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_impl"), - (params.ty_args(), params.const_args()), - vir::TYPE_BOOL, - ); - - let impl_unknown_fun_idn: ImplUnknownFun = { - // Omit the `Self` type as it is known to be the "Unknown_type" - let unknown_args = ¶ms.ty_args()[1..]; - FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_unknown_impl"), - (unknown_args, params.const_args(), vir::TYPE_INT), - vir::TYPE_BOOL, - ) - }; + let args = (params.ty_args(), params.const_args()); + let decls = (params.ty_decls(), params.const_decls()); + let impl_fun_idn = trait_impl_fun_idn(vcx, trait_name, args); + let unkown_impl_fun_idn = trait_unknown_impl_fun_idn(vcx, trait_name, args); // Emit the impl function reference early, so that it can be used to encode caller // bounds without causing dependency cycles. @@ -111,19 +152,20 @@ impl TaskEncoder for TraitEnc { // Case for unknown types let self_expr = params.ty_exprs()[0]; - let is_unknown_type = vcx.mk_adt_discriminator_expr(self_expr, "Unknown_type"); + let is_unknown_type = + vcx.mk_adt_discriminator_expr(self_expr, unknown_type_discriminator()); - let unknown_id_destructor = - vcx.mk_adt_destructor("non_unit", vir::TYPE_TYVAL, vir::TYPE_INT); - let extracted_id = unknown_id_destructor.call()(self_expr); + let extracted_id = unknown_type_id_accessor(vcx).call()(self_expr); - let unknown_impls = impl_unknown_fun_idn( + let unknown_impls = unkown_impl_fun_idn( + extracted_id, ¶ms.ty_exprs()[1..], params.const_exprs(), - extracted_id, ); - let unknown_check = vcx.mk_conj(&[is_unknown_type, unknown_impls]); + let unknown_check = vir::expr! { vcx; + (is_unknown_type) && (unknown_impls) + }; trait_impl_checks.push(unknown_check); @@ -131,32 +173,22 @@ impl TaskEncoder for TraitEnc { }; let impl_unknown_fun = vcx.mk_function( - impl_unknown_fun_idn, - ( - ¶ms.ty_decls()[1..], - params.const_decls(), - vcx.mk_local_decl("non_unit", vir::TYPE_INT), - ), + unkown_impl_fun_idn, + unknown_impl_decls(vcx, decls), &[], &[], None, None, ); - let impl_fun = vcx.mk_function( - impl_fun_idn, - (params.ty_decls(), params.const_decls()), - &[], - &[], - None, - Some(impl_fun_body), - ); + let impl_fun = + vcx.mk_function(impl_fun_idn, decls, &[], &[], None, Some(impl_fun_body)); let trait_domain = vcx.mk_domain( - vir_format_identifier!(vcx, "t_{trait_name}"), + trait_domain_idn(vcx, trait_name), &[], &[], - vcx.alloc_slice(vpr_funs.as_slice()), + vcx.alloc_slice(funs.as_slice()), None, ); Ok(( diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index 2cb2eb42f3f..6cf488e8cc3 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -1,10 +1,12 @@ -use prusti_rustc_interface::middle::ty; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder}; use vir::{CallableIdn, CastType, FunctionIdn, HasType}; use crate::encoders::ty::{ - RustTy, Sizedness, - generics::{GArgs, GArgsTyEnc, GenericParamsEnc, traits::TraitEnc}, + RustTy, + generics::{ + GenericParamsEnc, + sized_trait::{SizedTraitEnc, SizedTraitEncTask}, + }, }; use super::r#typeof::{TypeOfEnc, TypeOfEncOutputRef}; @@ -52,12 +54,6 @@ impl<'vir> TyConstructorEncOutputRef<'vir> { impl<'vir> OutputRefAny for TyConstructorEncOutputRef<'vir> {} -#[derive(Debug, Clone)] -pub struct TyConstructorEncOutput<'vir> { - constructor: vir::AdtConstructor<'vir>, - sized_check: Option>, -} - /// Encodes the lifted representation of a Rust type constructor (e.g. Option, /// Vec, user-defined ADTs). pub struct TyConstructorEnc; @@ -68,7 +64,7 @@ impl TaskEncoder for TyConstructorEnc { type OutputRef<'vir> = TyConstructorEncOutputRef<'vir>; - type OutputFullLocal<'vir> = TyConstructorEncOutput<'vir>; + type OutputFullLocal<'vir> = vir::AdtConstructor<'vir>; type EncodingError = (); @@ -101,6 +97,8 @@ impl TaskEncoder for TyConstructorEnc { ) }) .collect::>(); + + let ty_accessor_functions = vcx.alloc_slice(&ty_accessor_functions); let const_accessor_functions = params .const_decls() .iter() @@ -112,6 +110,7 @@ impl TaskEncoder for TyConstructorEnc { ) }) .collect::>(); + let const_accessor_functions = vcx.alloc_slice(&const_accessor_functions); let typeof_data = deps.require_ref::(*task_key)?; deps.emit_output_ref( @@ -119,8 +118,8 @@ impl TaskEncoder for TyConstructorEnc { TyConstructorEncOutputRef { typeof_data, ty_constructor: type_function_ident, - ty_param_accessors: vcx.alloc_slice(&ty_accessor_functions), - const_param_accessors: vcx.alloc_slice(&const_accessor_functions), + ty_param_accessors: ty_accessor_functions, + const_param_accessors: const_accessor_functions, }, )?; @@ -135,137 +134,48 @@ impl TaskEncoder for TyConstructorEnc { .collect::>>(); let constructor = vcx.mk_adt_constructor(type_function_ident.name().to_str(), vcx.alloc_slice(&args)); - let sized_check = { - // Use a local expression named "Self" to build the function body - let self_decl = vcx.mk_local_decl("Self$0", vir::TYPE_TYVAL); - let self_expr = vcx.mk_local_ex(self_decl); - let is_this_type = - vcx.mk_adt_discriminator_expr(self_expr, type_function_ident.name().to_str()); - let sized_impl_fun_idn: FunctionIdn<'vir, vir::TyVal, vir::Bool> = FunctionIdn::new( - vir::vir_format_identifier!(vcx, "Sized_impl"), - vir::TYPE_TYVAL, - vir::TYPE_BOOL, - ); - match task_key.sizedness { - Sizedness::Sized => Some(is_this_type), - Sizedness::Unsized => None, - Sizedness::Dependent(ty) => match ty.kind() { - ty::TyKind::Param(param) => { - let accessor = ty_accessor_functions[param.index as usize]; - let param_ty = accessor.call()(self_expr); - Some( - vir::expr! { vcx; (is_this_type) && ([sized_impl_fun_idn](param_ty)) }, - ) - } - ty::TyKind::Alias(ty::AliasTyKind::Projection, alias_ty) => { - let alias_did = alias_ty.def_id; - let trait_def = alias_ty.trait_def_id(vcx.tcx()); - let trait_ = deps.require_ref::(trait_def)?; - let projection_fun = trait_.funs.assoc_types[&alias_did]; - let args = deps.require_dep::(GArgs::new( - task_key.params, - alias_ty.args, - ))?; - let inner_expr = vir::expr! { vcx; - (is_this_type) && ([sized_impl_fun_idn]( - [projection_fun]([..[args.get_ty()]], [..[args.get_const()]]) - )) - }; - let with_consts_bound = params.const_decls().iter().enumerate().rfold( - inner_expr, - |expr, (i, decl)| { - let accessor = const_accessor_functions[i]; - vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) - }, - ); - let with_tys_bound = params.ty_decls().iter().enumerate().rfold( - with_consts_bound, - |expr, (i, decl)| { - let accessor = ty_accessor_functions[i]; - vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) - }, - ); - Some(with_tys_bound) - } - _ => panic!("Unsupported dependent sizedness for {ty:?}"), - }, - } + let sizedness_task = SizedTraitEncTask { + sizedness: task_key.sizedness, + discriminator: type_function_ident.name().to_str(), + ty_accessors: ty_accessor_functions, + const_accessors: const_accessor_functions, + ty_ctx: task_key.params, }; - Ok(( - TyConstructorEncOutput { - constructor, - sized_check, - }, - (), - )) + deps.require_dep::(sizedness_task)?; + + Ok((constructor, ())) }) } fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { - let (mut constructors, sized_checks): (Vec<_>, Vec<_>) = - Self::all_outputs_local_no_errors() - .into_iter() - .map(|out| (out.constructor, out.sized_check)) - .unzip(); - let mut sized_checks = sized_checks.into_iter().flatten().collect::>(); + let mut constructors = Self::all_outputs_local_no_errors(); vir::with_vcx(|vcx| { - let args = vcx.alloc_array(&[vcx.mk_local_decl("non_unit", vir::TYPE_INT)]); - let unknown = vcx.mk_adt_constructor("Unknown_type", args); - constructors.push(unknown); + constructors.push(unkonwn_type_constructor(vcx)); let adt = vcx.mk_adt( vir::ViperIdent::new("Type"), &[], vcx.alloc_slice(&constructors), ); program.add_adt(adt); + }) + } +} - // Since we know all type constructors now, we can emit the `Sized` trait - let sized_impl_fun_idn: FunctionIdn<'vir, vir::TyVal, vir::Bool> = FunctionIdn::new( - vir::vir_format_identifier!(vcx, "Sized_impl"), - vir::TYPE_TYVAL, - vir::TYPE_BOOL, - ); - let sized_impl_unknown_fun_idn: FunctionIdn<'vir, vir::Int, vir::Bool> = - FunctionIdn::new( - vir::vir_format_identifier!(vcx, "Sized_unknown_impl"), - vir::TYPE_INT, - vir::TYPE_BOOL, - ); - - let self_decl = vcx.mk_local_decl("Self$0", vir::TYPE_TYVAL); - let unknown_type_check = { - let self_expr = vcx.mk_local_ex(self_decl); - let is_unknown_type = vcx.mk_adt_discriminator_expr(self_expr, "Unknown_type"); - - let unknown_id_destructor = - vcx.mk_adt_destructor("non_unit", vir::TYPE_TYVAL, vir::TYPE_INT); - let extracted_id = unknown_id_destructor.call()(self_expr); - - vir::expr! {vcx; (is_unknown_type) && ([sized_impl_unknown_fun_idn](extracted_id)) } - }; +const UNKNOWN_TYPE_NAME: &str = "Unknown_type"; +const UNKNOWN_TYPE_ID: &str = "id"; - sized_checks.push(unknown_type_check); - let sized_impl_fun = vcx.mk_function( - sized_impl_fun_idn, - (self_decl,), - &[], - &[], - None, - Some(vcx.mk_disj(&sized_checks)), - ); +pub fn unkonwn_type_constructor<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::AdtConstructor<'vir> { + let args = vcx.alloc_array(&[vcx.mk_local_decl(UNKNOWN_TYPE_ID, vir::TYPE_INT)]); + vcx.mk_adt_constructor(UNKNOWN_TYPE_NAME, args) +} - program.add_function(sized_impl_fun); +pub fn unknown_type_discriminator<'vir>() -> &'static str { + UNKNOWN_TYPE_NAME +} - let sized_impl_unknown_fun = vcx.mk_function( - sized_impl_unknown_fun_idn, - (vcx.mk_local_decl("non_unit", vir::TYPE_INT),), - &[], - &[], - None, - None, - ); - program.add_function(sized_impl_unknown_fun); - }) - } +pub fn unknown_type_id_accessor<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, +) -> vir::AdtDestructor<'vir, vir::TyVal, vir::Int> { + vcx.mk_adt_destructor(UNKNOWN_TYPE_ID, vir::TYPE_TYVAL, vir::TYPE_INT) } diff --git a/prusti-encoder/src/lib.rs b/prusti-encoder/src/lib.rs index ab09b988bbc..07db80f7823 100644 --- a/prusti-encoder/src/lib.rs +++ b/prusti-encoder/src/lib.rs @@ -22,7 +22,9 @@ use crate::encoders::{ Impure, Pure, custom::PairUseEnc, ty::{ - generics::{GArgsCastEnc, trait_impls::TraitImplEnc, traits::TraitEnc}, + generics::{ + GArgsCastEnc, sized_trait::SizedTraitEnc, trait_impls::TraitImplEnc, traits::TraitEnc, + }, interpretation::bitvec::BitVecEnc, lifted::{TyConstructorEnc, TypeOfEnc}, }, @@ -106,6 +108,7 @@ pub fn test_entrypoint<'tcx>( PairUseEnc::emit_outputs(&mut program); TraitEnc::emit_outputs(&mut program); TraitImplEnc::emit_outputs(&mut program); + SizedTraitEnc::emit_outputs(&mut program); if std::env::var("LOCAL_TESTING").is_ok() { std::fs::write("local-testing/simple.vpr", program.code()).unwrap(); From ee87b3e9c70e722a658a881fdffa5092b5de5c4c Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 26 Feb 2026 13:26:32 +0100 Subject: [PATCH 38/70] Remove type aliases --- .../src/encoders/ty/generics/traits.rs | 119 +++++++++--------- 1 file changed, 57 insertions(+), 62 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index d83e40f4a3c..97e251ca916 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -12,18 +12,11 @@ use crate::encoders::ty::{ pub struct TraitEnc; -type TraitArgs = (vir::ManyTyVal, vir::ManyCSnap); -type AssocTypeFun<'vir> = FunctionIdn<'vir, TraitArgs, vir::TyVal>; -type AssocConstFun<'vir> = FunctionIdn<'vir, TraitArgs, vir::Snap>; -type ImplFun<'vir> = FunctionIdn<'vir, TraitArgs, vir::Bool>; -type ImplUnknownFun<'vir> = - FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap, vir::Int), vir::Bool>; - #[derive(Debug, Clone)] pub struct TraitEncOutputRef<'vir> { pub trait_name: &'vir str, pub funs: TraitFuns<'vir>, - pub impl_fun: ImplFun<'vir>, + pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, } impl OutputRefAny for TraitEncOutputRef<'_> {} @@ -34,57 +27,6 @@ pub struct TraitEncOutput<'vir> { impl_unknown_fun: vir::Function<'vir>, } -pub fn trait_impl_fun_idn<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - trait_name: &'a str, - args: ::Tys<'vir>, -) -> FunctionIdn<'vir, TraitArgs, vir::Bool> { - FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_impl"), - args, - vir::TYPE_BOOL, - ) -} - -pub fn trait_unknown_impl_fun_idn<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - trait_name: &'a str, - args: ::Tys<'vir>, -) -> FunctionIdn<'vir, (vir::Int, vir::ManyTyVal, vir::ManyCSnap), vir::Bool> { - // Omit the `Self` type as it is known to be the unknown type - let ty_args = &args.0[1..]; - let const_args = args.1; - FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_unknown_impl"), - (vir::TYPE_INT, ty_args, const_args), - vir::TYPE_BOOL, - ) -} - -pub fn trait_domain_idn<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - trait_name: &'a str, -) -> vir::ViperIdent<'vir> { - vir_format_identifier!(vcx, "t_{trait_name}") -} - -pub fn unknown_impl_decls<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - decls: ( - &'a [vir::LocalDeclTyVal<'vir>], - &'a [vir::LocalDeclCSnap<'vir>], - ), -) -> ( - vir::LocalDeclInt<'vir>, - &'a [vir::LocalDeclTyVal<'vir>], - &'a [vir::LocalDeclCSnap<'vir>], -) { - let (ty_decls, const_decls) = decls; - let ty_decls = &ty_decls[1..]; // Omit the `Self` type declaration - let unknown_id_decl = vcx.mk_local_decl("non_unit", vir::TYPE_INT); - (unknown_id_decl, ty_decls, const_decls) -} - impl TaskEncoder for TraitEnc { task_encoder::encoder_cache!(TraitEnc); @@ -204,9 +146,11 @@ impl TaskEncoder for TraitEnc { } #[derive(Debug, Clone)] -pub struct TraitFuns<'a> { - pub assoc_types: FxHashMap>, - pub assoc_consts: FxHashMap>, +pub struct TraitFuns<'vir> { + pub assoc_types: + FxHashMap>, + pub assoc_consts: + FxHashMap>, } impl<'vir> TraitFuns<'vir> { @@ -270,3 +214,54 @@ fn associated_items_funs<'vir>( assoc_consts, } } + +pub fn trait_impl_fun_idn<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + trait_name: &'a str, + args: <(vir::ManyTyVal, vir::ManyCSnap) as Arity>::Tys<'vir>, +) -> FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool> { + FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_impl"), + args, + vir::TYPE_BOOL, + ) +} + +pub fn trait_unknown_impl_fun_idn<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + trait_name: &'a str, + args: <(vir::ManyTyVal, vir::ManyCSnap) as Arity>::Tys<'vir>, +) -> FunctionIdn<'vir, (vir::Int, vir::ManyTyVal, vir::ManyCSnap), vir::Bool> { + // Omit the `Self` type as it is known to be the unknown type + let ty_args = &args.0[1..]; + let const_args = args.1; + FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_unknown_impl"), + (vir::TYPE_INT, ty_args, const_args), + vir::TYPE_BOOL, + ) +} + +pub fn trait_domain_idn<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + trait_name: &'a str, +) -> vir::ViperIdent<'vir> { + vir_format_identifier!(vcx, "t_{trait_name}") +} + +pub fn unknown_impl_decls<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + decls: ( + &'a [vir::LocalDeclTyVal<'vir>], + &'a [vir::LocalDeclCSnap<'vir>], + ), +) -> ( + vir::LocalDeclInt<'vir>, + &'a [vir::LocalDeclTyVal<'vir>], + &'a [vir::LocalDeclCSnap<'vir>], +) { + let (ty_decls, const_decls) = decls; + let ty_decls = &ty_decls[1..]; // Omit the `Self` type declaration + let unknown_id_decl = vcx.mk_local_decl("non_unit", vir::TYPE_INT); + (unknown_id_decl, ty_decls, const_decls) +} From 1826fbc7cffdb4837533bde3f633dc38240c8220 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 26 Feb 2026 14:29:25 +0100 Subject: [PATCH 39/70] Skip emitting local output for `Sized` trait --- .../src/encoders/ty/generics/traits.rs | 65 +++++++++---------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 97e251ca916..6ae69ad460a 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -24,7 +24,6 @@ impl OutputRefAny for TraitEncOutputRef<'_> {} pub struct TraitEncOutput<'vir> { trait_domain: vir::Domain<'vir>, impl_fun: vir::Function<'vir>, - impl_unknown_fun: vir::Function<'vir>, } impl TaskEncoder for TraitEnc { @@ -37,17 +36,15 @@ impl TaskEncoder for TraitEnc { type TaskDescription<'vir> = DefId; type OutputRef<'vir> = TraitEncOutputRef<'vir>; - type OutputFullLocal<'vir> = TraitEncOutput<'vir>; + type OutputFullLocal<'vir> = Option>; fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { - for trait_enc in TraitEnc::all_outputs_local_no_errors() { - // Skip `Sized`, as we need a special encoding for its body. Encoded by `TyConstructorEnc` - if trait_enc.trait_domain.name == "t_Sized" { - continue; - } + for trait_enc in TraitEnc::all_outputs_local_no_errors() + .into_iter() + .flatten() + { program.add_domain(trait_enc.trait_domain); program.add_function(trait_enc.impl_fun); - program.add_function(trait_enc.impl_unknown_fun); } } @@ -63,7 +60,7 @@ impl TaskEncoder for TraitEnc { let trait_items = tcx.associated_items(task_key).in_definition_order(); let assoc_funs = associated_items_funs(vcx, deps, trait_name, trait_items); - let funs = assoc_funs.mk_domain_functions(vcx); + let mut funs = assoc_funs.mk_domain_functions(vcx); let args = (params.ty_args(), params.const_args()); let decls = (params.ty_decls(), params.const_decls()); @@ -81,6 +78,11 @@ impl TaskEncoder for TraitEnc { }, )?; + // When encoding `Sized`, emitting of the impl function is handled by `SizedTraitEnc` + if tcx.lang_items().sized_trait() == Some(*task_key) { + return Ok((None, ())); + } + let impl_fun_body = { let mut trait_impl_checks: Vec<_> = tcx .all_impls(*task_key) @@ -92,40 +94,36 @@ impl TaskEncoder for TraitEnc { .collect(); // Case for unknown types - let self_expr = params.ty_exprs()[0]; + { + let self_expr = params.ty_exprs()[0]; - let is_unknown_type = - vcx.mk_adt_discriminator_expr(self_expr, unknown_type_discriminator()); + let is_unknown_type = + vcx.mk_adt_discriminator_expr(self_expr, unknown_type_discriminator()); - let extracted_id = unknown_type_id_accessor(vcx).call()(self_expr); + let extracted_id = unknown_type_id_accessor(vcx).call()(self_expr); - let unknown_impls = unkown_impl_fun_idn( - extracted_id, - ¶ms.ty_exprs()[1..], - params.const_exprs(), - ); + let unknown_impls = unkown_impl_fun_idn( + extracted_id, + ¶ms.ty_exprs()[1..], + params.const_exprs(), + ); - let unknown_check = vir::expr! { vcx; - (is_unknown_type) && (unknown_impls) - }; + let unknown_check = vir::expr! { vcx; + (is_unknown_type) && (unknown_impls) + }; - trait_impl_checks.push(unknown_check); + trait_impl_checks.push(unknown_check); + } vcx.mk_disj(&trait_impl_checks) }; - let impl_unknown_fun = vcx.mk_function( - unkown_impl_fun_idn, - unknown_impl_decls(vcx, decls), - &[], - &[], - None, - None, - ); - let impl_fun = vcx.mk_function(impl_fun_idn, decls, &[], &[], None, Some(impl_fun_body)); + let impl_unknown_fun = vcx.mk_domain_function(unkown_impl_fun_idn, false, None); + funs.push(impl_unknown_fun); + let trait_domain = vcx.mk_domain( trait_domain_idn(vcx, trait_name), &[], @@ -134,11 +132,10 @@ impl TaskEncoder for TraitEnc { None, ); Ok(( - TraitEncOutput { + Some(TraitEncOutput { trait_domain, impl_fun, - impl_unknown_fun, - }, + }), (), )) }) From a478d4085ac25f68be0d2f079a55ab89fc553348 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 26 Feb 2026 14:57:35 +0100 Subject: [PATCH 40/70] Clippy --- prusti-encoder/src/encoders/mir_fn/mod.rs | 2 +- .../src/encoders/ty/generics/trait_impls.rs | 22 +++++++++---------- .../src/encoders/ty/generics/traits.rs | 17 -------------- .../src/encoders/ty/lifted/ty_constructor.rs | 2 +- 4 files changed, 13 insertions(+), 30 deletions(-) diff --git a/prusti-encoder/src/encoders/mir_fn/mod.rs b/prusti-encoder/src/encoders/mir_fn/mod.rs index fb7586bed75..42646378acb 100644 --- a/prusti-encoder/src/encoders/mir_fn/mod.rs +++ b/prusti-encoder/src/encoders/mir_fn/mod.rs @@ -6,7 +6,7 @@ pub use function::*; pub use method::*; pub use signature::*; -use crate::encoders::ty::generics::{GArgs, GParams, trait_impls::TraitImplEnc}; +use crate::encoders::ty::generics::{GArgs, GParams}; use prusti_interface::specs::specifications::SpecQuery; use prusti_rustc_interface::{hir, middle::ty, span::def_id::DefId}; diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 4feb763f2ca..544a46aca4d 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -99,8 +99,8 @@ impl TaskEncoder for TraitImplEnc { let item_const_args = item_params.const_exprs(); // Combine substituted trait ty decls with the decls of the associated type - let ty_decls = [&trait_ty_decls, &item_ty_decls[ty_cnt..]].concat(); - let const_decls = [&trait_const_decls, &item_const_decls[const_cnt..]].concat(); + let ty_decls = [trait_ty_decls, &item_ty_decls[ty_cnt..]].concat(); + let const_decls = [trait_const_decls, &item_const_decls[const_cnt..]].concat(); // Combine substituted trait params with the params of the associated type let ty_args = &[trait_ty_args, &item_ty_args[ty_cnt..]].concat(); @@ -341,9 +341,9 @@ fn assemble_type<'vir>( if decomp.ty.specifics.is_param() { let arg = decomp.args.args().first().expect("Param missing arg"); return match arg.expect_ty().kind() { - ty::TyKind::Param(param) => *ty_map + ty::TyKind::Param(param) => ty_map .get(arg) - .expect(&format!("generic {param:?} to be mapped")), + .unwrap_or_else(|| panic!("generic {param:?} to be mapped")), ty::TyKind::Alias(ty::AliasTyKind::Projection, alias) => { let trait_did = tcx.parent(alias.def_id); let trait_ = deps.require_ref::(trait_did).unwrap(); @@ -394,7 +394,7 @@ fn assemble_const<'vir>( .expect("The const generic should have been bound in the map"), ty::ConstKind::Value(val) => { let task = ConstEncTask::Ty { - const_: const_, + const_, ty: val.ty, context: ctx, }; @@ -493,10 +493,10 @@ fn is_alias_ready<'vir>( for arg in arg.walk() { match arg.kind() { ty::GenericArgKind::Type(ty) => { - if let ty::TyKind::Param(_) = ty.kind() { - if !ty_map.contains_key(&arg) { - return false; - } + if let ty::TyKind::Param(_) = ty.kind() + && !ty_map.contains_key(&arg) + { + return false; } } ty::GenericArgKind::Const(_) => { @@ -531,13 +531,13 @@ fn process_trait_predicates<'vir>( let ty_args: Vec<_> = trait_args .iter() .filter_map(|arg| arg.as_type()) - .map(|arg| assemble_type(tcx, deps, &ty_map, &const_map, ctx, arg)) + .map(|arg| assemble_type(tcx, deps, ty_map, const_map, ctx, arg)) .collect(); let const_args: Vec<_> = trait_args .iter() .filter_map(|arg| arg.as_const()) - .map(|const_| assemble_const(deps, ctx, &const_map, const_).downcast_ty()) + .map(|const_| assemble_const(deps, ctx, const_map, const_).downcast_ty()) .collect(); conjuncts.push(required_trait_impl_fun(&ty_args, &const_args)); diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 6ae69ad460a..d64ad297f67 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -245,20 +245,3 @@ pub fn trait_domain_idn<'vir, 'a>( ) -> vir::ViperIdent<'vir> { vir_format_identifier!(vcx, "t_{trait_name}") } - -pub fn unknown_impl_decls<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - decls: ( - &'a [vir::LocalDeclTyVal<'vir>], - &'a [vir::LocalDeclCSnap<'vir>], - ), -) -> ( - vir::LocalDeclInt<'vir>, - &'a [vir::LocalDeclTyVal<'vir>], - &'a [vir::LocalDeclCSnap<'vir>], -) { - let (ty_decls, const_decls) = decls; - let ty_decls = &ty_decls[1..]; // Omit the `Self` type declaration - let unknown_id_decl = vcx.mk_local_decl("non_unit", vir::TYPE_INT); - (unknown_id_decl, ty_decls, const_decls) -} diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index 6cf488e8cc3..738d307f821 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -170,7 +170,7 @@ pub fn unkonwn_type_constructor<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::Adt vcx.mk_adt_constructor(UNKNOWN_TYPE_NAME, args) } -pub fn unknown_type_discriminator<'vir>() -> &'static str { +pub fn unknown_type_discriminator() -> &'static str { UNKNOWN_TYPE_NAME } From 9a6a3ed51d5f70941afa30a2db20963400699eed Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 26 Feb 2026 16:27:56 +0100 Subject: [PATCH 41/70] Revert some unnecessary changes --- .../src/encoders/ty/generics/trait_impls.rs | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 544a46aca4d..c9f471f66e5 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -51,34 +51,32 @@ impl TaskEncoder for TraitImplEnc { vir::with_vcx(|vcx| { let tcx = vcx.tcx(); - let impl_idx = { - let all_impls = tcx.trait_impls_in_crate(task_key.krate); - all_impls.iter().position(|did| did == task_key).unwrap() - }; + let all_impls = tcx.trait_impls_in_crate(task_key.krate); + let idx = all_impls.iter().position(|did| did == task_key).unwrap(); - let impl_ty = { - let implementing_ty = tcx.type_of(task_key).instantiate_identity(); - let implementing_ty = RustTyDecomposition::from_ty(implementing_ty, *task_key); - implementing_ty.ty.name() - }; + let ctx = GParams::from(*task_key); + let params = deps.require_dep::(ctx)?; let trait_ref = tcx.impl_trait_ref(task_key).unwrap().instantiate_identity(); let trait_did = trait_ref.def_id; let trait_data = deps.require_ref::(trait_did)?; let trait_name = trait_data.trait_name; - let ctx = GParams::from(*task_key); - let params = deps.require_dep::(ctx)?; + let trait_args = deps.require_dep::(GArgs::new(ctx, trait_ref.args))?; + let mut axs = Vec::new(); + + let implementing_ty = tcx.type_of(task_key).instantiate_identity(); + let implementing_ty = RustTyDecomposition::from_ty(implementing_ty, *task_key); + let implementing_ty = implementing_ty.ty.name(); + let trait_ty_decls = params.ty_decls(); let trait_const_decls = params.const_decls(); let ty_cnt = params.ty_count(); let const_cnt = params.const_count(); - let trait_args = deps.require_dep::(GArgs::new(ctx, trait_ref.args))?; let trait_ty_args = trait_args.get_ty(); let trait_const_args = trait_args.get_const(); - let mut axioms = Vec::new(); for impl_item in tcx.associated_items(*task_key).in_definition_order() { let trait_item_did = impl_item.trait_item_def_id.unwrap(); let item_did = impl_item.def_id; @@ -98,11 +96,11 @@ impl TaskEncoder for TraitImplEnc { let item_ty_args = item_params.ty_exprs(); let item_const_args = item_params.const_exprs(); - // Combine substituted trait ty decls with the decls of the associated type + // Combine substituted trait ty and const decls with the decls of the associated item let ty_decls = [trait_ty_decls, &item_ty_decls[ty_cnt..]].concat(); let const_decls = [trait_const_decls, &item_const_decls[const_cnt..]].concat(); - // Combine substituted trait params with the params of the associated type + // Combine substituted trait params with the params of the associated item let ty_args = &[trait_ty_args, &item_ty_args[ty_cnt..]].concat(); let const_args = &[trait_const_args, &item_const_args[const_cnt..]].concat(); @@ -118,8 +116,8 @@ impl TaskEncoder for TraitImplEnc { item_ctx, ), ); - axioms.push(vcx.mk_domain_axiom( - vir_format_identifier!(vcx, "{trait_name}_impl_{impl_ty}_{impl_idx}_assoc_type_{item_name}"), + axs.push(vcx.mk_domain_axiom( + vir_format_identifier!(vcx, "{trait_name}_impl_{implementing_ty}_{idx}_assoc_type_{item_name}"), vir::expr! {forall ..[ty_decls], ..[const_decls] :: {[assoc_type(ty_args, const_args)]} ([assoc_type(ty_args, const_args)]) == (assoc_type_expr)}, )); } @@ -130,9 +128,9 @@ impl TaskEncoder for TraitImplEnc { } let domain = vcx.mk_domain( - vir_format_identifier!(vcx, "t_{impl_idx}_{}_{impl_ty}", trait_data.trait_name,), + vir_format_identifier!(vcx, "t_{idx}_{trait_name}_{implementing_ty}",), &[], - vcx.alloc_slice(&axioms), + vcx.alloc_slice(&axs), &[], None, ); From 1cfc892858cee02845efbe05ac33f264a0f82f5a Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 26 Feb 2026 16:43:17 +0100 Subject: [PATCH 42/70] Match parts of trait_impls with #127 --- .../src/encoders/ty/generics/params.rs | 8 -- .../src/encoders/ty/generics/trait_impls.rs | 109 +++++++++--------- 2 files changed, 57 insertions(+), 60 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/params.rs b/prusti-encoder/src/encoders/ty/generics/params.rs index 49aabf5cc19..18256984950 100644 --- a/prusti-encoder/src/encoders/ty/generics/params.rs +++ b/prusti-encoder/src/encoders/ty/generics/params.rs @@ -202,14 +202,6 @@ pub struct GenericParams<'vir> { } impl<'vir> GenericParams<'vir> { - pub fn ty_count(&self) -> usize { - self.ty_decls.len() - } - - pub fn const_count(&self) -> usize { - self.const_decls.len() - } - pub fn ty_args(&self) -> &'vir [vir::TypeTyVal<'vir>] { self.ty_args } diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index c9f471f66e5..16322231137 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -1,10 +1,7 @@ use rustc_hash::FxHashMap; -use std::collections::VecDeque; +use std::{collections::VecDeque, iter}; -use prusti_rustc_interface::{ - middle::{ty, ty::AssocKind}, - span::def_id::DefId, -}; +use prusti_rustc_interface::{middle::ty, span::def_id::DefId}; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; use vir::{CallableIdn, CastType, Domain, vir_format_identifier}; @@ -62,25 +59,20 @@ impl TaskEncoder for TraitImplEnc { let trait_data = deps.require_ref::(trait_did)?; let trait_name = trait_data.trait_name; - let trait_args = deps.require_dep::(GArgs::new(ctx, trait_ref.args))?; - let mut axs = Vec::new(); + let args = deps.require_dep::(GArgs::new(ctx, trait_ref.args))?; + let mut axioms = Vec::new(); let implementing_ty = tcx.type_of(task_key).instantiate_identity(); let implementing_ty = RustTyDecomposition::from_ty(implementing_ty, *task_key); let implementing_ty = implementing_ty.ty.name(); - let trait_ty_decls = params.ty_decls(); - let trait_const_decls = params.const_decls(); - let ty_cnt = params.ty_count(); - let const_cnt = params.const_count(); - - let trait_ty_args = trait_args.get_ty(); - let trait_const_args = trait_args.get_const(); + let trait_ty_decls = params.ty_decls().to_vec(); + let trait_const_decls = params.const_decls().to_vec(); - for impl_item in tcx.associated_items(*task_key).in_definition_order() { - let trait_item_did = impl_item.trait_item_def_id.unwrap(); - let item_did = impl_item.def_id; - let item_name = tcx.item_name(item_did); + for impl_item in tcx.associated_items(task_key).in_definition_order() { + let trait_item_def_id = impl_item.trait_item_def_id.unwrap(); + let impl_item_def_id = impl_item.def_id; + let item_name = tcx.item_name(impl_item_def_id); // construct arguments for assoc_item function // parameters of the trait are substituted @@ -88,56 +80,69 @@ impl TaskEncoder for TraitImplEnc { // parameters of the associated type are kept // parameters of assoc item include already substituted arguments - let item_ctx = GParams::from(item_did); - let item_params = deps.require_dep::(item_ctx).unwrap(); - - let item_ty_decls = item_params.ty_decls(); - let item_const_decls = item_params.const_decls(); - let item_ty_args = item_params.ty_exprs(); - let item_const_args = item_params.const_exprs(); - - // Combine substituted trait ty and const decls with the decls of the associated item - let ty_decls = [trait_ty_decls, &item_ty_decls[ty_cnt..]].concat(); - let const_decls = [trait_const_decls, &item_const_decls[const_cnt..]].concat(); - - // Combine substituted trait params with the params of the associated item - let ty_args = &[trait_ty_args, &item_ty_args[ty_cnt..]].concat(); - let const_args = &[trait_const_args, &item_const_args[const_cnt..]].concat(); + let impl_item_params = GParams::from(impl_item_def_id); + let assoc_params = deps + .require_dep::(impl_item_params) + .unwrap(); + + let assoc_ty_decls = assoc_params.ty_decls(); + let assoc_const_decls = assoc_params.const_decls(); + + // Combine substituted trait ty decls with the decls of the associated type + let mut trait_ty_decls = trait_ty_decls.clone(); + trait_ty_decls.extend_from_slice(&assoc_ty_decls[params.ty_exprs().len()..]); + let mut trait_const_decls = trait_const_decls.clone(); + trait_const_decls + .extend_from_slice(&assoc_const_decls[params.const_exprs().len()..]); + + // Combine substituted trait params with the params of the associated type + let trait_tys = vcx.alloc_slice( + &iter::empty() + .chain(args.get_ty().to_owned()) + .chain(assoc_params.ty_exprs()[params.ty_exprs().len()..].to_owned()) + .collect::>(), + ); + let trait_consts = vcx.alloc_slice( + &iter::empty() + .chain(args.get_const().to_owned()) + .chain(assoc_params.const_exprs()[params.const_exprs().len()..].to_owned()) + .collect::>(), + ); match impl_item.kind { - AssocKind::Type { .. } => { - let assoc_type = trait_data.funs.assoc_types.get(&trait_item_did).unwrap(); + ty::AssocKind::Type { .. } => { + let assoc_type = + trait_data.funs.assoc_types.get(&trait_item_def_id).unwrap(); // the type we want to resolve the type alias to - let assoc_type_expr = item_params.ty_expr( + let assoc_type_expr = assoc_params.ty_expr( deps, RustTyDecomposition::from_ty( - tcx.type_of(item_did).instantiate_identity(), - item_ctx, + tcx.type_of(impl_item_def_id).instantiate_identity(), + impl_item_params, ), ); - axs.push(vcx.mk_domain_axiom( + axioms.push(vcx.mk_domain_axiom( vir_format_identifier!(vcx, "{trait_name}_impl_{implementing_ty}_{idx}_assoc_type_{item_name}"), - vir::expr! {forall ..[ty_decls], ..[const_decls] :: {[assoc_type(ty_args, const_args)]} ([assoc_type(ty_args, const_args)]) == (assoc_type_expr)}, + vir::expr! {forall ..[trait_ty_decls], ..[trait_const_decls] :: {[assoc_type(trait_tys, trait_consts)]} ([assoc_type(trait_tys, trait_consts)]) == (assoc_type_expr)}, )); } - _ => { - // unimplemented - } + _ => { /* unimplementd */ } } } - let domain = vcx.mk_domain( - vir_format_identifier!(vcx, "t_{idx}_{trait_name}_{implementing_ty}",), - &[], - vcx.alloc_slice(&axs), - &[], - None, - ); - let impl_condition = impl_block_condition(vcx, deps, *task_key); - Ok((domain, TraitImplEncOutput { impl_condition })) + Ok(( + vcx.mk_domain( + vir_format_identifier!(vcx, "trait_{trait_name}_impl_{implementing_ty}_{idx}"), + &[], + vcx.alloc_slice(&axioms), + &[], + None, + ), + TraitImplEncOutput { impl_condition }, + )) }) } } From 4a133a8b2f6b0159c17b709c1649ef25efcbaba8 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 26 Feb 2026 16:49:43 +0100 Subject: [PATCH 43/70] Remove unnecessary rename --- prusti-encoder/src/encoders/ty/generics/traits.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index d64ad297f67..7d73d26a49f 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -60,7 +60,7 @@ impl TaskEncoder for TraitEnc { let trait_items = tcx.associated_items(task_key).in_definition_order(); let assoc_funs = associated_items_funs(vcx, deps, trait_name, trait_items); - let mut funs = assoc_funs.mk_domain_functions(vcx); + let mut funcs = assoc_funs.mk_domain_functions(vcx); let args = (params.ty_args(), params.const_args()); let decls = (params.ty_decls(), params.const_decls()); @@ -122,13 +122,13 @@ impl TaskEncoder for TraitEnc { vcx.mk_function(impl_fun_idn, decls, &[], &[], None, Some(impl_fun_body)); let impl_unknown_fun = vcx.mk_domain_function(unkown_impl_fun_idn, false, None); - funs.push(impl_unknown_fun); + funcs.push(impl_unknown_fun); let trait_domain = vcx.mk_domain( trait_domain_idn(vcx, trait_name), &[], &[], - vcx.alloc_slice(funs.as_slice()), + vcx.alloc_slice(funcs.as_slice()), None, ); Ok(( From 7ebc976a5a5e8ac5843bd9148edd16280fabd8b4 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 26 Feb 2026 17:28:02 +0100 Subject: [PATCH 44/70] Omit dependent sizedness check if it is known to be sized --- .../src/encoders/ty/generics/sized_trait.rs | 137 ++++++++++++------ 1 file changed, 91 insertions(+), 46 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index b5a0f1245ad..fee8264ac26 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -53,55 +53,26 @@ impl TaskEncoder for SizedTraitEnc { let sized_impl_fun_idn = trait_impl_fun_idn(vcx, SIZED_TRAIT_NAME, (&[vir::TYPE_TYVAL], &[])); + let check = match sizedness { Sizedness::Sized => Some(is_this_type), Sizedness::Unsized => None, - Sizedness::Dependent(ty) => match ty.kind() { - ty::TyKind::Param(param) => { - let accessor = ty_accessors[param.index as usize]; - let param_ty = accessor.call()(self_expr); - // We know that in reality `Sized` only has the `Self` type parameter - let inner_sized_check = sized_impl_fun_idn.call()(&[param_ty], &[]); - Some(vir::expr! { vcx; (is_this_type) && (inner_sized_check) }) - } - ty::TyKind::Alias(ty::AliasTyKind::Projection, alias_ty) => { - let alias_did = alias_ty.def_id; - let trait_def = alias_ty.trait_def_id(vcx.tcx()); - let trait_ = deps.require_ref::(trait_def)?; - let projection_fun = trait_.funs.assoc_types[&alias_did]; - - let ty_params = deps.require_dep::(ty_ctx)?; - let args = - deps.require_dep::(GArgs::new(ty_ctx, alias_ty.args))?; - - let projection = projection_fun(args.get_ty(), args.get_const()); - let inner_sized_check = sized_impl_fun_idn.call()(&[projection], &[]); - - let inner_expr = vir::expr! { vcx; - (is_this_type) && (inner_sized_check) - }; - - // Introduce let-bindings for the generics of the type - // NOTE: There won't be any name collisions as user defined ADTs cannot - // have a generic called `Self` - let with_consts_bound = ty_params.const_decls().iter().enumerate().rfold( - inner_expr, - |expr, (i, decl)| { - let accessor = const_accessors[i]; - vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) - }, - ); - let with_tys_bound = ty_params.ty_decls().iter().enumerate().rfold( - with_consts_bound, - |expr, (i, decl)| { - let accessor = ty_accessors[i]; - vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) - }, - ); - Some(with_tys_bound) - } - _ => panic!("Unsupported dependent sizedness for {ty:?}"), - }, + Sizedness::Dependent(ty) => Some( + sizedness_for_dependent( + vcx, + deps, + sized_impl_fun_idn, + ty_accessors, + const_accessors, + ty_ctx, + self_expr, + ty, + ) + .map_or( + is_this_type, + |extra_check| vir::expr! {vcx; (is_this_type) && (extra_check) }, + ), + ), }; Ok((check, ())) @@ -162,3 +133,77 @@ impl TaskEncoder for SizedTraitEnc { fn sized_self_decl<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::LocalDecl<'vir, vir::TyVal> { vcx.mk_local_decl("Self$0", vir::TYPE_TYVAL) } + +/// Check if we need an extra sizedness check as a result of the sizedness dependency +fn sizedness_for_dependent<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut task_encoder::TaskEncoderDependencies<'vir, SizedTraitEnc>, + sized_impl_fun: vir::FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, + ty_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::TyVal>], + const_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::CSnap>], + ty_ctx: GParams<'vir>, + self_expr: vir::Expr<'vir, vir::TyVal>, + ty: ty::Ty<'vir>, +) -> Option> { + match ty.kind() { + ty::TyKind::Param(param) => { + let accessor = ty_accessors[param.index as usize]; + let param_ty = accessor.call()(self_expr); + // We know that in reality `Sized` only has the `Self` type parameter + let inner_sized_check = sized_impl_fun.call()(&[param_ty], &[]); + Some(inner_sized_check) + } + ty::TyKind::Alias(ty::AliasTyKind::Projection, alias_ty) => { + let tcx = vcx.tcx(); + + let is_forced_sized = tcx.item_bounds(alias_ty.def_id) + .instantiate_identity() + .iter() + .any(|clause| { + matches!(clause.kind().skip_binder(), + ty::ClauseKind::Trait(p) if Some(p.def_id()) == tcx.lang_items().sized_trait() + ) + }); + + if is_forced_sized { + // This projection is forced to be `Sized` by its own bounds, so we don't need an + // extra check + return None; + } + + let trait_def = alias_ty.trait_def_id(tcx); + let trait_ = deps.require_ref::(trait_def).unwrap(); + let projection_fun = trait_.funs.assoc_types[&alias_ty.def_id]; + + let ty_params = deps.require_dep::(ty_ctx).unwrap(); + let args = deps + .require_dep::(GArgs::new(ty_ctx, alias_ty.args)) + .unwrap(); + + let projection = projection_fun(args.get_ty(), args.get_const()); + + let inner_sized_check = sized_impl_fun.call()(&[projection], &[]); + + // Introduce let-bindings for the generics of the type + // NOTE: There won't be any name collisions as user defined ADTs cannot + // have a generic called `Self` + let with_consts_bound = ty_params.const_decls().iter().enumerate().rfold( + inner_sized_check, + |expr, (i, decl)| { + let accessor = const_accessors[i]; + vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) + }, + ); + let with_tys_bound = ty_params.ty_decls().iter().enumerate().rfold( + with_consts_bound, + |expr, (i, decl)| { + let accessor = ty_accessors[i]; + vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) + }, + ); + + Some(with_tys_bound) + } + _ => panic!("Unsupported dependent sizedness for {ty:?}"), + } +} From 48ae48ee5b6fb466fcc007b8c6d2b5f7adbf6fcd Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Thu, 26 Feb 2026 17:33:26 +0100 Subject: [PATCH 45/70] Revert unnecessary change --- prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index 738d307f821..48faf4bbbad 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -54,6 +54,8 @@ impl<'vir> TyConstructorEncOutputRef<'vir> { impl<'vir> OutputRefAny for TyConstructorEncOutputRef<'vir> {} +pub type TyConstructorEncOutput<'vir> = vir::AdtConstructor<'vir>; + /// Encodes the lifted representation of a Rust type constructor (e.g. Option, /// Vec, user-defined ADTs). pub struct TyConstructorEnc; @@ -64,7 +66,7 @@ impl TaskEncoder for TyConstructorEnc { type OutputRef<'vir> = TyConstructorEncOutputRef<'vir>; - type OutputFullLocal<'vir> = vir::AdtConstructor<'vir>; + type OutputFullLocal<'vir> = TyConstructorEncOutput<'vir>; type EncodingError = (); From 4efe4a022bead5f9fa1a425067ce9123b4c36db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Fiala?= Date: Thu, 26 Feb 2026 19:20:54 +0100 Subject: [PATCH 46/70] Cleanup, restructuring and renaming The main change is to use proper visibility attributes for the various defined functions and to put these functions in an `impl` of an associated struct (code style in Prusti) --- .../src/encoders/ty/generics/params.rs | 2 +- .../src/encoders/ty/generics/sized_trait.rs | 183 +++++++++-------- .../src/encoders/ty/generics/trait_impls.rs | 8 +- .../src/encoders/ty/generics/traits.rs | 189 +++++++++--------- .../src/encoders/ty/lifted/ty_constructor.rs | 27 +-- 5 files changed, 202 insertions(+), 207 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/params.rs b/prusti-encoder/src/encoders/ty/generics/params.rs index 18256984950..103107f5d81 100644 --- a/prusti-encoder/src/encoders/ty/generics/params.rs +++ b/prusti-encoder/src/encoders/ty/generics/params.rs @@ -278,7 +278,7 @@ impl<'vir> GenericParams<'vir> { }) .collect::>(); // TODO: We also need to handle const args here - (trait_data.funs.assoc_types.get(&a.def_id).unwrap())(tys, &[]) + (trait_data.fns.assoc_types.get(&a.def_id).unwrap())(tys, &[]) }), }; } diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index fee8264ac26..3ee095c1b9f 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -2,11 +2,8 @@ use crate::{ TaskEncoder, encoders::ty::{ Sizedness, - generics::{ - GArgs, GArgsTyEnc, GParams, GenericParamsEnc, - traits::{TraitEnc, trait_impl_fun_idn, trait_unknown_impl_fun_idn}, - }, - lifted::ty_constructor::{unknown_type_discriminator, unknown_type_id_accessor}, + generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc, traits::TraitEnc}, + lifted::TyConstructorEnc, }, }; use prusti_rustc_interface::middle::ty; @@ -14,6 +11,8 @@ use prusti_rustc_interface::middle::ty; pub struct SizedTraitEnc; const SIZED_TRAIT_NAME: &str = "Sized"; +const SIZED_ARGS: <(vir::ManyTyVal, vir::ManyCSnap) as vir::Arity>::Tys<'static> = + (&[vir::TYPE_TYVAL], &[]); #[derive(Copy, Debug, Clone, Hash, Eq, PartialEq)] pub struct SizedTraitEncTask<'vir> { @@ -47,21 +46,20 @@ impl TaskEncoder for SizedTraitEnc { ty_ctx, } = task_key; vir::with_vcx(|vcx| { - let self_expr = vcx.mk_local_ex(sized_self_decl(vcx)); + let self_expr = vcx.mk_local_ex(Self::sized_self_decl(vcx)); let is_this_type = vcx.mk_adt_discriminator_expr(self_expr, discriminator); - let sized_impl_fun_idn = - trait_impl_fun_idn(vcx, SIZED_TRAIT_NAME, (&[vir::TYPE_TYVAL], &[])); + let sized_impl_idn = TraitEnc::trait_impl_idn(vcx, SIZED_TRAIT_NAME, SIZED_ARGS); let check = match sizedness { Sizedness::Sized => Some(is_this_type), Sizedness::Unsized => None, Sizedness::Dependent(ty) => Some( - sizedness_for_dependent( + Self::sizedness_for_dependent( vcx, deps, - sized_impl_fun_idn, + sized_impl_idn, ty_accessors, const_accessors, ty_ctx, @@ -86,20 +84,19 @@ impl TaskEncoder for SizedTraitEnc { .flatten() .collect(); - let sized_impl_fun_idn = - trait_impl_fun_idn(vcx, SIZED_TRAIT_NAME, (&[vir::TYPE_TYVAL], &[])); - let sized_impl_unknown_fun_idn = - trait_unknown_impl_fun_idn(vcx, SIZED_TRAIT_NAME, (&[vir::TYPE_TYVAL], &[])); + let sized_impl_idn = TraitEnc::trait_impl_idn(vcx, SIZED_TRAIT_NAME, SIZED_ARGS); + let sized_impl_unknown_idn = + TraitEnc::trait_unknown_impl_idn(vcx, SIZED_TRAIT_NAME, SIZED_ARGS); - let self_decl = sized_self_decl(vcx); + let self_decl = Self::sized_self_decl(vcx); let self_expr = vcx.mk_local_ex(self_decl); let unknown_check = { let is_unknown = - vcx.mk_adt_discriminator_expr(self_expr, unknown_type_discriminator()); - let unknown_id = unknown_type_id_accessor(vcx).call()(self_expr); + vcx.mk_adt_discriminator_expr(self_expr, TyConstructorEnc::UNKNOWN_TYPE_NAME); + let unknown_id = TyConstructorEnc::unknown_type_id_accessor(vcx).call()(self_expr); - let unknown_impls = sized_impl_unknown_fun_idn.call()(unknown_id, &[], &[]); + let unknown_impls = sized_impl_unknown_idn.call()(unknown_id, &[], &[]); vir::expr! {vcx; (is_unknown) && (unknown_impls) } }; @@ -107,7 +104,7 @@ impl TaskEncoder for SizedTraitEnc { checks.push(unknown_check); let sized_impl_fun = vcx.mk_function( - sized_impl_fun_idn, + sized_impl_idn, (&[self_decl], &[]), &[], &[], @@ -117,7 +114,7 @@ impl TaskEncoder for SizedTraitEnc { program.add_function(sized_impl_fun); let sized_impl_unknown_fun = vcx.mk_function( - sized_impl_unknown_fun_idn, + sized_impl_unknown_idn, (vcx.mk_local_decl("id", vir::TYPE_INT), &[], &[]), &[], &[], @@ -130,80 +127,82 @@ impl TaskEncoder for SizedTraitEnc { } } -fn sized_self_decl<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::LocalDecl<'vir, vir::TyVal> { - vcx.mk_local_decl("Self$0", vir::TYPE_TYVAL) -} +impl SizedTraitEnc { + fn sized_self_decl<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::LocalDecl<'vir, vir::TyVal> { + vcx.mk_local_decl("Self$0", vir::TYPE_TYVAL) + } -/// Check if we need an extra sizedness check as a result of the sizedness dependency -fn sizedness_for_dependent<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, - deps: &mut task_encoder::TaskEncoderDependencies<'vir, SizedTraitEnc>, - sized_impl_fun: vir::FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, - ty_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::TyVal>], - const_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::CSnap>], - ty_ctx: GParams<'vir>, - self_expr: vir::Expr<'vir, vir::TyVal>, - ty: ty::Ty<'vir>, -) -> Option> { - match ty.kind() { - ty::TyKind::Param(param) => { - let accessor = ty_accessors[param.index as usize]; - let param_ty = accessor.call()(self_expr); - // We know that in reality `Sized` only has the `Self` type parameter - let inner_sized_check = sized_impl_fun.call()(&[param_ty], &[]); - Some(inner_sized_check) - } - ty::TyKind::Alias(ty::AliasTyKind::Projection, alias_ty) => { - let tcx = vcx.tcx(); - - let is_forced_sized = tcx.item_bounds(alias_ty.def_id) - .instantiate_identity() - .iter() - .any(|clause| { - matches!(clause.kind().skip_binder(), - ty::ClauseKind::Trait(p) if Some(p.def_id()) == tcx.lang_items().sized_trait() - ) - }); - - if is_forced_sized { - // This projection is forced to be `Sized` by its own bounds, so we don't need an - // extra check - return None; + /// Check if we need an extra sizedness check as a result of the sizedness dependency + fn sizedness_for_dependent<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut task_encoder::TaskEncoderDependencies<'vir, SizedTraitEnc>, + sized_impl_idn: vir::FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, + ty_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::TyVal>], + const_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::CSnap>], + ty_ctx: GParams<'vir>, + self_expr: vir::Expr<'vir, vir::TyVal>, + ty: ty::Ty<'vir>, + ) -> Option> { + match ty.kind() { + ty::TyKind::Param(param) => { + let accessor = ty_accessors[param.index as usize]; + let param_ty = accessor.call()(self_expr); + // We know that in reality `Sized` only has the `Self` type parameter + let inner_sized_check = sized_impl_idn.call()(&[param_ty], &[]); + Some(inner_sized_check) } - - let trait_def = alias_ty.trait_def_id(tcx); - let trait_ = deps.require_ref::(trait_def).unwrap(); - let projection_fun = trait_.funs.assoc_types[&alias_ty.def_id]; - - let ty_params = deps.require_dep::(ty_ctx).unwrap(); - let args = deps - .require_dep::(GArgs::new(ty_ctx, alias_ty.args)) - .unwrap(); - - let projection = projection_fun(args.get_ty(), args.get_const()); - - let inner_sized_check = sized_impl_fun.call()(&[projection], &[]); - - // Introduce let-bindings for the generics of the type - // NOTE: There won't be any name collisions as user defined ADTs cannot - // have a generic called `Self` - let with_consts_bound = ty_params.const_decls().iter().enumerate().rfold( - inner_sized_check, - |expr, (i, decl)| { - let accessor = const_accessors[i]; - vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) - }, - ); - let with_tys_bound = ty_params.ty_decls().iter().enumerate().rfold( - with_consts_bound, - |expr, (i, decl)| { - let accessor = ty_accessors[i]; - vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) - }, - ); - - Some(with_tys_bound) + ty::TyKind::Alias(ty::AliasTyKind::Projection, alias_ty) => { + let tcx = vcx.tcx(); + + let is_forced_sized = tcx.item_bounds(alias_ty.def_id) + .instantiate_identity() + .iter() + .any(|clause| { + matches!(clause.kind().skip_binder(), + ty::ClauseKind::Trait(p) if Some(p.def_id()) == tcx.lang_items().sized_trait() + ) + }); + + if is_forced_sized { + // This projection is forced to be `Sized` by its own bounds, so we don't need an + // extra check + return None; + } + + let trait_def = alias_ty.trait_def_id(tcx); + let trait_ = deps.require_ref::(trait_def).unwrap(); + let projection_fun = trait_.fns.assoc_types[&alias_ty.def_id]; + + let ty_params = deps.require_dep::(ty_ctx).unwrap(); + let args = deps + .require_dep::(GArgs::new(ty_ctx, alias_ty.args)) + .unwrap(); + + let projection = projection_fun(args.get_ty(), args.get_const()); + + let inner_sized_check = sized_impl_idn.call()(&[projection], &[]); + + // Introduce let-bindings for the generics of the type + // NOTE: There won't be any name collisions as user defined ADTs cannot + // have a generic called `Self` + let with_consts_bound = ty_params.const_decls().iter().enumerate().rfold( + inner_sized_check, + |expr, (i, decl)| { + let accessor = const_accessors[i]; + vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) + }, + ); + let with_tys_bound = ty_params.ty_decls().iter().enumerate().rfold( + with_consts_bound, + |expr, (i, decl)| { + let accessor = ty_accessors[i]; + vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) + }, + ); + + Some(with_tys_bound) + } + _ => panic!("Unsupported dependent sizedness for {ty:?}"), } - _ => panic!("Unsupported dependent sizedness for {ty:?}"), } } diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 16322231137..4fd259db9a3 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -112,7 +112,7 @@ impl TaskEncoder for TraitImplEnc { match impl_item.kind { ty::AssocKind::Type { .. } => { let assoc_type = - trait_data.funs.assoc_types.get(&trait_item_def_id).unwrap(); + trait_data.fns.assoc_types.get(&trait_item_def_id).unwrap(); // the type we want to resolve the type alias to let assoc_type_expr = assoc_params.ty_expr( @@ -352,7 +352,7 @@ fn assemble_type<'vir>( let trait_ = deps.require_ref::(trait_did).unwrap(); let assoc_ty_fun = trait_ - .funs + .fns .assoc_types .get(&alias.def_id) .expect("associated type to be in the mapping"); @@ -464,7 +464,7 @@ fn process_projection<'vir>( match projection.term.kind() { ty::TermKind::Ty(tgt_ty) => { let projection_fun = trait_ - .funs + .fns .assoc_types .get(&proj_did) .expect("Projection did should be in the mapping"); @@ -475,7 +475,7 @@ fn process_projection<'vir>( } ty::TermKind::Const(const_) => { let projection_fun = trait_ - .funs + .fns .assoc_consts .get(&proj_did) .expect("Projection did should be in the mapping"); diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 7d73d26a49f..38fd7417d53 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -6,7 +6,7 @@ use vir::{Arity, FunctionIdn, vir_format_identifier}; use crate::encoders::ty::{ RustTyDecomposition, generics::{GParams, GenericParamsEnc, trait_impls::TraitImplEnc}, - lifted::ty_constructor::{unknown_type_discriminator, unknown_type_id_accessor}, + lifted::TyConstructorEnc, pure::TyPureEnc, }; @@ -15,7 +15,7 @@ pub struct TraitEnc; #[derive(Debug, Clone)] pub struct TraitEncOutputRef<'vir> { pub trait_name: &'vir str, - pub funs: TraitFuns<'vir>, + pub fns: TraitFuns<'vir>, pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, } impl OutputRefAny for TraitEncOutputRef<'_> {} @@ -38,16 +38,6 @@ impl TaskEncoder for TraitEnc { type OutputRef<'vir> = TraitEncOutputRef<'vir>; type OutputFullLocal<'vir> = Option>; - fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { - for trait_enc in TraitEnc::all_outputs_local_no_errors() - .into_iter() - .flatten() - { - program.add_domain(trait_enc.trait_domain); - program.add_function(trait_enc.impl_fun); - } - } - fn do_encode_full<'vir>( task_key: &Self::TaskKey<'vir>, deps: &mut TaskEncoderDependencies<'vir, Self>, @@ -58,14 +48,14 @@ impl TaskEncoder for TraitEnc { let trait_name = vcx.alloc_str(tcx.item_name(task_key).as_str()); let trait_items = tcx.associated_items(task_key).in_definition_order(); - let assoc_funs = associated_items_funs(vcx, deps, trait_name, trait_items); + let assoc_fns = Self::associated_items_fns(vcx, deps, trait_name, trait_items); - let mut funcs = assoc_funs.mk_domain_functions(vcx); + let mut funcs = assoc_fns.mk_domain_functions(vcx); let args = (params.ty_args(), params.const_args()); let decls = (params.ty_decls(), params.const_decls()); - let impl_fun_idn = trait_impl_fun_idn(vcx, trait_name, args); - let unkown_impl_fun_idn = trait_unknown_impl_fun_idn(vcx, trait_name, args); + let impl_fun_idn = Self::trait_impl_idn(vcx, trait_name, args); + let unkown_impl_fun_idn = Self::trait_unknown_impl_idn(vcx, trait_name, args); // Emit the impl function reference early, so that it can be used to encode caller // bounds without causing dependency cycles. @@ -73,7 +63,7 @@ impl TaskEncoder for TraitEnc { *task_key, TraitEncOutputRef { trait_name, - funs: assoc_funs, + fns: assoc_fns, impl_fun: impl_fun_idn, }, )?; @@ -97,10 +87,11 @@ impl TaskEncoder for TraitEnc { { let self_expr = params.ty_exprs()[0]; - let is_unknown_type = - vcx.mk_adt_discriminator_expr(self_expr, unknown_type_discriminator()); + let is_unknown_type = vcx + .mk_adt_discriminator_expr(self_expr, TyConstructorEnc::UNKNOWN_TYPE_NAME); - let extracted_id = unknown_type_id_accessor(vcx).call()(self_expr); + let extracted_id = + TyConstructorEnc::unknown_type_id_accessor(vcx).call()(self_expr); let unknown_impls = unkown_impl_fun_idn( extracted_id, @@ -125,7 +116,7 @@ impl TaskEncoder for TraitEnc { funcs.push(impl_unknown_fun); let trait_domain = vcx.mk_domain( - trait_domain_idn(vcx, trait_name), + Self::trait_domain_idn(vcx, trait_name), &[], &[], vcx.alloc_slice(funcs.as_slice()), @@ -140,6 +131,14 @@ impl TaskEncoder for TraitEnc { )) }) } + + fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { + for output in TraitEnc::all_outputs_local_no_errors() { + let Some(output) = output else { continue }; + program.add_domain(output.trait_domain); + program.add_function(output.impl_fun); + } + } } #[derive(Debug, Clone)] @@ -164,84 +163,86 @@ impl<'vir> TraitFuns<'vir> { } } -/// Collect mappings for associated items of a trait to their corresponding VIR functions. -fn associated_items_funs<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, - deps: &mut TaskEncoderDependencies<'vir, TraitEnc>, - trait_name: &str, - assoc_items: impl Iterator, -) -> TraitFuns<'vir> { - let tcx = vcx.tcx(); - - let mut assoc_types = FxHashMap::default(); - let mut assoc_consts = FxHashMap::default(); - - let mk_identifier = |item_name, item_type| { - vir_format_identifier!(vcx, "{trait_name}_assoc_{item_type}_{item_name}") - }; - - for item in assoc_items { - let assoc_did = item.def_id; - let name = item.name(); - let params = deps - .require_dep::(GParams::from(assoc_did)) - .unwrap(); - let args = (params.ty_args(), params.const_args()); - - match item.kind { - ty::AssocKind::Type { .. } => { - let fun = FunctionIdn::new(mk_identifier(name, "type"), args, vir::TYPE_TYVAL); - assoc_types.insert(assoc_did, fun); - } - ty::AssocKind::Const { .. } => { - let rust_ty = tcx.type_of(assoc_did).skip_binder(); - let decomp = RustTyDecomposition::from_ty(rust_ty, assoc_did); - let ret_ty = (deps.require_ref::(decomp.ty).unwrap().domain)(); +impl TraitEnc { + /// Collect mappings for associated items of a trait to their corresponding VIR functions. + fn associated_items_fns<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, Self>, + trait_name: &str, + assoc_items: impl Iterator, + ) -> TraitFuns<'vir> { + let tcx = vcx.tcx(); + + let mut assoc_types = FxHashMap::default(); + let mut assoc_consts = FxHashMap::default(); + + let mk_identifier = |item_name, item_type| { + vir_format_identifier!(vcx, "{trait_name}_assoc_{item_type}_{item_name}") + }; + + for item in assoc_items { + let assoc_did = item.def_id; + let name = item.name(); + let params = deps + .require_dep::(GParams::from(assoc_did)) + .unwrap(); + let args = (params.ty_args(), params.const_args()); - let fun = FunctionIdn::new(mk_identifier(name, "const"), args, ret_ty); - assoc_consts.insert(assoc_did, fun); - } - ty::AssocKind::Fn { .. } => { - // unimplemented + match item.kind { + ty::AssocKind::Type { .. } => { + let fun = FunctionIdn::new(mk_identifier(name, "type"), args, vir::TYPE_TYVAL); + assoc_types.insert(assoc_did, fun); + } + ty::AssocKind::Const { .. } => { + let rust_ty = tcx.type_of(assoc_did).skip_binder(); + let decomp = RustTyDecomposition::from_ty(rust_ty, assoc_did); + let ret_ty = (deps.require_ref::(decomp.ty).unwrap().domain)(); + + let fun = FunctionIdn::new(mk_identifier(name, "const"), args, ret_ty); + assoc_consts.insert(assoc_did, fun); + } + ty::AssocKind::Fn { .. } => { + // unimplemented + } } } + TraitFuns { + assoc_types, + assoc_consts, + } } - TraitFuns { - assoc_types, - assoc_consts, - } -} -pub fn trait_impl_fun_idn<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - trait_name: &'a str, - args: <(vir::ManyTyVal, vir::ManyCSnap) as Arity>::Tys<'vir>, -) -> FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool> { - FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_impl"), - args, - vir::TYPE_BOOL, - ) -} + pub(super) fn trait_impl_idn<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + trait_name: &'a str, + args: <(vir::ManyTyVal, vir::ManyCSnap) as Arity>::Tys<'vir>, + ) -> FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool> { + FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_impl"), + args, + vir::TYPE_BOOL, + ) + } -pub fn trait_unknown_impl_fun_idn<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - trait_name: &'a str, - args: <(vir::ManyTyVal, vir::ManyCSnap) as Arity>::Tys<'vir>, -) -> FunctionIdn<'vir, (vir::Int, vir::ManyTyVal, vir::ManyCSnap), vir::Bool> { - // Omit the `Self` type as it is known to be the unknown type - let ty_args = &args.0[1..]; - let const_args = args.1; - FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_unknown_impl"), - (vir::TYPE_INT, ty_args, const_args), - vir::TYPE_BOOL, - ) -} + pub(super) fn trait_unknown_impl_idn<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + trait_name: &'a str, + args: <(vir::ManyTyVal, vir::ManyCSnap) as Arity>::Tys<'vir>, + ) -> FunctionIdn<'vir, (vir::Int, vir::ManyTyVal, vir::ManyCSnap), vir::Bool> { + // Omit the `Self` type as it is known to be the unknown type + let ty_args = &args.0[1..]; + let const_args = args.1; + FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_unknown_impl"), + (vir::TYPE_INT, ty_args, const_args), + vir::TYPE_BOOL, + ) + } -pub fn trait_domain_idn<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - trait_name: &'a str, -) -> vir::ViperIdent<'vir> { - vir_format_identifier!(vcx, "t_{trait_name}") + pub(super) fn trait_domain_idn<'vir, 'a>( + vcx: &'vir vir::VirCtxt<'vir>, + trait_name: &'a str, + ) -> vir::ViperIdent<'vir> { + vir_format_identifier!(vcx, "t_{trait_name}") + } } diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index 48faf4bbbad..d659edbaf89 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -153,7 +153,8 @@ impl TaskEncoder for TyConstructorEnc { fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { let mut constructors = Self::all_outputs_local_no_errors(); vir::with_vcx(|vcx| { - constructors.push(unkonwn_type_constructor(vcx)); + let args = vcx.alloc_array(&[vcx.mk_local_decl(Self::UNKNOWN_TYPE_ID, vir::TYPE_INT)]); + constructors.push(vcx.mk_adt_constructor(Self::UNKNOWN_TYPE_NAME, args)); let adt = vcx.mk_adt( vir::ViperIdent::new("Type"), &[], @@ -164,20 +165,14 @@ impl TaskEncoder for TyConstructorEnc { } } -const UNKNOWN_TYPE_NAME: &str = "Unknown_type"; -const UNKNOWN_TYPE_ID: &str = "id"; +impl TyConstructorEnc { + /// The name of the constructor for the unknown type variant in the `Type` ADT. + pub const UNKNOWN_TYPE_NAME: &str = "Unknown_type"; + const UNKNOWN_TYPE_ID: &str = "id"; -pub fn unkonwn_type_constructor<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::AdtConstructor<'vir> { - let args = vcx.alloc_array(&[vcx.mk_local_decl(UNKNOWN_TYPE_ID, vir::TYPE_INT)]); - vcx.mk_adt_constructor(UNKNOWN_TYPE_NAME, args) -} - -pub fn unknown_type_discriminator() -> &'static str { - UNKNOWN_TYPE_NAME -} - -pub fn unknown_type_id_accessor<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, -) -> vir::AdtDestructor<'vir, vir::TyVal, vir::Int> { - vcx.mk_adt_destructor(UNKNOWN_TYPE_ID, vir::TYPE_TYVAL, vir::TYPE_INT) + pub fn unknown_type_id_accessor<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + ) -> vir::AdtDestructor<'vir, vir::TyVal, vir::Int> { + vcx.mk_adt_destructor(Self::UNKNOWN_TYPE_ID, vir::TYPE_TYVAL, vir::TYPE_INT) + } } From 230fbb0396de7487a6b6fa24d54fa914c803fe7b Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Fri, 27 Feb 2026 15:49:31 +0100 Subject: [PATCH 47/70] Emit the domain for the Sized trait --- .../src/encoders/ty/generics/sized_trait.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index 3ee095c1b9f..cf64144842d 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -113,16 +113,17 @@ impl TaskEncoder for SizedTraitEnc { ); program.add_function(sized_impl_fun); - let sized_impl_unknown_fun = vcx.mk_function( - sized_impl_unknown_idn, - (vcx.mk_local_decl("id", vir::TYPE_INT), &[], &[]), + let sized_impl_unknown_fun = + vcx.mk_domain_function(sized_impl_unknown_idn, false, None); + + let sized_domain = vcx.mk_domain( + TraitEnc::trait_domain_idn(vcx, SIZED_TRAIT_NAME), &[], &[], - None, + vcx.alloc_slice(&[sized_impl_unknown_fun]), None, ); - - program.add_function(sized_impl_unknown_fun); + program.add_domain(sized_domain); }); } } From bd641cef509b119d36670dd3ed84b5f8bd4f51c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Fiala?= Date: Fri, 27 Feb 2026 11:59:11 +0100 Subject: [PATCH 48/70] Correctly implement the `Alias` case of `ty_expr` --- .../src/encoders/ty/generics/params.rs | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/params.rs b/prusti-encoder/src/encoders/ty/generics/params.rs index 103107f5d81..cf1a3f3bb15 100644 --- a/prusti-encoder/src/encoders/ty/generics/params.rs +++ b/prusti-encoder/src/encoders/ty/generics/params.rs @@ -1,6 +1,6 @@ use prusti_interface::specs::typed::ExternSpecKind; use prusti_rustc_interface::{ - middle::{ty, ty::TyKind}, + middle::ty, span::{def_id::DefId, symbol}, }; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; @@ -11,7 +11,7 @@ use crate::encoders::{ ty::{ RustTyDecomposition, data::TySpecifics, - generics::{GArgsTyEnc, GParamVariant, traits::TraitEnc}, + generics::{GArgs, GArgsTyEnc, GParamVariant, traits::TraitEnc}, lifted::TyConstructorEnc, }, }; @@ -262,23 +262,13 @@ impl<'vir> GenericParams<'vir> { let param = ty.args.expect_param(); return match param { GParamVariant::Param(p) => self.ty_exprs[self.map_idx(p.index).unwrap()], - GParamVariant::Alias(a) => vir::with_vcx(|vcx| { + GParamVariant::Alias(alias) => vir::with_vcx(|vcx| { let tcx = vcx.tcx(); - let trait_did = tcx.associated_item(a.def_id).container_id(tcx); + let trait_did = tcx.associated_item(alias.def_id).container_id(tcx); let trait_data = deps.require_ref::(trait_did).unwrap(); - let tys = &a - .args - .iter() - .map(|arg| match arg.expect_ty().kind() { - TyKind::Param(p) => self.ty_exprs[self.map_idx(p.index).unwrap()], - _ => self.ty_expr( - deps, - RustTyDecomposition::from_ty(arg.expect_ty(), ty.args.context), - ), - }) - .collect::>(); - // TODO: We also need to handle const args here - (trait_data.fns.assoc_types.get(&a.def_id).unwrap())(tys, &[]) + let args = GArgs::new(ty.args.context, alias.args); + let args = deps.require_dep::(args).unwrap(); + (trait_data.fns.assoc_types[&alias.def_id])(args.get_ty(), args.get_const()) }), }; } From abf0947e13d8a67cc87afc2d744eee37eb1bb030 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Fri, 27 Feb 2026 16:58:36 +0100 Subject: [PATCH 49/70] Simplify the task for SizedTraitEnc --- .../src/encoders/ty/generics/sized_trait.rs | 54 ++++++++----------- .../src/encoders/ty/lifted/ty_constructor.rs | 15 ++---- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index cf64144842d..dca9c5262e3 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -1,12 +1,13 @@ use crate::{ TaskEncoder, encoders::ty::{ - Sizedness, + RustTy, Sizedness, generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc, traits::TraitEnc}, - lifted::TyConstructorEnc, + lifted::{TyConstructorEnc, ty_constructor::TyConstructorEncOutputRef}, }, }; use prusti_rustc_interface::middle::ty; +use vir::CallableIdn; pub struct SizedTraitEnc; @@ -14,18 +15,9 @@ const SIZED_TRAIT_NAME: &str = "Sized"; const SIZED_ARGS: <(vir::ManyTyVal, vir::ManyCSnap) as vir::Arity>::Tys<'static> = (&[vir::TYPE_TYVAL], &[]); -#[derive(Copy, Debug, Clone, Hash, Eq, PartialEq)] -pub struct SizedTraitEncTask<'vir> { - pub sizedness: Sizedness<'vir>, - pub discriminator: &'vir str, - pub ty_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::TyVal>], - pub const_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::CSnap>], - pub ty_ctx: GParams<'vir>, -} - impl TaskEncoder for SizedTraitEnc { task_encoder::encoder_cache!(SizedTraitEnc); - type TaskDescription<'vir> = SizedTraitEncTask<'vir>; + type TaskDescription<'vir> = RustTy<'vir>; type OutputFullLocal<'vir> = Option>; @@ -37,14 +29,12 @@ impl TaskEncoder for SizedTraitEnc { task_key: &Self::TaskKey<'vir>, deps: &mut task_encoder::TaskEncoderDependencies<'vir, Self>, ) -> task_encoder::EncodeFullResult<'vir, Self> { + assert!(!task_key.specifics.is_param()); deps.emit_output_ref(*task_key, ())?; - let &Self::TaskKey { - sizedness, - discriminator, - ty_accessors, - const_accessors, - ty_ctx, - } = task_key; + + let vpr_type = deps.require_ref::(*task_key)?; + let discriminator = vpr_type.ty_constructor.name().to_str(); + vir::with_vcx(|vcx| { let self_expr = vcx.mk_local_ex(Self::sized_self_decl(vcx)); @@ -52,19 +42,18 @@ impl TaskEncoder for SizedTraitEnc { let sized_impl_idn = TraitEnc::trait_impl_idn(vcx, SIZED_TRAIT_NAME, SIZED_ARGS); - let check = match sizedness { + let check = match task_key.sizedness { Sizedness::Sized => Some(is_this_type), Sizedness::Unsized => None, - Sizedness::Dependent(ty) => Some( + Sizedness::Dependent(dep_ty) => Some( Self::sizedness_for_dependent( vcx, deps, sized_impl_idn, - ty_accessors, - const_accessors, - ty_ctx, + vpr_type, + task_key.params, self_expr, - ty, + dep_ty, ) .map_or( is_this_type, @@ -138,15 +127,14 @@ impl SizedTraitEnc { vcx: &'vir vir::VirCtxt<'vir>, deps: &mut task_encoder::TaskEncoderDependencies<'vir, SizedTraitEnc>, sized_impl_idn: vir::FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, - ty_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::TyVal>], - const_accessors: &'vir [vir::AdtDestructor<'vir, vir::TyVal, vir::CSnap>], + vpr_type: TyConstructorEncOutputRef<'vir>, ty_ctx: GParams<'vir>, self_expr: vir::Expr<'vir, vir::TyVal>, - ty: ty::Ty<'vir>, + depneded_on: ty::Ty<'vir>, ) -> Option> { - match ty.kind() { + match depneded_on.kind() { ty::TyKind::Param(param) => { - let accessor = ty_accessors[param.index as usize]; + let accessor = vpr_type.ty_param_accessors[param.index as usize]; let param_ty = accessor.call()(self_expr); // We know that in reality `Sized` only has the `Self` type parameter let inner_sized_check = sized_impl_idn.call()(&[param_ty], &[]); @@ -189,21 +177,21 @@ impl SizedTraitEnc { let with_consts_bound = ty_params.const_decls().iter().enumerate().rfold( inner_sized_check, |expr, (i, decl)| { - let accessor = const_accessors[i]; + let accessor = vpr_type.const_param_accessors[i]; vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) }, ); let with_tys_bound = ty_params.ty_decls().iter().enumerate().rfold( with_consts_bound, |expr, (i, decl)| { - let accessor = ty_accessors[i]; + let accessor = vpr_type.ty_param_accessors[i]; vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) }, ); Some(with_tys_bound) } - _ => panic!("Unsupported dependent sizedness for {ty:?}"), + _ => panic!("Unsupported dependent sizedness for {depneded_on:?}"), } } } diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index d659edbaf89..9517f20c0b9 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -3,10 +3,7 @@ use vir::{CallableIdn, CastType, FunctionIdn, HasType}; use crate::encoders::ty::{ RustTy, - generics::{ - GenericParamsEnc, - sized_trait::{SizedTraitEnc, SizedTraitEncTask}, - }, + generics::{GenericParamsEnc, sized_trait::SizedTraitEnc}, }; use super::r#typeof::{TypeOfEnc, TypeOfEncOutputRef}; @@ -137,14 +134,8 @@ impl TaskEncoder for TyConstructorEnc { let constructor = vcx.mk_adt_constructor(type_function_ident.name().to_str(), vcx.alloc_slice(&args)); - let sizedness_task = SizedTraitEncTask { - sizedness: task_key.sizedness, - discriminator: type_function_ident.name().to_str(), - ty_accessors: ty_accessor_functions, - const_accessors: const_accessor_functions, - ty_ctx: task_key.params, - }; - deps.require_dep::(sizedness_task)?; + // NOTE: This call depends on the ref output of this encoder + deps.require_dep::(task_key)?; Ok((constructor, ())) }) From 42d860c6aa07d25bcecf2ad04b18297527a6833e Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 3 Mar 2026 16:20:34 +0100 Subject: [PATCH 50/70] Add optional suffix for generics renaming --- .../src/encoders/ty/generics/params.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/prusti-encoder/src/encoders/ty/generics/params.rs b/prusti-encoder/src/encoders/ty/generics/params.rs index cf1a3f3bb15..5d9205fc521 100644 --- a/prusti-encoder/src/encoders/ty/generics/params.rs +++ b/prusti-encoder/src/encoders/ty/generics/params.rs @@ -27,6 +27,8 @@ pub struct GParams<'tcx> { /// This flag indicates whether this is the case, so that we can replace it /// with the actual `Self` parameter when needed. is_trait_extern_spec: bool, + /// A suffix to disambiguate generic parameters of different contexts + suffix: Option<&'static str>, } impl<'tcx> GParams<'tcx> { @@ -39,6 +41,7 @@ impl<'tcx> GParams<'tcx> { params, env, is_trait_extern_spec, + suffix: None, } } @@ -89,6 +92,13 @@ impl<'tcx> GParams<'tcx> { ); } + pub fn with_suffix(self, suffix: &'static str) -> Self { + Self { + suffix: Some(suffix), + ..self + } + } + /// Tries to normalize associated types of the corresponding type. Returns /// `Some` if managed to normalize (or there were no associated types), else /// returns None. @@ -297,7 +307,12 @@ impl TaskEncoder for GenericParamsEnc { deps.emit_output_ref(*task_key, ())?; vir::with_vcx(|vcx| { let sanitize = |name: symbol::Symbol, index: u32| { - vir::ViperIdent::sanitize(vcx, &format!("{name}${index}")).to_str() + let name = if let Some(suffix) = task_key.suffix { + format!("{name}${index}_{suffix}") + } else { + format!("{name}${index}") + }; + vir::ViperIdent::sanitize(vcx, &name).to_str() }; let mut indices = vec![Ok(usize::MAX); task_key.params.len()]; From 01bf445b762aa005f92f3c6dc5a754183001821b Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 3 Mar 2026 17:39:43 +0100 Subject: [PATCH 51/70] Simplified clauses encoding prototype --- .../src/encoders/ty/generics/trait_impls.rs | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 4fd259db9a3..34b29f4028b 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -3,7 +3,7 @@ use std::{collections::VecDeque, iter}; use prusti_rustc_interface::{middle::ty, span::def_id::DefId}; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; -use vir::{CallableIdn, CastType, Domain, vir_format_identifier}; +use vir::{CallableIdn, CastType, Domain, Dyn, vir_format_identifier}; use crate::encoders::{ ConstEnc, @@ -147,11 +147,100 @@ impl TaskEncoder for TraitImplEnc { } } +fn impl_block_condition2<'vir>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, + impl_did: DefId, +) { + let tcx = vcx.tcx(); + let impl_ctx = GParams::from(impl_did).with_suffix("impl"); + + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); + + let trait_params = GParams::from(trait_ref.def_id).with_suffix("trait"); + let trait_params = deps.require_dep::(trait_params).unwrap(); + let impl_params = deps.require_dep::(impl_ctx).unwrap(); + + let impl_args = deps + .require_dep::(GArgs::new(impl_ctx, trait_ref.args)) + .unwrap(); + let impl_ty_args = impl_args.get_ty(); + let trait_ty_params = trait_params.ty_exprs(); + + let mut checks = Vec::new(); + for (trait_ty_param, impl_ty_arg) in std::iter::zip(trait_ty_params, impl_ty_args) { + checks.push(vcx.mk_eq_expr(trait_ty_param, impl_ty_arg)); + } + + dbg!(checks); + + let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); + dbg!(&caller_bounds); + + let mut bound_checks = Vec::new(); + + for clause in caller_bounds { + match clause.kind().skip_binder() { + ty::ClauseKind::Trait(trait_pred) => { + let trait_did = trait_pred.def_id(); + let trait_ = deps.require_ref::(trait_did).unwrap(); + let gargs = GArgs::new(impl_ctx, trait_pred.trait_ref.args); + let gargs = deps.require_dep::(gargs).unwrap(); + + let impl_check = (trait_.impl_fun)(gargs.get_ty(), gargs.get_const()); + dbg!(&impl_check); + bound_checks.push(impl_check); + } + ty::ClauseKind::Projection(proj_pred) => { + let trait_did = proj_pred.trait_def_id(tcx); + let trait_ = deps.require_ref::(trait_did).unwrap(); + let gargs = GArgs::new(impl_ctx, proj_pred.projection_term.args); + let gargs = deps.require_dep::(gargs).unwrap(); + + let (projection, expr): (vir::ExprDyn, vir::ExprDyn) = match proj_pred.term.kind() { + ty::TermKind::Ty(ty) => { + let projection = trait_.fns.assoc_types[&proj_pred.def_id()]( + gargs.get_ty(), + gargs.get_const(), + ); + let ty = RustTyDecomposition::from_ty(ty, impl_ctx); + let ty_expr = impl_params.ty_expr(deps, ty); + (projection.upcast_ty(), ty_expr.upcast_ty()) + } + ty::TermKind::Const(const_) => { + let projection = trait_.fns.assoc_consts[&proj_pred.def_id()]( + gargs.get_ty(), + gargs.get_const(), + ); + let const_ty = tcx.type_of(proj_pred.def_id()).instantiate_identity(); + let const_task = ConstEncTask::Ty { + const_, + ty: const_ty, + context: impl_ctx, + }; + let const_expr = deps.require_dep::(const_task).unwrap(); + (projection.upcast_ty(), const_expr.upcast_ty()) + } + }; + + let projection_check = vcx.mk_eq_expr(projection, expr); + dbg!(&projection_check); + bound_checks.push(projection_check); + } + _ => unimplemented!( + "only trait and projection predicates in caller bounds are supported" + ), + } + } + dbg!(bound_checks); +} + fn impl_block_condition<'vir>( vcx: &'vir vir::VirCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, impl_did: DefId, ) -> vir::ExprBool<'vir> { + impl_block_condition2(vcx, deps, impl_did); let tcx = vcx.tcx(); let impl_ctx = GParams::from(impl_did); From 1f6bdb1754f4764d9901f6596a9dc85df608352c Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 4 Mar 2026 15:20:45 +0100 Subject: [PATCH 52/70] Let binding encoding --- .../src/encoders/ty/generics/params.rs | 2 +- .../src/encoders/ty/generics/trait_impls.rs | 648 ++++++------------ .../src/encoders/ty/generics/traits.rs | 3 +- 3 files changed, 224 insertions(+), 429 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/params.rs b/prusti-encoder/src/encoders/ty/generics/params.rs index 5d9205fc521..6a945466f81 100644 --- a/prusti-encoder/src/encoders/ty/generics/params.rs +++ b/prusti-encoder/src/encoders/ty/generics/params.rs @@ -240,7 +240,7 @@ impl<'vir> GenericParams<'vir> { self.const_exprs()[self.map_idx(param.index).unwrap_err()] } - fn map_idx(&self, index: u32) -> Result { + pub(super) fn map_idx(&self, index: u32) -> Result { let result = self.indices[index as usize]; assert!( result.ok().is_none_or(|i| i != usize::MAX), diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 34b29f4028b..5daca388c75 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -1,16 +1,15 @@ -use rustc_hash::FxHashMap; use std::{collections::VecDeque, iter}; -use prusti_rustc_interface::{middle::ty, span::def_id::DefId}; +use prusti_rustc_interface::{index::bit_set::DenseBitSet, middle::ty, span::def_id::DefId}; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; -use vir::{CallableIdn, CastType, Domain, Dyn, vir_format_identifier}; +use vir::{CastType, Domain, vir_format_identifier}; use crate::encoders::{ ConstEnc, r#const::ConstEncTask, ty::{ RustTyDecomposition, - generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc, traits::TraitEnc}, + generics::{GArgs, GArgsTyEnc, GParams, GenericParams, GenericParamsEnc, traits::TraitEnc}, lifted::TyConstructorEnc, }, }; @@ -147,92 +146,100 @@ impl TaskEncoder for TraitImplEnc { } } -fn impl_block_condition2<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, - deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - impl_did: DefId, -) { - let tcx = vcx.tcx(); - let impl_ctx = GParams::from(impl_did).with_suffix("impl"); +fn bitset_from(iter: impl IntoIterator, size: usize) -> DenseBitSet { + iter.into_iter() + .fold(DenseBitSet::new_empty(size), |mut acc, idx| { + acc.insert(idx); + acc + }) +} +fn projection_deps<'vir>( + projection: ty::ProjectionPredicate<'vir>, + generics_count: usize, +) -> (DenseBitSet, DenseBitSet) { + let generic_idx = |arg: ty::GenericArg| match arg.kind() { + ty::GenericArgKind::Type(ty) if let ty::TyKind::Param(p) = ty.kind() => Some(p.index), + ty::GenericArgKind::Const(const_) if let ty::ConstKind::Param(p) = const_.kind() => { + Some(p.index) + } + _ => None, + }; - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); + let required = projection + .projection_term + .args + .iter() + .flat_map(|arg| arg.walk().filter_map(generic_idx)); - let trait_params = GParams::from(trait_ref.def_id).with_suffix("trait"); - let trait_params = deps.require_dep::(trait_params).unwrap(); - let impl_params = deps.require_dep::(impl_ctx).unwrap(); + let produced = projection.term.walk().filter_map(generic_idx); - let impl_args = deps - .require_dep::(GArgs::new(impl_ctx, trait_ref.args)) - .unwrap(); - let impl_ty_args = impl_args.get_ty(); - let trait_ty_params = trait_params.ty_exprs(); + ( + bitset_from(required, generics_count), + bitset_from(produced, generics_count), + ) +} - let mut checks = Vec::new(); - for (trait_ty_param, impl_ty_arg) in std::iter::zip(trait_ty_params, impl_ty_args) { - checks.push(vcx.mk_eq_expr(trait_ty_param, impl_ty_arg)); +fn order_projections<'vir>( + known_generics: impl IntoIterator, + projections: impl IntoIterator>, + generics_count: usize, +) -> Vec> { + let mut known_generics = bitset_from(known_generics, generics_count); + + let mut worklist: VecDeque<_> = projections + .into_iter() + .map(|p| (p, projection_deps(p, generics_count))) + .collect(); + + let mut ordered = Vec::new(); + + while let Some((proj, (required, produced))) = worklist.pop_front() { + if known_generics.superset(&required) { + known_generics.union(&produced); + ordered.push(proj); + } else { + worklist.push_back((proj, (required, produced))); + } } - dbg!(checks); + ordered +} - let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); - dbg!(&caller_bounds); +fn discover_bind_points<'vir>( + deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, + generic_map: &mut GenericsMap<'vir>, + ctx: GParams<'vir>, + expr: vir::ExprDyn<'vir>, + ty: ty::Ty<'vir>, +) { + dbg!(&ty); - let mut bound_checks = Vec::new(); + if let ty::TyKind::Param(p) = ty.kind() { + generic_map.try_insert(p.index, expr); + return; + } - for clause in caller_bounds { - match clause.kind().skip_binder() { - ty::ClauseKind::Trait(trait_pred) => { - let trait_did = trait_pred.def_id(); - let trait_ = deps.require_ref::(trait_did).unwrap(); - let gargs = GArgs::new(impl_ctx, trait_pred.trait_ref.args); - let gargs = deps.require_dep::(gargs).unwrap(); + let decomp = RustTyDecomposition::from_ty(ty, ctx); + let ty_enc = deps.require_ref::(decomp.ty).unwrap(); - let impl_check = (trait_.impl_fun)(gargs.get_ty(), gargs.get_const()); - dbg!(&impl_check); - bound_checks.push(impl_check); - } - ty::ClauseKind::Projection(proj_pred) => { - let trait_did = proj_pred.trait_def_id(tcx); - let trait_ = deps.require_ref::(trait_did).unwrap(); - let gargs = GArgs::new(impl_ctx, proj_pred.projection_term.args); - let gargs = deps.require_dep::(gargs).unwrap(); - - let (projection, expr): (vir::ExprDyn, vir::ExprDyn) = match proj_pred.term.kind() { - ty::TermKind::Ty(ty) => { - let projection = trait_.fns.assoc_types[&proj_pred.def_id()]( - gargs.get_ty(), - gargs.get_const(), - ); - let ty = RustTyDecomposition::from_ty(ty, impl_ctx); - let ty_expr = impl_params.ty_expr(deps, ty); - (projection.upcast_ty(), ty_expr.upcast_ty()) - } - ty::TermKind::Const(const_) => { - let projection = trait_.fns.assoc_consts[&proj_pred.def_id()]( - gargs.get_ty(), - gargs.get_const(), - ); - let const_ty = tcx.type_of(proj_pred.def_id()).instantiate_identity(); - let const_task = ConstEncTask::Ty { - const_, - ty: const_ty, - context: impl_ctx, - }; - let const_expr = deps.require_dep::(const_task).unwrap(); - (projection.upcast_ty(), const_expr.upcast_ty()) - } - }; + let args = decomp.args.args(); + let inner_types = args.iter().filter_map(|arg| arg.as_type()); + for (i, inner_ty) in inner_types.enumerate() { + let accessor = ty_enc.ty_param_accessors[i]; + let inner_expr = accessor.call()(expr.downcast_ty()); - let projection_check = vcx.mk_eq_expr(projection, expr); - dbg!(&projection_check); - bound_checks.push(projection_check); - } - _ => unimplemented!( - "only trait and projection predicates in caller bounds are supported" - ), + discover_bind_points(deps, generic_map, ctx, inner_expr.upcast_ty(), inner_ty); + } + + let inner_consts = args.iter().filter_map(|arg| arg.as_const()); + for (i, inner_const) in inner_consts.enumerate() { + let accessor = ty_enc.const_param_accessors[i]; + let inner_expr = accessor.call()(expr.downcast_ty()); + + if let ty::ConstKind::Param(p) = inner_const.kind() { + generic_map.try_insert(p.index, inner_expr.upcast_ty()); } } - dbg!(bound_checks); } fn impl_block_condition<'vir>( @@ -240,399 +247,186 @@ fn impl_block_condition<'vir>( deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, impl_did: DefId, ) -> vir::ExprBool<'vir> { - impl_block_condition2(vcx, deps, impl_did); let tcx = vcx.tcx(); - let impl_ctx = GParams::from(impl_did); + let impl_ctx = GParams::from(impl_did).with_suffix("impl"); - let mut checks = Vec::new(); + let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); - // Collect the mappings from generic arguments to viper expressions that occur - // in the impl block, such that we can refer to them when encoding the trait - // bounds of the impl block. For example, for an impl like `impl Trait for - // (T, i32)`, we would map `T` to an accessor expression to the first member of - // the tuple type - `self.2_tup.0` - let mut ty_map = FxHashMap::default(); - let mut const_map = FxHashMap::default(); + let trait_ctx = GParams::from(trait_ref.def_id).with_suffix("trait"); + let trait_params = deps.require_dep::(trait_ctx).unwrap(); + let impl_params = deps.require_dep::(impl_ctx).unwrap(); - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); - let trait_rust_tys = trait_ref.args.iter().filter_map(|arg| arg.as_type()); - let trait_params = deps - .require_dep::(trait_ref.def_id.into()) + let impl_args = deps + .require_dep::(GArgs::new(impl_ctx, trait_ref.args)) .unwrap(); + let impl_ty_args = impl_args.get_ty(); + let trait_ty_params = trait_params.ty_exprs(); + let trait_const_params = trait_params.const_exprs(); - for (&ty_expr, rust_ty) in std::iter::zip(trait_params.ty_exprs(), trait_rust_tys) { - checks.extend(encode_type_check( - vcx, - deps, - &mut ty_map, - &mut const_map, - impl_ctx, - ty_expr, - rust_ty, - )); + // Collect the bindings for the generics of this impl block + let mut generics_map = GenericsMap::new(impl_ctx.count()); + collect_bindings( + deps, + &mut generics_map, + trait_ctx, + &trait_params, + trait_ref.args, + ); + + let mut checks = Vec::new(); + for (trait_ty_param, impl_ty_arg) in std::iter::zip(trait_ty_params, impl_ty_args) { + checks.push(vcx.mk_eq_expr(trait_ty_param, impl_ty_arg)); } let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); - // Process the projection predicates first as they might introduce new bindings - // for generic parameters - let projection_preds = caller_bounds - .iter() - .filter_map(ty::Clause::as_projection_clause) - .map(ty::Binder::skip_binder); - - checks.extend(process_projection_predicates( - vcx, - deps, - &mut ty_map, - &mut const_map, - impl_ctx, - projection_preds, - )); - - // Process trait predicates last as they cannot introduce new bindings for - // generics let trait_preds = caller_bounds .iter() .filter_map(ty::Clause::as_trait_clause) .map(ty::Binder::skip_binder); - checks.push(process_trait_predicates( - vcx, - deps, - &ty_map, - &const_map, - impl_ctx, - trait_preds, - )); - - vcx.mk_conj(&checks) -} - -/// Encode a check that the type expression `expr` is the same as the rust type `ty`. -/// Additionally, collect the generic parameters of the impl block and map them to -/// their occurances in the type expression, such that they can be referred to when -/// encoding the trait bounds of the impl block. -/// -/// For example, for `expr` equal to `(T, i32)`, `T` would be mapped to an accessor -/// expression to the first member of the tuple type - `expr.2_tup.0` -fn encode_type_check<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, - deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - ty_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, - const_map: &mut FxHashMap, vir::ExprSnap<'vir>>, - ctx: GParams<'vir>, - expr: vir::ExprTyVal<'vir>, - ty: ty::Ty<'vir>, -) -> Option> { - let decomp = RustTyDecomposition::from_ty(ty, ctx); + for trait_pred in trait_preds { + let trait_did = trait_pred.def_id(); + let trait_ = deps.require_ref::(trait_did).unwrap(); + let gargs = GArgs::new(impl_ctx, trait_pred.trait_ref.args); + let gargs = deps.require_dep::(gargs).unwrap(); + + let impl_check = (trait_.impl_fun)(gargs.get_ty(), gargs.get_const()); + dbg!(&impl_check); + checks.push(impl_check); + } - if decomp.ty.specifics.is_param() { - let arg = decomp.args.args().first().expect("Param missing arg"); + let proj_preds = caller_bounds + .iter() + .filter_map(ty::Clause::as_projection_clause) + .map(ty::Binder::skip_binder); - use std::collections::hash_map::Entry; - return match ty_map.entry(*arg) { - Entry::Occupied(occ) => { - // Already seen this T: ensure this type matches the originally found - Some(vcx.mk_eq_expr(expr, *occ.get())) + let proj_preds = order_projections(generics_map.keys(), proj_preds, impl_ctx.count()); + + for proj_pred in proj_preds { + let trait_did = proj_pred.trait_def_id(tcx); + let trait_ = deps.require_ref::(trait_did).unwrap(); + let gargs = GArgs::new(impl_ctx, proj_pred.projection_term.args); + let gargs = deps.require_dep::(gargs).unwrap(); + + let (projection, expr): (vir::ExprDyn, vir::ExprDyn) = match proj_pred.term.kind() { + ty::TermKind::Ty(ty) => { + let projection = + trait_.fns.assoc_types[&proj_pred.def_id()](gargs.get_ty(), gargs.get_const()); + let decomp = RustTyDecomposition::from_ty(ty, impl_ctx); + let ty_expr = impl_params.ty_expr(deps, decomp); + discover_bind_points( + deps, + &mut generics_map, + impl_ctx, + projection.upcast_ty(), + ty, + ); + (projection.upcast_ty(), ty_expr.upcast_ty()) } - Entry::Vacant(vac) => { - // First time seeing T: map it to the current accessor path for future references - vac.insert(expr); - None + ty::TermKind::Const(const_) => { + let projection = + trait_.fns.assoc_consts[&proj_pred.def_id()](gargs.get_ty(), gargs.get_const()); + let ty = tcx.type_of(proj_pred.def_id()).instantiate_identity(); + let const_task = ConstEncTask::Ty { + const_, + ty, + context: impl_ctx, + }; + let const_expr = deps.require_dep::(const_task).unwrap(); + if let ty::ConstKind::Param(p) = const_.kind() { + generics_map.try_insert(p.index, const_expr.upcast_ty()); + } + (projection.upcast_ty(), const_expr.upcast_ty()) } }; - } - - let ty_enc = deps.require_ref::(decomp.ty).unwrap(); - - let discr_check = vcx.mk_adt_discriminator_expr(expr, ty_enc.ty_constructor.name().to_str()); - - let mut conjuncts = vec![discr_check]; - - let args = decomp.args.args(); - - // Collect checks for inner types - let inner_types = args.iter().filter_map(|arg| arg.as_type()); - for (i, inner_ty) in inner_types.enumerate() { - let accessor = ty_enc.ty_param_accessors[i]; - let expr = accessor.call()(expr); - conjuncts.extend(encode_type_check( - vcx, deps, ty_map, const_map, ctx, expr, inner_ty, - )); + let projection_check = vcx.mk_eq_expr(projection, expr); + dbg!(&projection_check); + checks.push(projection_check); } - // Collect the "locations" of const parameters and assert equality for repeated occurances - let consts = args.iter().filter_map(|arg| arg.as_const()); - for (i, const_) in consts.enumerate() { - let accessor = ty_enc.const_param_accessors[i]; - let expr = accessor.call()(expr); - - conjuncts.extend(encode_const_check( - vcx, - deps, - const_map, - ctx, - expr.upcast_ty(), - const_, - )); - } + dbg!(&checks); + dbg!(&generics_map); - Some(vcx.mk_conj(&conjuncts)) -} + let checks = vcx.mk_conj(&checks); -/// Encode a check that the expression `expr` is the same as the rust const `const_`. -fn encode_const_check<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, - deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - const_map: &mut FxHashMap, vir::ExprSnap<'vir>>, - ctx: GParams<'vir>, - expr: vir::ExprSnap<'vir>, - const_: ty::Const<'vir>, -) -> Option> { - match const_.kind() { - ty::ConstKind::Param(..) => { - use std::collections::hash_map::Entry; - match const_map.entry(const_.into()) { - Entry::Occupied(occ) => { - // Already seen this const parameter: ensure this const expression matches the originally found - Some(vcx.mk_eq_expr(expr, *occ.get())) - } - Entry::Vacant(vac) => { - // First time seeing this const parameter: map it to the current accessor path for future references - vac.insert(expr); - None - } - } - } - ty::ConstKind::Value(val) => { - let task = ConstEncTask::Ty { - const_, - ty: val.ty, - context: ctx, - }; - let value = deps.require_dep::(task).unwrap(); - Some(vcx.mk_eq_expr(expr, value.upcast_ty())) - } - _ => unimplemented!("other kinds of const parameters not supported yet"), - } + dbg!( + generics_map + .insertion_ordered() + .rfold(checks, |acc, (idx, expr)| { + let idx = impl_params.map_idx(idx); + let decl = match idx { + Result::Ok(idx) => impl_params.ty_decls()[idx].upcast_ty(), + Result::Err(idx) => impl_params.const_decls()[idx].upcast_ty(), + }; + vcx.mk_let_expr(decl, expr, acc) + }) + ) } -/// Assemble a VIR type using the map of generic parameters we have collected earlier. -fn assemble_type<'vir>( - tcx: ty::TyCtxt<'vir>, +fn collect_bindings<'vir>( deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - ty_map: &FxHashMap, vir::ExprTyVal<'vir>>, - const_map: &FxHashMap, vir::ExprSnap<'vir>>, - ctx: GParams<'vir>, - ty: ty::Ty<'vir>, -) -> vir::ExprTyVal<'vir> { - let decomp = RustTyDecomposition::from_ty(ty, ctx); - - if decomp.ty.specifics.is_param() { - let arg = decomp.args.args().first().expect("Param missing arg"); - return match arg.expect_ty().kind() { - ty::TyKind::Param(param) => ty_map - .get(arg) - .unwrap_or_else(|| panic!("generic {param:?} to be mapped")), - ty::TyKind::Alias(ty::AliasTyKind::Projection, alias) => { - let trait_did = tcx.parent(alias.def_id); - let trait_ = deps.require_ref::(trait_did).unwrap(); - - let assoc_ty_fun = trait_ - .fns - .assoc_types - .get(&alias.def_id) - .expect("associated type to be in the mapping"); - - let gargs = GArgs::new(trait_did, alias.args); - let gargs = deps.require_dep::(gargs).unwrap(); - assoc_ty_fun(gargs.get_ty(), gargs.get_const()) - } - _ => unimplemented!("unsupported kind of generic parameter in type position"), - }; + generics_map: &mut GenericsMap<'vir>, + trait_ctx: GParams<'vir>, + trait_params: &GenericParams<'vir>, + trait_args: ty::GenericArgsRef<'vir>, +) { + // Walk the trait type generic arguments + let impl_rust_tys = trait_args.iter().filter_map(|arg| arg.as_type()); + for (ty_arg, rust_ty) in std::iter::zip(trait_params.ty_exprs(), impl_rust_tys) { + discover_bind_points(deps, generics_map, trait_ctx, ty_arg.upcast_ty(), rust_ty); } - let ty_enc = deps.require_ref::(decomp.ty).unwrap(); - - let args = decomp.args.args(); - - let inner_ty_args = args - .iter() - .filter_map(|arg| arg.as_type()) - .map(|inner_ty| assemble_type(tcx, deps, ty_map, const_map, ctx, inner_ty)) - .collect::>(); - - let inner_const_args = args - .iter() - .filter_map(|arg| arg.as_const()) - .map(|const_| assemble_const(deps, ctx, const_map, const_).downcast_ty()) - .collect::>(); - - (ty_enc.ty_constructor)(&inner_ty_args, &inner_const_args) -} - -fn assemble_const<'vir>( - deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - ctx: GParams<'vir>, - const_map: &FxHashMap, vir::ExprSnap<'vir>>, - const_: ty::Const<'vir>, -) -> vir::ExprSnap<'vir> { - match const_.kind() { - ty::ConstKind::Param(..) => const_map - .get(&const_.into()) - .copied() - .expect("The const generic should have been bound in the map"), - ty::ConstKind::Value(val) => { - let task = ConstEncTask::Ty { - const_, - ty: val.ty, - context: ctx, - }; - deps.require_dep::(task).unwrap().upcast_ty() + // Walk the trait const generic arguments + let impl_rust_consts = trait_args.iter().filter_map(|arg| arg.as_const()); + for (const_arg, rust_const) in std::iter::zip(trait_params.const_exprs(), impl_rust_consts) { + if let ty::ConstKind::Param(p) = rust_const.kind() { + generics_map.try_insert(p.index, const_arg.upcast_ty()); } - _ => unimplemented!("other kinds of const parameters not supported yet"), } } -/// Process the projection predicates in a topological order, such that when processing a -/// projection predicate, the projection term is already mapped to a VIR expression. This is needed -/// to handle cases like `T::Item: Trait` where we need to refer to `T::Item` when encoding the -/// trait bound check for `Trait`. -fn process_projection_predicates<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, - deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - ty_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, - const_map: &mut FxHashMap, vir::ExprSnap<'vir>>, - ctx: GParams<'vir>, - projections: impl Iterator>, -) -> Option> { - let mut worklist: VecDeque<_> = projections.collect(); - let mut conjuncts = Vec::new(); - while let Some(proj) = worklist.pop_front() { - if is_alias_ready(&proj.projection_term, ty_map, const_map) { - conjuncts.extend(process_projection(vcx, deps, ty_map, const_map, ctx, proj)); - } else { - worklist.push_back(proj); - } - } - if conjuncts.is_empty() { - None - } else { - Some(vcx.mk_conj(&conjuncts)) - } +#[derive(Clone, Debug)] +struct GenericsMap<'vir> { + present: usize, + map: Vec)>>, } -fn process_projection<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, - deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - ty_map: &mut FxHashMap, vir::ExprTyVal<'vir>>, - const_map: &mut FxHashMap, vir::ExprSnap<'vir>>, - ctx: GParams<'vir>, - projection: ty::ProjectionPredicate<'vir>, -) -> Option> { - let tcx = vcx.tcx(); - let proj_did = projection.def_id(); - let trait_did = projection.trait_def_id(tcx); - let trait_ = deps.require_ref::(trait_did).unwrap(); - - let proj_args = projection.projection_term.args; - - let proj_ty_args: Vec<_> = proj_args - .iter() - .filter_map(|arg| arg.as_type()) - .map(|ty| assemble_type(tcx, deps, ty_map, const_map, ctx, ty)) - .collect(); - let proj_const_args: Vec<_> = proj_args - .iter() - .filter_map(|arg| arg.as_const()) - .map(|const_| assemble_const(deps, ctx, const_map, const_).downcast_ty()) - .collect(); - - match projection.term.kind() { - ty::TermKind::Ty(tgt_ty) => { - let projection_fun = trait_ - .fns - .assoc_types - .get(&proj_did) - .expect("Projection did should be in the mapping"); - - let projection = projection_fun(&proj_ty_args, &proj_const_args); - - encode_type_check(vcx, deps, ty_map, const_map, ctx, projection, tgt_ty) - } - ty::TermKind::Const(const_) => { - let projection_fun = trait_ - .fns - .assoc_consts - .get(&proj_did) - .expect("Projection did should be in the mapping"); - let projection = projection_fun(&proj_ty_args, &proj_const_args); - - encode_const_check(vcx, deps, const_map, ctx, projection, const_) +impl<'vir> GenericsMap<'vir> { + fn new(size: usize) -> Self { + GenericsMap { + present: 0, + map: vec![None; size], } } -} - -/// Check whether all generic parameters of the given alias term have alredy been mapped -fn is_alias_ready<'vir>( - term: &ty::AliasTerm<'vir>, - ty_map: &FxHashMap, vir::ExprTyVal<'vir>>, - const_map: &FxHashMap, vir::ExprSnap<'vir>>, -) -> bool { - for arg in term.args { - for arg in arg.walk() { - match arg.kind() { - ty::GenericArgKind::Type(ty) => { - if let ty::TyKind::Param(_) = ty.kind() - && !ty_map.contains_key(&arg) - { - return false; - } - } - ty::GenericArgKind::Const(_) => { - if !const_map.contains_key(&arg) { - return false; - } - } - _ => {} - } + fn try_insert(&mut self, idx: u32, expr: vir::ExprDyn<'vir>) -> bool { + let idx = idx as usize; + if self.map[idx].is_some() { + return false; } + self.map[idx] = Some((self.present, expr)); + self.present += 1; + true } - true -} - -fn process_trait_predicates<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, - deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - ty_map: &FxHashMap, vir::ExprTyVal<'vir>>, - const_map: &FxHashMap, vir::ExprSnap<'vir>>, - ctx: GParams<'vir>, - trait_preds: impl Iterator>, -) -> vir::ExprBool<'vir> { - let tcx = vcx.tcx(); - let mut conjuncts = Vec::new(); - for trait_pred in trait_preds { - let required_trait_impl_fun = deps - .require_ref::(trait_pred.def_id()) - .unwrap() - .impl_fun; - let trait_args = trait_pred.trait_ref.args; - let ty_args: Vec<_> = trait_args + fn insertion_ordered( + &self, + ) -> impl Iterator)> + DoubleEndedIterator { + let mut ordered = self + .map .iter() - .filter_map(|arg| arg.as_type()) - .map(|arg| assemble_type(tcx, deps, ty_map, const_map, ctx, arg)) - .collect(); + .enumerate() + .filter_map(|(idx, opt)| opt.map(|(order, expr)| (order, (idx as u32, expr)))) + .collect::>(); + ordered.sort_by_key(|(order, _)| *order); + ordered.into_iter().map(|(_, (idx, expr))| (idx, expr)) + } - let const_args: Vec<_> = trait_args + fn keys(&self) -> impl Iterator { + self.map .iter() - .filter_map(|arg| arg.as_const()) - .map(|const_| assemble_const(deps, ctx, const_map, const_).downcast_ty()) - .collect(); - - conjuncts.push(required_trait_impl_fun(&ty_args, &const_args)); + .enumerate() + .filter_map(|(idx, opt)| opt.as_ref().map(|_| idx as u32)) } - vcx.mk_conj(&conjuncts) } diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index 38fd7417d53..fbfa0d0c396 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -44,7 +44,8 @@ impl TaskEncoder for TraitEnc { ) -> EncodeFullResult<'vir, Self> { vir::with_vcx(|vcx| { let tcx = vcx.tcx(); - let params = deps.require_dep::(GParams::from(*task_key))?; + let params = deps + .require_dep::(GParams::from(*task_key).with_suffix("trait"))?; let trait_name = vcx.alloc_str(tcx.item_name(task_key).as_str()); let trait_items = tcx.associated_items(task_key).in_definition_order(); From f42b721e9155bbd36d27ddbc6c42648fa526eb5d Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 4 Mar 2026 18:01:29 +0100 Subject: [PATCH 53/70] Clean up trait impl check --- .../src/encoders/ty/generics/trait_impls.rs | 150 ++++++++---------- .../src/encoders/ty/generics/traits.rs | 7 +- 2 files changed, 71 insertions(+), 86 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 5daca388c75..16d00dd3750 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -9,7 +9,7 @@ use crate::encoders::{ r#const::ConstEncTask, ty::{ RustTyDecomposition, - generics::{GArgs, GArgsTyEnc, GParams, GenericParams, GenericParamsEnc, traits::TraitEnc}, + generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc, traits::TraitEnc}, lifted::TyConstructorEnc, }, }; @@ -130,7 +130,8 @@ impl TaskEncoder for TraitImplEnc { } } - let impl_condition = impl_block_condition(vcx, deps, *task_key); + let trait_ref = tcx.impl_trait_ref(task_key).unwrap().instantiate_identity(); + let impl_condition = impl_block_check(vcx, deps, GParams::from(*task_key), trait_ref); Ok(( vcx.mk_domain( @@ -209,13 +210,11 @@ fn discover_bind_points<'vir>( deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, generic_map: &mut GenericsMap<'vir>, ctx: GParams<'vir>, - expr: vir::ExprDyn<'vir>, + expr: vir::ExprTyVal<'vir>, ty: ty::Ty<'vir>, ) { - dbg!(&ty); - if let ty::TyKind::Param(p) = ty.kind() { - generic_map.try_insert(p.index, expr); + generic_map.try_insert(p.index, expr.upcast_ty()); return; } @@ -226,15 +225,15 @@ fn discover_bind_points<'vir>( let inner_types = args.iter().filter_map(|arg| arg.as_type()); for (i, inner_ty) in inner_types.enumerate() { let accessor = ty_enc.ty_param_accessors[i]; - let inner_expr = accessor.call()(expr.downcast_ty()); + let inner_expr = accessor.call()(expr); - discover_bind_points(deps, generic_map, ctx, inner_expr.upcast_ty(), inner_ty); + discover_bind_points(deps, generic_map, ctx, inner_expr, inner_ty); } let inner_consts = args.iter().filter_map(|arg| arg.as_const()); for (i, inner_const) in inner_consts.enumerate() { let accessor = ty_enc.const_param_accessors[i]; - let inner_expr = accessor.call()(expr.downcast_ty()); + let inner_expr = accessor.call()(expr); if let ty::ConstKind::Param(p) = inner_const.kind() { generic_map.try_insert(p.index, inner_expr.upcast_ty()); @@ -242,49 +241,60 @@ fn discover_bind_points<'vir>( } } -fn impl_block_condition<'vir>( +fn impl_block_check<'vir>( vcx: &'vir vir::VirCtxt<'vir>, deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - impl_did: DefId, + impl_ctx: GParams<'vir>, + trait_ref: ty::TraitRef<'vir>, ) -> vir::ExprBool<'vir> { let tcx = vcx.tcx(); - let impl_ctx = GParams::from(impl_did).with_suffix("impl"); - - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity(); + let impl_ctx = impl_ctx.with_suffix("impl"); + let impl_params = deps.require_dep::(impl_ctx).unwrap(); - let trait_ctx = GParams::from(trait_ref.def_id).with_suffix("trait"); + let trait_ctx = TraitEnc::trait_gparams(trait_ref.def_id); let trait_params = deps.require_dep::(trait_ctx).unwrap(); - let impl_params = deps.require_dep::(impl_ctx).unwrap(); - let impl_args = deps + let args = deps .require_dep::(GArgs::new(impl_ctx, trait_ref.args)) .unwrap(); - let impl_ty_args = impl_args.get_ty(); - let trait_ty_params = trait_params.ty_exprs(); - let trait_const_params = trait_params.const_exprs(); + + let generics_count = impl_ctx.rust_params().len(); // Collect the bindings for the generics of this impl block - let mut generics_map = GenericsMap::new(impl_ctx.count()); - collect_bindings( - deps, - &mut generics_map, - trait_ctx, - &trait_params, - trait_ref.args, - ); + let mut generics_map = GenericsMap::new(generics_count); + + // Walk the trait type generic arguments + let impl_rust_tys = trait_ref.args.iter().filter_map(|arg| arg.as_type()); + for (ty_arg, rust_ty) in std::iter::zip(trait_params.ty_exprs(), impl_rust_tys) { + discover_bind_points(deps, &mut generics_map, trait_ctx, ty_arg, rust_ty); + } + + // Walk the trait const generic arguments + let impl_rust_consts = trait_ref.args.iter().filter_map(|arg| arg.as_const()); + for (const_arg, rust_const) in std::iter::zip(trait_params.const_exprs(), impl_rust_consts) { + if let ty::ConstKind::Param(p) = rust_const.kind() { + generics_map.try_insert(p.index, const_arg.upcast_ty()); + } + } let mut checks = Vec::new(); - for (trait_ty_param, impl_ty_arg) in std::iter::zip(trait_ty_params, impl_ty_args) { - checks.push(vcx.mk_eq_expr(trait_ty_param, impl_ty_arg)); + // Collect checks for the generics of the trait and their corresponding arguments in the impl + for (trait_ty_param, ty_args) in std::iter::zip(trait_params.ty_exprs(), args.get_ty()) { + checks.push(vcx.mk_eq_expr(trait_ty_param, ty_args)); + } + for (trait_const_param, const_args) in + std::iter::zip(trait_params.const_exprs(), args.get_const()) + { + checks.push(vcx.mk_eq_expr(trait_const_param, const_args)); } let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); + // Collect checks for the trait bounds let trait_preds = caller_bounds .iter() .filter_map(ty::Clause::as_trait_clause) .map(ty::Binder::skip_binder); - for trait_pred in trait_preds { let trait_did = trait_pred.def_id(); let trait_ = deps.require_ref::(trait_did).unwrap(); @@ -292,17 +302,16 @@ fn impl_block_condition<'vir>( let gargs = deps.require_dep::(gargs).unwrap(); let impl_check = (trait_.impl_fun)(gargs.get_ty(), gargs.get_const()); - dbg!(&impl_check); checks.push(impl_check); } + // Collect checks for the projection predicates. These have to be processed in a way such that + // any bindpoints they introduce are introduced with the let-bindings in the correct order. let proj_preds = caller_bounds .iter() .filter_map(ty::Clause::as_projection_clause) .map(ty::Binder::skip_binder); - - let proj_preds = order_projections(generics_map.keys(), proj_preds, impl_ctx.count()); - + let proj_preds = order_projections(generics_map.keys(), proj_preds, generics_count); for proj_pred in proj_preds { let trait_did = proj_pred.trait_def_id(tcx); let trait_ = deps.require_ref::(trait_did).unwrap(); @@ -315,13 +324,7 @@ fn impl_block_condition<'vir>( trait_.fns.assoc_types[&proj_pred.def_id()](gargs.get_ty(), gargs.get_const()); let decomp = RustTyDecomposition::from_ty(ty, impl_ctx); let ty_expr = impl_params.ty_expr(deps, decomp); - discover_bind_points( - deps, - &mut generics_map, - impl_ctx, - projection.upcast_ty(), - ty, - ); + discover_bind_points(deps, &mut generics_map, impl_ctx, projection, ty); (projection.upcast_ty(), ty_expr.upcast_ty()) } ty::TermKind::Const(const_) => { @@ -342,74 +345,52 @@ fn impl_block_condition<'vir>( }; let projection_check = vcx.mk_eq_expr(projection, expr); - dbg!(&projection_check); checks.push(projection_check); } - dbg!(&checks); - dbg!(&generics_map); - let checks = vcx.mk_conj(&checks); - dbg!( - generics_map - .insertion_ordered() - .rfold(checks, |acc, (idx, expr)| { - let idx = impl_params.map_idx(idx); - let decl = match idx { - Result::Ok(idx) => impl_params.ty_decls()[idx].upcast_ty(), - Result::Err(idx) => impl_params.const_decls()[idx].upcast_ty(), - }; - vcx.mk_let_expr(decl, expr, acc) - }) - ) -} - -fn collect_bindings<'vir>( - deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - generics_map: &mut GenericsMap<'vir>, - trait_ctx: GParams<'vir>, - trait_params: &GenericParams<'vir>, - trait_args: ty::GenericArgsRef<'vir>, -) { - // Walk the trait type generic arguments - let impl_rust_tys = trait_args.iter().filter_map(|arg| arg.as_type()); - for (ty_arg, rust_ty) in std::iter::zip(trait_params.ty_exprs(), impl_rust_tys) { - discover_bind_points(deps, generics_map, trait_ctx, ty_arg.upcast_ty(), rust_ty); - } - - // Walk the trait const generic arguments - let impl_rust_consts = trait_args.iter().filter_map(|arg| arg.as_const()); - for (const_arg, rust_const) in std::iter::zip(trait_params.const_exprs(), impl_rust_consts) { - if let ty::ConstKind::Param(p) = rust_const.kind() { - generics_map.try_insert(p.index, const_arg.upcast_ty()); - } - } + generics_map + .insertion_ordered() + .rfold(checks, |acc, (idx, expr)| { + let idx = impl_params.map_idx(idx); + let decl = match idx { + Result::Ok(idx) => impl_params.ty_decls()[idx].upcast_ty(), + Result::Err(idx) => impl_params.const_decls()[idx].upcast_ty(), + }; + vcx.mk_let_expr(decl, expr, acc) + }) } +/// Collects bindings for generics in the order they are discovered. #[derive(Clone, Debug)] struct GenericsMap<'vir> { - present: usize, + order: usize, map: Vec)>>, } impl<'vir> GenericsMap<'vir> { fn new(size: usize) -> Self { GenericsMap { - present: 0, + order: 0, map: vec![None; size], } } + + /// Insert the given expression as the binding for the `idx`-th generic, while recording the + /// insertion order. Returns `false` if the generic already has a binding, and `true` + /// otherwise. fn try_insert(&mut self, idx: u32, expr: vir::ExprDyn<'vir>) -> bool { let idx = idx as usize; if self.map[idx].is_some() { return false; } - self.map[idx] = Some((self.present, expr)); - self.present += 1; + self.map[idx] = Some((self.order, expr)); + self.order += 1; true } + /// Collect the bindings in their insertion order. fn insertion_ordered( &self, ) -> impl Iterator)> + DoubleEndedIterator { @@ -423,6 +404,7 @@ impl<'vir> GenericsMap<'vir> { ordered.into_iter().map(|(_, (idx, expr))| (idx, expr)) } + /// Already present generics. fn keys(&self) -> impl Iterator { self.map .iter() diff --git a/prusti-encoder/src/encoders/ty/generics/traits.rs b/prusti-encoder/src/encoders/ty/generics/traits.rs index fbfa0d0c396..1e382f04f0e 100644 --- a/prusti-encoder/src/encoders/ty/generics/traits.rs +++ b/prusti-encoder/src/encoders/ty/generics/traits.rs @@ -44,8 +44,7 @@ impl TaskEncoder for TraitEnc { ) -> EncodeFullResult<'vir, Self> { vir::with_vcx(|vcx| { let tcx = vcx.tcx(); - let params = deps - .require_dep::(GParams::from(*task_key).with_suffix("trait"))?; + let params = deps.require_dep::(Self::trait_gparams(*task_key))?; let trait_name = vcx.alloc_str(tcx.item_name(task_key).as_str()); let trait_items = tcx.associated_items(task_key).in_definition_order(); @@ -246,4 +245,8 @@ impl TraitEnc { ) -> vir::ViperIdent<'vir> { vir_format_identifier!(vcx, "t_{trait_name}") } + + pub(super) fn trait_gparams<'tcx>(trait_did: DefId) -> GParams<'tcx> { + GParams::from(trait_did).with_suffix("trait") + } } From acb6357182c142d7163735525494e29d788693da Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 4 Mar 2026 23:34:00 +0100 Subject: [PATCH 54/70] Simplify encoding of Sized --- .../src/encoders/ty/generics/sized_trait.rs | 143 ++----- .../src/encoders/ty/generics/trait_impls.rs | 379 +++++++++--------- prusti-encoder/src/encoders/ty/rust_ty.rs | 6 +- 3 files changed, 242 insertions(+), 286 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index dca9c5262e3..d5d71df2b89 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -2,12 +2,11 @@ use crate::{ TaskEncoder, encoders::ty::{ RustTy, Sizedness, - generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc, traits::TraitEnc}, - lifted::{TyConstructorEnc, ty_constructor::TyConstructorEncOutputRef}, + generics::{GParams, trait_impls::TraitImplEnc, traits::TraitEnc}, + lifted::TyConstructorEnc, }, }; -use prusti_rustc_interface::middle::ty; -use vir::CallableIdn; +use prusti_rustc_interface::middle::{ty, ty::Upcast}; pub struct SizedTraitEnc; @@ -32,34 +31,23 @@ impl TaskEncoder for SizedTraitEnc { assert!(!task_key.specifics.is_param()); deps.emit_output_ref(*task_key, ())?; - let vpr_type = deps.require_ref::(*task_key)?; - let discriminator = vpr_type.ty_constructor.name().to_str(); - vir::with_vcx(|vcx| { - let self_expr = vcx.mk_local_ex(Self::sized_self_decl(vcx)); - - let is_this_type = vcx.mk_adt_discriminator_expr(self_expr, discriminator); - - let sized_impl_idn = TraitEnc::trait_impl_idn(vcx, SIZED_TRAIT_NAME, SIZED_ARGS); - let check = match task_key.sizedness { - Sizedness::Sized => Some(is_this_type), Sizedness::Unsized => None, - Sizedness::Dependent(dep_ty) => Some( - Self::sizedness_for_dependent( - vcx, - deps, - sized_impl_idn, - vpr_type, - task_key.params, - self_expr, - dep_ty, - ) - .map_or( - is_this_type, - |extra_check| vir::expr! {vcx; (is_this_type) && (extra_check) }, - ), - ), + Sizedness::Sized => Some(Self::sizedness_check( + vcx, + deps, + task_key.params, + task_key.erased_ty.unwrap(), + None, + )), + Sizedness::Dependent(dep_ty) => Some(Self::sizedness_check( + vcx, + deps, + task_key.params, + task_key.erased_ty.unwrap(), + Some(dep_ty), + )), }; Ok((check, ())) @@ -119,79 +107,36 @@ impl TaskEncoder for SizedTraitEnc { impl SizedTraitEnc { fn sized_self_decl<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::LocalDecl<'vir, vir::TyVal> { - vcx.mk_local_decl("Self$0", vir::TYPE_TYVAL) + vcx.mk_local_decl("Self$0_trait", vir::TYPE_TYVAL) } - /// Check if we need an extra sizedness check as a result of the sizedness dependency - fn sizedness_for_dependent<'vir>( + fn sizedness_check<'vir>( vcx: &'vir vir::VirCtxt<'vir>, deps: &mut task_encoder::TaskEncoderDependencies<'vir, SizedTraitEnc>, - sized_impl_idn: vir::FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, - vpr_type: TyConstructorEncOutputRef<'vir>, - ty_ctx: GParams<'vir>, - self_expr: vir::Expr<'vir, vir::TyVal>, - depneded_on: ty::Ty<'vir>, - ) -> Option> { - match depneded_on.kind() { - ty::TyKind::Param(param) => { - let accessor = vpr_type.ty_param_accessors[param.index as usize]; - let param_ty = accessor.call()(self_expr); - // We know that in reality `Sized` only has the `Self` type parameter - let inner_sized_check = sized_impl_idn.call()(&[param_ty], &[]); - Some(inner_sized_check) - } - ty::TyKind::Alias(ty::AliasTyKind::Projection, alias_ty) => { - let tcx = vcx.tcx(); - - let is_forced_sized = tcx.item_bounds(alias_ty.def_id) - .instantiate_identity() - .iter() - .any(|clause| { - matches!(clause.kind().skip_binder(), - ty::ClauseKind::Trait(p) if Some(p.def_id()) == tcx.lang_items().sized_trait() - ) - }); - - if is_forced_sized { - // This projection is forced to be `Sized` by its own bounds, so we don't need an - // extra check - return None; - } - - let trait_def = alias_ty.trait_def_id(tcx); - let trait_ = deps.require_ref::(trait_def).unwrap(); - let projection_fun = trait_.fns.assoc_types[&alias_ty.def_id]; - - let ty_params = deps.require_dep::(ty_ctx).unwrap(); - let args = deps - .require_dep::(GArgs::new(ty_ctx, alias_ty.args)) - .unwrap(); - - let projection = projection_fun(args.get_ty(), args.get_const()); - - let inner_sized_check = sized_impl_idn.call()(&[projection], &[]); - - // Introduce let-bindings for the generics of the type - // NOTE: There won't be any name collisions as user defined ADTs cannot - // have a generic called `Self` - let with_consts_bound = ty_params.const_decls().iter().enumerate().rfold( - inner_sized_check, - |expr, (i, decl)| { - let accessor = vpr_type.const_param_accessors[i]; - vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) - }, - ); - let with_tys_bound = ty_params.ty_decls().iter().enumerate().rfold( - with_consts_bound, - |expr, (i, decl)| { - let accessor = vpr_type.ty_param_accessors[i]; - vcx.mk_let_expr(decl, accessor.call()(self_expr), expr) - }, - ); - - Some(with_tys_bound) - } - _ => panic!("Unsupported dependent sizedness for {depneded_on:?}"), - } + impl_ctx: GParams<'vir>, + impl_ty: ty::Ty<'vir>, + depended_on: Option>, + ) -> vir::ExprBool<'vir> { + let tcx = vcx.tcx(); + + let sized_did = tcx.lang_items().sized_trait().unwrap(); + + let impls_sized = ty::TraitRef::new_from_args( + tcx, + sized_did, + tcx.mk_args_trait(impl_ty, std::iter::empty()), + ); + + let param_env = ty::ParamEnv::new( + tcx.mk_clauses( + depended_on + .map(|dep_ty| ty::TraitRef::new(tcx, sized_did, [dep_ty]).upcast(tcx)) + .as_slice(), + ), + ); + + let impl_ctx = GParams::new(impl_ctx.rust_params(), param_env, false); + + TraitImplEnc::impl_block_check(vcx, deps, impl_ctx, impls_sized) } } diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 16d00dd3750..5867ee22320 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -131,7 +131,8 @@ impl TaskEncoder for TraitImplEnc { } let trait_ref = tcx.impl_trait_ref(task_key).unwrap().instantiate_identity(); - let impl_condition = impl_block_check(vcx, deps, GParams::from(*task_key), trait_ref); + let impl_condition = + Self::impl_block_check(vcx, deps, GParams::from(*task_key), trait_ref); Ok(( vcx.mk_domain( @@ -146,220 +147,226 @@ impl TaskEncoder for TraitImplEnc { }) } } +impl TraitImplEnc { + fn bitset_from(iter: impl IntoIterator, size: usize) -> DenseBitSet { + iter.into_iter() + .fold(DenseBitSet::new_empty(size), |mut acc, idx| { + acc.insert(idx); + acc + }) + } + fn projection_deps<'vir>( + projection: ty::ProjectionPredicate<'vir>, + generics_count: usize, + ) -> (DenseBitSet, DenseBitSet) { + let generic_idx = |arg: ty::GenericArg| match arg.kind() { + ty::GenericArgKind::Type(ty) if let ty::TyKind::Param(p) = ty.kind() => Some(p.index), + ty::GenericArgKind::Const(const_) if let ty::ConstKind::Param(p) = const_.kind() => { + Some(p.index) + } + _ => None, + }; -fn bitset_from(iter: impl IntoIterator, size: usize) -> DenseBitSet { - iter.into_iter() - .fold(DenseBitSet::new_empty(size), |mut acc, idx| { - acc.insert(idx); - acc - }) -} -fn projection_deps<'vir>( - projection: ty::ProjectionPredicate<'vir>, - generics_count: usize, -) -> (DenseBitSet, DenseBitSet) { - let generic_idx = |arg: ty::GenericArg| match arg.kind() { - ty::GenericArgKind::Type(ty) if let ty::TyKind::Param(p) = ty.kind() => Some(p.index), - ty::GenericArgKind::Const(const_) if let ty::ConstKind::Param(p) = const_.kind() => { - Some(p.index) - } - _ => None, - }; - - let required = projection - .projection_term - .args - .iter() - .flat_map(|arg| arg.walk().filter_map(generic_idx)); - - let produced = projection.term.walk().filter_map(generic_idx); + let required = projection + .projection_term + .args + .iter() + .flat_map(|arg| arg.walk().filter_map(generic_idx)); - ( - bitset_from(required, generics_count), - bitset_from(produced, generics_count), - ) -} + let produced = projection.term.walk().filter_map(generic_idx); -fn order_projections<'vir>( - known_generics: impl IntoIterator, - projections: impl IntoIterator>, - generics_count: usize, -) -> Vec> { - let mut known_generics = bitset_from(known_generics, generics_count); - - let mut worklist: VecDeque<_> = projections - .into_iter() - .map(|p| (p, projection_deps(p, generics_count))) - .collect(); - - let mut ordered = Vec::new(); - - while let Some((proj, (required, produced))) = worklist.pop_front() { - if known_generics.superset(&required) { - known_generics.union(&produced); - ordered.push(proj); - } else { - worklist.push_back((proj, (required, produced))); - } + ( + Self::bitset_from(required, generics_count), + Self::bitset_from(produced, generics_count), + ) } - ordered -} + fn order_projections<'vir>( + known_generics: impl IntoIterator, + projections: impl IntoIterator>, + generics_count: usize, + ) -> Vec> { + let mut known_generics = Self::bitset_from(known_generics, generics_count); + + let mut worklist: VecDeque<_> = projections + .into_iter() + .map(|p| (p, Self::projection_deps(p, generics_count))) + .collect(); + + let mut ordered = Vec::new(); + + while let Some((proj, (required, produced))) = worklist.pop_front() { + if known_generics.superset(&required) { + known_generics.union(&produced); + ordered.push(proj); + } else { + worklist.push_back((proj, (required, produced))); + } + } -fn discover_bind_points<'vir>( - deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - generic_map: &mut GenericsMap<'vir>, - ctx: GParams<'vir>, - expr: vir::ExprTyVal<'vir>, - ty: ty::Ty<'vir>, -) { - if let ty::TyKind::Param(p) = ty.kind() { - generic_map.try_insert(p.index, expr.upcast_ty()); - return; + ordered } - let decomp = RustTyDecomposition::from_ty(ty, ctx); - let ty_enc = deps.require_ref::(decomp.ty).unwrap(); + fn discover_bind_points<'vir, E: TaskEncoder + 'vir + ?Sized>( + deps: &mut TaskEncoderDependencies<'vir, E>, + generic_map: &mut GenericsMap<'vir>, + ctx: GParams<'vir>, + expr: vir::ExprTyVal<'vir>, + ty: ty::Ty<'vir>, + ) { + if let ty::TyKind::Param(p) = ty.kind() { + generic_map.try_insert(p.index, expr.upcast_ty()); + return; + } + + let decomp = RustTyDecomposition::from_ty(ty, ctx); + let ty_enc = deps.require_ref::(decomp.ty).unwrap(); - let args = decomp.args.args(); - let inner_types = args.iter().filter_map(|arg| arg.as_type()); - for (i, inner_ty) in inner_types.enumerate() { - let accessor = ty_enc.ty_param_accessors[i]; - let inner_expr = accessor.call()(expr); + let args = decomp.args.args(); + let inner_types = args.iter().filter_map(|arg| arg.as_type()); + for (i, inner_ty) in inner_types.enumerate() { + let accessor = ty_enc.ty_param_accessors[i]; + let inner_expr = accessor.call()(expr); - discover_bind_points(deps, generic_map, ctx, inner_expr, inner_ty); - } + Self::discover_bind_points(deps, generic_map, ctx, inner_expr, inner_ty); + } - let inner_consts = args.iter().filter_map(|arg| arg.as_const()); - for (i, inner_const) in inner_consts.enumerate() { - let accessor = ty_enc.const_param_accessors[i]; - let inner_expr = accessor.call()(expr); + let inner_consts = args.iter().filter_map(|arg| arg.as_const()); + for (i, inner_const) in inner_consts.enumerate() { + let accessor = ty_enc.const_param_accessors[i]; + let inner_expr = accessor.call()(expr); - if let ty::ConstKind::Param(p) = inner_const.kind() { - generic_map.try_insert(p.index, inner_expr.upcast_ty()); + if let ty::ConstKind::Param(p) = inner_const.kind() { + generic_map.try_insert(p.index, inner_expr.upcast_ty()); + } } } -} -fn impl_block_check<'vir>( - vcx: &'vir vir::VirCtxt<'vir>, - deps: &mut TaskEncoderDependencies<'vir, TraitImplEnc>, - impl_ctx: GParams<'vir>, - trait_ref: ty::TraitRef<'vir>, -) -> vir::ExprBool<'vir> { - let tcx = vcx.tcx(); - let impl_ctx = impl_ctx.with_suffix("impl"); - let impl_params = deps.require_dep::(impl_ctx).unwrap(); + pub(super) fn impl_block_check<'vir, E: TaskEncoder + 'vir + ?Sized>( + vcx: &'vir vir::VirCtxt<'vir>, + deps: &mut TaskEncoderDependencies<'vir, E>, + impl_ctx: GParams<'vir>, + trait_ref: ty::TraitRef<'vir>, + ) -> vir::ExprBool<'vir> { + let tcx = vcx.tcx(); + let impl_ctx = impl_ctx.with_suffix("impl"); + let impl_params = deps.require_dep::(impl_ctx).unwrap(); - let trait_ctx = TraitEnc::trait_gparams(trait_ref.def_id); - let trait_params = deps.require_dep::(trait_ctx).unwrap(); + let trait_ctx = TraitEnc::trait_gparams(trait_ref.def_id); + let trait_params = deps.require_dep::(trait_ctx).unwrap(); - let args = deps - .require_dep::(GArgs::new(impl_ctx, trait_ref.args)) - .unwrap(); + let args = deps + .require_dep::(GArgs::new(impl_ctx, trait_ref.args)) + .unwrap(); - let generics_count = impl_ctx.rust_params().len(); + let generics_count = impl_ctx.rust_params().len(); - // Collect the bindings for the generics of this impl block - let mut generics_map = GenericsMap::new(generics_count); + // Collect the bindings for the generics of this impl block + let mut generics_map = GenericsMap::new(generics_count); - // Walk the trait type generic arguments - let impl_rust_tys = trait_ref.args.iter().filter_map(|arg| arg.as_type()); - for (ty_arg, rust_ty) in std::iter::zip(trait_params.ty_exprs(), impl_rust_tys) { - discover_bind_points(deps, &mut generics_map, trait_ctx, ty_arg, rust_ty); - } + // Walk the trait type generic arguments + let impl_rust_tys = trait_ref.args.iter().filter_map(|arg| arg.as_type()); + for (ty_arg, rust_ty) in std::iter::zip(trait_params.ty_exprs(), impl_rust_tys) { + Self::discover_bind_points(deps, &mut generics_map, trait_ctx, ty_arg, rust_ty); + } + + // Walk the trait const generic arguments + let impl_rust_consts = trait_ref.args.iter().filter_map(|arg| arg.as_const()); + for (const_arg, rust_const) in std::iter::zip(trait_params.const_exprs(), impl_rust_consts) + { + if let ty::ConstKind::Param(p) = rust_const.kind() { + generics_map.try_insert(p.index, const_arg.upcast_ty()); + } + } - // Walk the trait const generic arguments - let impl_rust_consts = trait_ref.args.iter().filter_map(|arg| arg.as_const()); - for (const_arg, rust_const) in std::iter::zip(trait_params.const_exprs(), impl_rust_consts) { - if let ty::ConstKind::Param(p) = rust_const.kind() { - generics_map.try_insert(p.index, const_arg.upcast_ty()); + let mut checks = Vec::new(); + // Collect checks for the generics of the trait and their corresponding arguments in the impl + for (trait_ty_param, ty_args) in std::iter::zip(trait_params.ty_exprs(), args.get_ty()) { + checks.push(vcx.mk_eq_expr(trait_ty_param, ty_args)); + } + for (trait_const_param, const_args) in + std::iter::zip(trait_params.const_exprs(), args.get_const()) + { + checks.push(vcx.mk_eq_expr(trait_const_param, const_args)); } - } - let mut checks = Vec::new(); - // Collect checks for the generics of the trait and their corresponding arguments in the impl - for (trait_ty_param, ty_args) in std::iter::zip(trait_params.ty_exprs(), args.get_ty()) { - checks.push(vcx.mk_eq_expr(trait_ty_param, ty_args)); - } - for (trait_const_param, const_args) in - std::iter::zip(trait_params.const_exprs(), args.get_const()) - { - checks.push(vcx.mk_eq_expr(trait_const_param, const_args)); - } + let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); - let caller_bounds = impl_ctx.typing_env().param_env.caller_bounds(); - - // Collect checks for the trait bounds - let trait_preds = caller_bounds - .iter() - .filter_map(ty::Clause::as_trait_clause) - .map(ty::Binder::skip_binder); - for trait_pred in trait_preds { - let trait_did = trait_pred.def_id(); - let trait_ = deps.require_ref::(trait_did).unwrap(); - let gargs = GArgs::new(impl_ctx, trait_pred.trait_ref.args); - let gargs = deps.require_dep::(gargs).unwrap(); - - let impl_check = (trait_.impl_fun)(gargs.get_ty(), gargs.get_const()); - checks.push(impl_check); - } + // Collect checks for the trait bounds + let trait_preds = caller_bounds + .iter() + .filter_map(ty::Clause::as_trait_clause) + .map(ty::Binder::skip_binder); + for trait_pred in trait_preds { + let trait_did = trait_pred.def_id(); + let trait_ = deps.require_ref::(trait_did).unwrap(); + let gargs = GArgs::new(impl_ctx, trait_pred.trait_ref.args); + let gargs = deps.require_dep::(gargs).unwrap(); + + let impl_check = (trait_.impl_fun)(gargs.get_ty(), gargs.get_const()); + checks.push(impl_check); + } - // Collect checks for the projection predicates. These have to be processed in a way such that - // any bindpoints they introduce are introduced with the let-bindings in the correct order. - let proj_preds = caller_bounds - .iter() - .filter_map(ty::Clause::as_projection_clause) - .map(ty::Binder::skip_binder); - let proj_preds = order_projections(generics_map.keys(), proj_preds, generics_count); - for proj_pred in proj_preds { - let trait_did = proj_pred.trait_def_id(tcx); - let trait_ = deps.require_ref::(trait_did).unwrap(); - let gargs = GArgs::new(impl_ctx, proj_pred.projection_term.args); - let gargs = deps.require_dep::(gargs).unwrap(); - - let (projection, expr): (vir::ExprDyn, vir::ExprDyn) = match proj_pred.term.kind() { - ty::TermKind::Ty(ty) => { - let projection = - trait_.fns.assoc_types[&proj_pred.def_id()](gargs.get_ty(), gargs.get_const()); - let decomp = RustTyDecomposition::from_ty(ty, impl_ctx); - let ty_expr = impl_params.ty_expr(deps, decomp); - discover_bind_points(deps, &mut generics_map, impl_ctx, projection, ty); - (projection.upcast_ty(), ty_expr.upcast_ty()) - } - ty::TermKind::Const(const_) => { - let projection = - trait_.fns.assoc_consts[&proj_pred.def_id()](gargs.get_ty(), gargs.get_const()); - let ty = tcx.type_of(proj_pred.def_id()).instantiate_identity(); - let const_task = ConstEncTask::Ty { - const_, - ty, - context: impl_ctx, - }; - let const_expr = deps.require_dep::(const_task).unwrap(); - if let ty::ConstKind::Param(p) = const_.kind() { - generics_map.try_insert(p.index, const_expr.upcast_ty()); + // Collect checks for the projection predicates. These have to be processed in a way such that + // any bindpoints they introduce are introduced with the let-bindings in the correct order. + let proj_preds = caller_bounds + .iter() + .filter_map(ty::Clause::as_projection_clause) + .map(ty::Binder::skip_binder); + let proj_preds = Self::order_projections(generics_map.keys(), proj_preds, generics_count); + for proj_pred in proj_preds { + let trait_did = proj_pred.trait_def_id(tcx); + let trait_ = deps.require_ref::(trait_did).unwrap(); + let gargs = GArgs::new(impl_ctx, proj_pred.projection_term.args); + let gargs = deps.require_dep::(gargs).unwrap(); + + let (projection, expr): (vir::ExprDyn, vir::ExprDyn) = match proj_pred.term.kind() { + ty::TermKind::Ty(ty) => { + let projection = trait_.fns.assoc_types[&proj_pred.def_id()]( + gargs.get_ty(), + gargs.get_const(), + ); + let decomp = RustTyDecomposition::from_ty(ty, impl_ctx); + let ty_expr = impl_params.ty_expr(deps, decomp); + Self::discover_bind_points(deps, &mut generics_map, impl_ctx, projection, ty); + (projection.upcast_ty(), ty_expr.upcast_ty()) } - (projection.upcast_ty(), const_expr.upcast_ty()) - } - }; + ty::TermKind::Const(const_) => { + let projection = trait_.fns.assoc_consts[&proj_pred.def_id()]( + gargs.get_ty(), + gargs.get_const(), + ); + let ty = tcx.type_of(proj_pred.def_id()).instantiate_identity(); + let const_task = ConstEncTask::Ty { + const_, + ty, + context: impl_ctx, + }; + let const_expr = deps.require_dep::(const_task).unwrap(); + if let ty::ConstKind::Param(p) = const_.kind() { + generics_map.try_insert(p.index, const_expr.upcast_ty()); + } + (projection.upcast_ty(), const_expr.upcast_ty()) + } + }; - let projection_check = vcx.mk_eq_expr(projection, expr); - checks.push(projection_check); - } + let projection_check = vcx.mk_eq_expr(projection, expr); + checks.push(projection_check); + } - let checks = vcx.mk_conj(&checks); + let checks = vcx.mk_conj(&checks); - generics_map - .insertion_ordered() - .rfold(checks, |acc, (idx, expr)| { - let idx = impl_params.map_idx(idx); - let decl = match idx { - Result::Ok(idx) => impl_params.ty_decls()[idx].upcast_ty(), - Result::Err(idx) => impl_params.const_decls()[idx].upcast_ty(), - }; - vcx.mk_let_expr(decl, expr, acc) - }) + generics_map + .insertion_ordered() + .rfold(checks, |acc, (idx, expr)| { + let idx = impl_params.map_idx(idx); + let decl = match idx { + Result::Ok(idx) => impl_params.ty_decls()[idx].upcast_ty(), + Result::Err(idx) => impl_params.const_decls()[idx].upcast_ty(), + }; + vcx.mk_let_expr(decl, expr, acc) + }) + } } /// Collects bindings for generics in the order they are discovered. diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index 7fbe7c6bfdf..0bfa98039d5 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -73,6 +73,7 @@ impl<'tcx> RustTyDecomposition<'tcx> { let data = RustTyData { name: symbol::Symbol::intern("Real"), params: GParams::empty(), + erased_ty: None, sizedness: Sizedness::Sized, }; let specifics = TySpecifics::Builtin(RustBuiltinData::BuiltinReal); @@ -243,6 +244,7 @@ pub type RustBuiltin<'tcx> = >::BuiltinData; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct RustTyData<'tcx> { pub name: symbol::Symbol, + pub erased_ty: Option>, pub params: GParams<'tcx>, pub sizedness: Sizedness<'tcx>, } @@ -301,6 +303,7 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { let args = GArgs::new(context, args); let data = RustTyData { name: symbol::Symbol::intern(&name), + erased_ty: erased_ty.into(), params, sizedness: sizedness_for_ty(vir::with_vcx(|vcx| vcx.tcx()), erased_ty), }; @@ -316,10 +319,11 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { fn from_prim_ty(ty: ty::Ty<'tcx>) -> RustTyDecomposition<'tcx> { let name = Self::prim_ty_name(ty); - let (_, params, args) = Self::identity_for_prim_ty(ty); + let (erased_ty, params, args) = Self::identity_for_prim_ty(ty); let args = GArgs::new(params, args); let data = RustTyData { name: symbol::Symbol::intern(&name), + erased_ty: erased_ty.into(), params, sizedness: Sizedness::Sized, }; From 489f3e94a268414e294e385e5556ac83aac969c8 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Mon, 9 Mar 2026 15:37:06 +0100 Subject: [PATCH 55/70] Remove sizedness from `RustTy` --- .../src/encoders/ty/generics/sized_trait.rs | 81 ++++++++++++++++++- prusti-encoder/src/encoders/ty/rust_ty.rs | 77 ------------------ 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index d5d71df2b89..ee20ea400e9 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -1,7 +1,7 @@ use crate::{ TaskEncoder, encoders::ty::{ - RustTy, Sizedness, + RustTy, generics::{GParams, trait_impls::TraitImplEnc, traits::TraitEnc}, lifted::TyConstructorEnc, }, @@ -29,10 +29,14 @@ impl TaskEncoder for SizedTraitEnc { deps: &mut task_encoder::TaskEncoderDependencies<'vir, Self>, ) -> task_encoder::EncodeFullResult<'vir, Self> { assert!(!task_key.specifics.is_param()); + assert!(task_key.erased_ty.is_some()); deps.emit_output_ref(*task_key, ())?; + if task_key.erased_ty.is_none() {} + vir::with_vcx(|vcx| { - let check = match task_key.sizedness { + let sizedness = sizedness_for_ty(vcx.tcx(), task_key.erased_ty.unwrap()); + let check = match sizedness { Sizedness::Unsized => None, Sizedness::Sized => Some(Self::sizedness_check( vcx, @@ -140,3 +144,76 @@ impl SizedTraitEnc { TraitImplEnc::impl_block_check(vcx, deps, impl_ctx, impls_sized) } } + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub enum Sizedness<'tcx> { + /// A type is definitely `Sized` + Sized, + /// A type is definitely not `Sized` + Unsized, + /// The sizedness of the type depends on the sizedness some other type contained within + Dependent(ty::Ty<'tcx>), +} + +impl<'tcx> Sizedness<'tcx> { + fn map(self, f: impl FnOnce(ty::Ty<'tcx>) -> ty::Ty<'tcx>) -> Self { + match self { + Sizedness::Dependent(ty) => Sizedness::Dependent(f(ty)), + other => other, + } + } +} + +/// Modified version of `https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ty_utils/ty/fn.sizedness_constraint_for_ty.html` +fn sizedness_for_ty<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> Sizedness<'tcx> { + match ty.kind() { + // Always `Sized` + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Array(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never => Sizedness::Sized, + + ty::Str | ty::Slice(..) | ty::Dynamic(..) => Sizedness::Unsized, + + // Maybe `Sized` + ty::Param(..) | ty::Alias(..) | ty::Error(_) => Sizedness::Dependent(ty), + + // We cannot instantiate the binder, so just return the *original* type back, + // but only if the inner type has a sized constraint. Thus we skip the binder, + // but don't actually use the result from `sizedness_for_ty`. + ty::UnsafeBinder(inner_ty) => sizedness_for_ty(tcx, inner_ty.skip_binder()).map(|_| ty), + + // Never `Sized` + ty::Foreign(..) => Sizedness::Unsized, + + // Recursive cases + ty::Pat(ty, _) => sizedness_for_ty(tcx, *ty), + + // Empty tuple always `Sized`, otherwise sizedness depends on last field + ty::Tuple(tys) => tys + .last() + .map_or(Sizedness::Sized, |last| sizedness_for_ty(tcx, *last)), + + ty::Adt(adt, args) => adt + .sizedness_constraint(tcx, ty::SizedTraitKind::Sized) + .map_or(Sizedness::Sized, |intermediate| { + let ty = intermediate.instantiate(tcx, args); + sizedness_for_ty(tcx, ty) + }), + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { + panic!("unexpected type `{ty:?}` in `sizedness_for_ty`") + } + } +} diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index 0bfa98039d5..7d47218cd6b 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -74,7 +74,6 @@ impl<'tcx> RustTyDecomposition<'tcx> { name: symbol::Symbol::intern("Real"), params: GParams::empty(), erased_ty: None, - sizedness: Sizedness::Sized, }; let specifics = TySpecifics::Builtin(RustBuiltinData::BuiltinReal); Self { @@ -246,7 +245,6 @@ pub struct RustTyData<'tcx> { pub name: symbol::Symbol, pub erased_ty: Option>, pub params: GParams<'tcx>, - pub sizedness: Sizedness<'tcx>, } impl<'tcx> RustTyData<'tcx> { @@ -305,7 +303,6 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { name: symbol::Symbol::intern(&name), erased_ty: erased_ty.into(), params, - sizedness: sizedness_for_ty(vir::with_vcx(|vcx| vcx.tcx()), erased_ty), }; let specifics = TySpecifics::from_ty(erased_ty); let maybe_inhabited = @@ -325,7 +322,6 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { name: symbol::Symbol::intern(&name), erased_ty: erased_ty.into(), params, - sizedness: Sizedness::Sized, }; let specifics = TySpecifics::from_prim_ty(ty); RustTyDecomposition { @@ -648,76 +644,3 @@ impl<'tcx> TySpecifics<'tcx, RustTyDatas> { vir::with_vcx(|vcx| ty::Const::new_param(vcx.tcx(), param)) } } - -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub enum Sizedness<'tcx> { - /// A type is definitely `Sized` - Sized, - /// A type is definitely not `Sized` - Unsized, - /// The sizedness of the type depends on the sizedness some other type contained within - Dependent(ty::Ty<'tcx>), -} - -impl<'tcx> Sizedness<'tcx> { - fn map(self, f: impl FnOnce(ty::Ty<'tcx>) -> ty::Ty<'tcx>) -> Self { - match self { - Sizedness::Dependent(ty) => Sizedness::Dependent(f(ty)), - other => other, - } - } -} - -/// Modified version of `https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ty_utils/ty/fn.sizedness_constraint_for_ty.html` -fn sizedness_for_ty<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: ty::Ty<'tcx>) -> Sizedness<'tcx> { - match ty.kind() { - // Always `Sized` - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Array(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Never => Sizedness::Sized, - - ty::Str | ty::Slice(..) | ty::Dynamic(..) => Sizedness::Unsized, - - // Maybe `Sized` - ty::Param(..) | ty::Alias(..) | ty::Error(_) => Sizedness::Dependent(ty), - - // We cannot instantiate the binder, so just return the *original* type back, - // but only if the inner type has a sized constraint. Thus we skip the binder, - // but don't actually use the result from `sizedness_for_ty`. - ty::UnsafeBinder(inner_ty) => sizedness_for_ty(tcx, inner_ty.skip_binder()).map(|_| ty), - - // Never `Sized` - ty::Foreign(..) => Sizedness::Unsized, - - // Recursive cases - ty::Pat(ty, _) => sizedness_for_ty(tcx, *ty), - - // Empty tuple always `Sized`, otherwise sizedness depends on last field - ty::Tuple(tys) => tys - .last() - .map_or(Sizedness::Sized, |last| sizedness_for_ty(tcx, *last)), - - ty::Adt(adt, args) => adt - .sizedness_constraint(tcx, ty::SizedTraitKind::Sized) - .map_or(Sizedness::Sized, |intermediate| { - let ty = intermediate.instantiate(tcx, args); - sizedness_for_ty(tcx, ty) - }), - - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { - panic!("unexpected type `{ty:?}` in `sizedness_for_ty`") - } - } -} From 1a27b01ef6ffb941fb5f35237095c60c9703d5fa Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 10 Mar 2026 09:29:16 +0100 Subject: [PATCH 56/70] Merge imports --- .../src/encoders/ty/generics/trait_impls.rs | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index c17b86f56b2..06e47732801 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -5,30 +5,13 @@ use prusti_interface::{PrustiError, specs::specifications::SpecQuery}; use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; use vir::{CastType, Domain, Method, MethodIdn, vir_format_identifier}; -use crate::encoders::{ - ConstEnc, - r#const::ConstEncTask, - ty::{ +use crate::{encoders::{ + ConstEnc, FunctionCallEnc, MirLocalDefEnc, MirLocalDefEncTask, MirSpecEnc, Pure, r#const::ConstEncTask, mir_fn::{CallTaskDescription, RustSignature}, pure::spec::MirSpecEncMode, ty::{ RustTyDecomposition, - generics::{GArgs, GArgsTyEnc, GParams, GenericParamsEnc, r#trait::TraitEnc}, + generics::{GArgs, GArgsCastEnc, GArgsTyEnc, GParams, GenericParamsEnc, r#trait::TraitEnc, trait_fn::TraitFnEnc}, lifted::TyConstructorEnc, - }, -}; -use crate::{ - encoders::{ - FunctionCallEnc, MirLocalDefEnc, MirLocalDefEncTask, MirSpecEnc, Pure, - mir_fn::{CallTaskDescription, RustSignature}, - pure::spec::MirSpecEncMode, - ty::{ - - generics::{ - GArgsCastEnc, - trait_fn::TraitFnEnc, - }, - }, - }, - trait_support::is_function_with_body, -}; + } +}, trait_support::is_function_with_body}; pub struct TraitImplEnc; From 60b36a43b941fb729a17f19c680d08f9aee47c34 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 10 Mar 2026 09:33:15 +0100 Subject: [PATCH 57/70] Remove unnecessary changes in `TyConstructorEnc` --- .../src/encoders/ty/lifted/ty_constructor.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index 9517f20c0b9..c39e8bfbf43 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -97,7 +97,6 @@ impl TaskEncoder for TyConstructorEnc { }) .collect::>(); - let ty_accessor_functions = vcx.alloc_slice(&ty_accessor_functions); let const_accessor_functions = params .const_decls() .iter() @@ -109,7 +108,6 @@ impl TaskEncoder for TyConstructorEnc { ) }) .collect::>(); - let const_accessor_functions = vcx.alloc_slice(&const_accessor_functions); let typeof_data = deps.require_ref::(*task_key)?; deps.emit_output_ref( @@ -117,8 +115,8 @@ impl TaskEncoder for TyConstructorEnc { TyConstructorEncOutputRef { typeof_data, ty_constructor: type_function_ident, - ty_param_accessors: ty_accessor_functions, - const_param_accessors: const_accessor_functions, + ty_param_accessors: vcx.alloc_slice(&ty_accessor_functions), + const_param_accessors: vcx.alloc_slice(&const_accessor_functions), }, )?; @@ -131,13 +129,13 @@ impl TaskEncoder for TyConstructorEnc { .map(|d| vcx.mk_local_decl(d.name, d.ty).upcast_ty()), ) .collect::>>(); - let constructor = + let variant = vcx.mk_adt_constructor(type_function_ident.name().to_str(), vcx.alloc_slice(&args)); // NOTE: This call depends on the ref output of this encoder deps.require_dep::(task_key)?; - Ok((constructor, ())) + Ok((variant, ())) }) } From 7bb452a5a51e76bc51277bea40cbacbe324ca0d6 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 10 Mar 2026 16:28:40 +0100 Subject: [PATCH 58/70] Return `Result`s instead of unwrapping to avoid cyclic errors --- .../src/encoders/ty/generics/args_ty.rs | 2 +- .../src/encoders/ty/generics/params.rs | 28 +++++------ .../src/encoders/ty/generics/sized_trait.rs | 16 ++++--- .../src/encoders/ty/generics/trait_impls.rs | 47 +++++++++---------- 4 files changed, 48 insertions(+), 45 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/args_ty.rs b/prusti-encoder/src/encoders/ty/generics/args_ty.rs index f85d0221b60..c952db6f08f 100644 --- a/prusti-encoder/src/encoders/ty/generics/args_ty.rs +++ b/prusti-encoder/src/encoders/ty/generics/args_ty.rs @@ -52,7 +52,7 @@ impl TaskEncoder for GArgsTyEnc { let decomp = RustTyDecomposition::from_ty(arg, task_key.context); params.ty_expr(deps, decomp) }) - .collect::>(); + .collect::, _>>()?; let const_args = task_key .args .iter() diff --git a/prusti-encoder/src/encoders/ty/generics/params.rs b/prusti-encoder/src/encoders/ty/generics/params.rs index 70be66dde50..0305f919acb 100644 --- a/prusti-encoder/src/encoders/ty/generics/params.rs +++ b/prusti-encoder/src/encoders/ty/generics/params.rs @@ -3,7 +3,7 @@ use prusti_rustc_interface::{ middle::ty, span::{def_id::DefId, symbol}, }; -use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; +use task_encoder::{EncodeFullError, EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; use vir::{CastType, HasType}; use crate::encoders::{ @@ -267,27 +267,27 @@ impl<'vir> GenericParams<'vir> { &self, deps: &mut TaskEncoderDependencies<'vir, E>, ty: RustTyDecomposition<'vir>, - ) -> vir::ExprTyVal<'vir> { + ) -> Result, EncodeFullError<'vir, E>> { if let TySpecifics::Param(()) = &ty.ty.specifics { let param = ty.args.expect_param(); - return match param { + return Ok(match param { GParamVariant::Param(p) => self.ty_exprs[self.map_idx(p.index).unwrap()], GParamVariant::Alias(alias) => vir::with_vcx(|vcx| { let tcx = vcx.tcx(); let trait_did = tcx.associated_item(alias.def_id).container_id(tcx); - let trait_data = deps.require_ref::(trait_did).unwrap(); + let trait_data = deps.require_ref::(trait_did)?; let args = GArgs::new(ty.args.context, alias.args); - let args = deps.require_dep::(args).unwrap(); - (trait_data.assoc_types[&alias.def_id])(args.get_ty(), args.get_const()) - }), - }; + let args = deps.require_dep::(args)?; + Ok((trait_data.assoc_types[&alias.def_id])( + args.get_ty(), + args.get_const(), + )) + })?, + }); } - let ty_constructor = deps - .require_ref::(ty.ty) - .unwrap() - .ty_constructor; - let args = deps.require_dep::(ty.args).unwrap(); - ty_constructor(args.get_ty(), args.get_const()) + let ty_constructor = deps.require_ref::(ty.ty)?.ty_constructor; + let args = deps.require_dep::(ty.args)?; + Ok(ty_constructor(args.get_ty(), args.get_const())) } } diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index ea2ce95d81e..2ec86c7a829 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -7,6 +7,7 @@ use crate::{ }, }; use prusti_rustc_interface::middle::{ty, ty::Upcast}; +use task_encoder::EncodeFullError; pub struct SizedTraitEnc; @@ -32,8 +33,6 @@ impl TaskEncoder for SizedTraitEnc { assert!(task_key.erased_ty.is_some()); deps.emit_output_ref(*task_key, ())?; - if task_key.erased_ty.is_none() {} - vir::with_vcx(|vcx| { let sizedness = sizedness_for_ty(vcx.tcx(), task_key.erased_ty.unwrap()); let check = match sizedness { @@ -44,14 +43,14 @@ impl TaskEncoder for SizedTraitEnc { task_key.params, task_key.erased_ty.unwrap(), None, - )), + )?), Sizedness::Dependent(dep_ty) => Some(Self::sizedness_check( vcx, deps, task_key.params, task_key.erased_ty.unwrap(), Some(dep_ty), - )), + )?), }; Ok((check, ())) @@ -120,7 +119,7 @@ impl SizedTraitEnc { impl_ctx: GParams<'vir>, impl_ty: ty::Ty<'vir>, depended_on: Option>, - ) -> vir::ExprBool<'vir> { + ) -> Result, EncodeFullError<'vir, SizedTraitEnc>> { let tcx = vcx.tcx(); let sized_did = tcx.lang_items().sized_trait().unwrap(); @@ -141,7 +140,12 @@ impl SizedTraitEnc { let impl_ctx = GParams::new(impl_ctx.rust_params(), param_env, false); - TraitImplEnc::impl_block_check(vcx, deps, impl_ctx, impls_sized) + Ok(TraitImplEnc::impl_block_check( + vcx, + deps, + impl_ctx, + impls_sized, + )?) } } diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 06e47732801..67e01c4e38f 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -2,7 +2,7 @@ use std::collections::VecDeque; use prusti_rustc_interface::{index::bit_set::DenseBitSet, middle::ty, span::def_id::DefId, middle::mir}; use prusti_interface::{PrustiError, specs::specifications::SpecQuery}; -use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; +use task_encoder::{EncodeFullError, EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; use vir::{CastType, Domain, Method, MethodIdn, vir_format_identifier}; use crate::{encoders::{ @@ -84,8 +84,7 @@ impl TaskEncoder for TraitImplEnc { // parameters of assoc item include already substituted arguments let impl_item_context = GParams::from(impl_item_def_id); let impl_item_params = deps - .require_dep::(impl_item_context) - .unwrap(); + .require_dep::(impl_item_context)?; // The ty and const decls of the trait items are the decls of // the item itself prefixed by the decls of the impl itself. @@ -132,7 +131,7 @@ impl TaskEncoder for TraitImplEnc { tcx.type_of(impl_item_def_id).instantiate_identity(), impl_item_context, ), - ); + )?; axioms.push(vcx.mk_domain_axiom( vir_format_identifier!(vcx, "{trait_name}_impl_{implementing_ty}_{idx}_assoc_type_{item_name}"), vir::expr! {forall ..[trait_ty_decls], ..[trait_const_decls] :: @@ -421,7 +420,7 @@ impl TaskEncoder for TraitImplEnc { let trait_ref = tcx.impl_trait_ref(task_key).unwrap().instantiate_identity(); let impl_condition = - Self::impl_block_check(vcx, deps, GParams::from(*task_key), trait_ref); + Self::impl_block_check(vcx, deps, GParams::from(*task_key), trait_ref)?; Ok(( ( @@ -508,14 +507,14 @@ impl TraitImplEnc { ctx: GParams<'vir>, expr: vir::ExprTyVal<'vir>, ty: ty::Ty<'vir>, - ) { + ) -> Result<(), EncodeFullError<'vir, E>> { if let ty::TyKind::Param(p) = ty.kind() { generic_map.try_insert(p.index, expr.upcast_ty()); - return; + return Ok(()); } let decomp = RustTyDecomposition::from_ty(ty, ctx); - let ty_enc = deps.require_ref::(decomp.ty).unwrap(); + let ty_enc = deps.require_ref::(decomp.ty)?; let args = decomp.args.args(); let inner_types = args.iter().filter_map(|arg| arg.as_type()); @@ -523,7 +522,7 @@ impl TraitImplEnc { let accessor = ty_enc.ty_param_accessors[i]; let inner_expr = accessor.call()(expr); - Self::discover_bind_points(deps, generic_map, ctx, inner_expr, inner_ty); + Self::discover_bind_points(deps, generic_map, ctx, inner_expr, inner_ty)?; } let inner_consts = args.iter().filter_map(|arg| arg.as_const()); @@ -535,6 +534,7 @@ impl TraitImplEnc { generic_map.try_insert(p.index, inner_expr.upcast_ty()); } } + Ok(()) } pub(super) fn impl_block_check<'vir, E: TaskEncoder + 'vir + ?Sized>( @@ -542,17 +542,16 @@ impl TraitImplEnc { deps: &mut TaskEncoderDependencies<'vir, E>, impl_ctx: GParams<'vir>, trait_ref: ty::TraitRef<'vir>, - ) -> vir::ExprBool<'vir> { + ) -> Result, EncodeFullError<'vir, E>> { let tcx = vcx.tcx(); let impl_ctx = impl_ctx.with_suffix("impl"); - let impl_params = deps.require_dep::(impl_ctx).unwrap(); + let impl_params = deps.require_dep::(impl_ctx)?; let trait_ctx = TraitEnc::trait_params(trait_ref.def_id); - let trait_params = deps.require_dep::(trait_ctx).unwrap(); + let trait_params = deps.require_dep::(trait_ctx)?; let args = deps - .require_dep::(GArgs::new(impl_ctx, trait_ref.args)) - .unwrap(); + .require_dep::(GArgs::new(impl_ctx, trait_ref.args))?; let generics_count = impl_ctx.rust_params().len(); @@ -562,7 +561,7 @@ impl TraitImplEnc { // Walk the trait type generic arguments let impl_rust_tys = trait_ref.args.iter().filter_map(|arg| arg.as_type()); for (ty_arg, rust_ty) in std::iter::zip(trait_params.ty_exprs(), impl_rust_tys) { - Self::discover_bind_points(deps, &mut generics_map, trait_ctx, ty_arg, rust_ty); + Self::discover_bind_points(deps, &mut generics_map, trait_ctx, ty_arg, rust_ty)?; } // Walk the trait const generic arguments @@ -594,9 +593,9 @@ impl TraitImplEnc { .map(ty::Binder::skip_binder); for trait_pred in trait_preds { let trait_did = trait_pred.def_id(); - let trait_ = deps.require_ref::(trait_did).unwrap(); + let trait_ = deps.require_ref::(trait_did)?; let gargs = GArgs::new(impl_ctx, trait_pred.trait_ref.args); - let gargs = deps.require_dep::(gargs).unwrap(); + let gargs = deps.require_dep::(gargs)?; let impl_check = (trait_.impl_fun)(gargs.get_ty(), gargs.get_const()); checks.push(impl_check); @@ -611,9 +610,9 @@ impl TraitImplEnc { let proj_preds = Self::order_projections(generics_map.keys(), proj_preds, generics_count); for proj_pred in proj_preds { let trait_did = proj_pred.trait_def_id(tcx); - let trait_ = deps.require_ref::(trait_did).unwrap(); + let trait_ = deps.require_ref::(trait_did)?; let gargs = GArgs::new(impl_ctx, proj_pred.projection_term.args); - let gargs = deps.require_dep::(gargs).unwrap(); + let gargs = deps.require_dep::(gargs)?; let (projection, expr): (vir::ExprDyn, vir::ExprDyn) = match proj_pred.term.kind() { ty::TermKind::Ty(ty) => { @@ -622,8 +621,8 @@ impl TraitImplEnc { gargs.get_const(), ); let decomp = RustTyDecomposition::from_ty(ty, impl_ctx); - let ty_expr = impl_params.ty_expr(deps, decomp); - Self::discover_bind_points(deps, &mut generics_map, impl_ctx, projection, ty); + let ty_expr = impl_params.ty_expr(deps, decomp)?; + Self::discover_bind_points(deps, &mut generics_map, impl_ctx, projection, ty)?; (projection.upcast_ty(), ty_expr.upcast_ty()) } ty::TermKind::Const(const_) => { @@ -637,7 +636,7 @@ impl TraitImplEnc { ty, context: impl_ctx, }; - let const_expr = deps.require_dep::(const_task).unwrap(); + let const_expr = deps.require_dep::(const_task)?; if let ty::ConstKind::Param(p) = const_.kind() { generics_map.try_insert(p.index, const_expr.upcast_ty()); } @@ -651,7 +650,7 @@ impl TraitImplEnc { let checks = vcx.mk_conj(&checks); - generics_map + Ok(generics_map .insertion_ordered() .rfold(checks, |acc, (idx, expr)| { let idx = impl_params.map_idx(idx); @@ -660,7 +659,7 @@ impl TraitImplEnc { Result::Err(idx) => impl_params.const_decls()[idx].upcast_ty(), }; vcx.mk_let_expr(decl, expr, acc) - }) + })) } } From 8288aa3e9a58b27b45774851d9b4bcfaf7b08d08 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 10 Mar 2026 17:25:26 +0100 Subject: [PATCH 59/70] Move trait check into an `ensures` instead of the body --- .../src/encoders/ty/generics/sized_trait.rs | 6 ++++-- prusti-encoder/src/encoders/ty/generics/trait.rs | 14 +++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index 2ec86c7a829..5323c2c1462 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -83,13 +83,15 @@ impl TaskEncoder for SizedTraitEnc { checks.push(unknown_check); + let ensures = vcx.mk_eq_expr(vcx.mk_result(vir::TYPE_BOOL), vcx.mk_disj(&checks)); + let sized_impl_fun = vcx.mk_function( sized_impl_idn, (&[self_decl], &[]), &[], - &[], + vcx.alloc_slice(&[ensures]), + Some(&vir::DecreasesGenData::Star), None, - Some(vcx.mk_disj(&checks)), ); program.add_function(sized_impl_fun); diff --git a/prusti-encoder/src/encoders/ty/generics/trait.rs b/prusti-encoder/src/encoders/ty/generics/trait.rs index e90a57f89cc..a5a5f900495 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait.rs @@ -99,7 +99,7 @@ impl TaskEncoder for TraitEnc { assoc_consts.insert(assoc_did, fun); dom_funcs.push(vcx.mk_domain_function(fun, false, None)); } - ty::AssocKind::Fn { .. } => { /* handled in trait_fn.rs */ } + ty::AssocKind::Fn { .. } => {} } } @@ -164,8 +164,16 @@ impl TaskEncoder for TraitEnc { vcx.mk_disj(&trait_impl_checks) }; - let impl_fun = - vcx.mk_function(impl_fun, trait_decls, &[], &[], None, Some(impl_fun_body)); + let ensures = vcx.mk_eq_expr(vcx.mk_result(vir::TYPE_BOOL), impl_fun_body); + + let impl_fun = vcx.mk_function( + impl_fun, + trait_decls, + &[], + vcx.alloc_slice(&[ensures]), + Some(&vir::DecreasesGenData::Star), + None, + ); let impl_for_unknown_fun = vcx.mk_domain_function(impl_for_unknown_idn, false, None); dom_funcs.push(impl_for_unknown_fun); From 687f26da50d098052406b95d121b895934e2b4cc Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 11 Mar 2026 11:37:19 +0100 Subject: [PATCH 60/70] Accessor for the `erased_ty` --- .../src/encoders/ty/generics/sized_trait.rs | 16 ++++++---------- prusti-encoder/src/encoders/ty/rust_ty.rs | 9 ++++++++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index 5323c2c1462..bb8dd72f503 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -30,25 +30,21 @@ impl TaskEncoder for SizedTraitEnc { deps: &mut task_encoder::TaskEncoderDependencies<'vir, Self>, ) -> task_encoder::EncodeFullResult<'vir, Self> { assert!(!task_key.specifics.is_param()); - assert!(task_key.erased_ty.is_some()); deps.emit_output_ref(*task_key, ())?; vir::with_vcx(|vcx| { - let sizedness = sizedness_for_ty(vcx.tcx(), task_key.erased_ty.unwrap()); + let ty = task_key.erased_ty_for_sizedness(); + let sizedness = sizedness_for_ty(vcx.tcx(), ty); let check = match sizedness { Sizedness::Unsized => None, - Sizedness::Sized => Some(Self::sizedness_check( - vcx, - deps, - task_key.params, - task_key.erased_ty.unwrap(), - None, - )?), + Sizedness::Sized => { + Some(Self::sizedness_check(vcx, deps, task_key.params, ty, None)?) + } Sizedness::Dependent(dep_ty) => Some(Self::sizedness_check( vcx, deps, task_key.params, - task_key.erased_ty.unwrap(), + ty, Some(dep_ty), )?), }; diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index cc1e2c41ca9..a6e5557dbb9 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -243,7 +243,7 @@ pub type RustBuiltin<'tcx> = >::BuiltinData; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct RustTyData<'tcx> { pub name: symbol::Symbol, - pub erased_ty: Option>, + erased_ty: Option>, pub params: GParams<'tcx>, } @@ -251,6 +251,13 @@ impl<'tcx> RustTyData<'tcx> { pub fn name(&self) -> &str { self.name.as_str() } + + /// NOTE: this is a temporary hack to get the `ty::Ty` for the `SizedTraitEnc`. Should not be + /// used in other places + pub(super) fn erased_ty_for_sizedness(&self) -> ty::Ty<'tcx> { + self.erased_ty + .expect("erased_ty should be Some for the `Sized` trait encoder") + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] From bf3bbf21fa17d47ccbc3bec0b4268518161d32cb Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Wed, 11 Mar 2026 11:39:44 +0100 Subject: [PATCH 61/70] Clippy --- prusti-encoder/src/encoders/ty/generics/sized_trait.rs | 7 +------ prusti-encoder/src/encoders/ty/generics/trait_impls.rs | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index bb8dd72f503..a2a10dc993b 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -138,12 +138,7 @@ impl SizedTraitEnc { let impl_ctx = GParams::new(impl_ctx.rust_params(), param_env, false); - Ok(TraitImplEnc::impl_block_check( - vcx, - deps, - impl_ctx, - impls_sized, - )?) + TraitImplEnc::impl_block_check(vcx, deps, impl_ctx, impls_sized) } } diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 67e01c4e38f..6474489faeb 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -694,7 +694,7 @@ impl<'vir> GenericsMap<'vir> { /// Collect the bindings in their insertion order. fn insertion_ordered( &self, - ) -> impl Iterator)> + DoubleEndedIterator { + ) -> impl DoubleEndedIterator)> { let mut ordered = self .map .iter() From 392e097d2aacc7b87c3a0b4ed9ab03abe2554690 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Sun, 15 Mar 2026 11:46:37 +0100 Subject: [PATCH 62/70] fmt --- .../src/encoders/ty/generics/trait_impls.rs | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs index 6474489faeb..a6b3d368cf1 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait_impls.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait_impls.rs @@ -1,17 +1,31 @@ use std::collections::VecDeque; -use prusti_rustc_interface::{index::bit_set::DenseBitSet, middle::ty, span::def_id::DefId, middle::mir}; use prusti_interface::{PrustiError, specs::specifications::SpecQuery}; +use prusti_rustc_interface::{ + index::bit_set::DenseBitSet, + middle::{mir, ty}, + span::def_id::DefId, +}; use task_encoder::{EncodeFullError, EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; use vir::{CastType, Domain, Method, MethodIdn, vir_format_identifier}; -use crate::{encoders::{ - ConstEnc, FunctionCallEnc, MirLocalDefEnc, MirLocalDefEncTask, MirSpecEnc, Pure, r#const::ConstEncTask, mir_fn::{CallTaskDescription, RustSignature}, pure::spec::MirSpecEncMode, ty::{ - RustTyDecomposition, - generics::{GArgs, GArgsCastEnc, GArgsTyEnc, GParams, GenericParamsEnc, r#trait::TraitEnc, trait_fn::TraitFnEnc}, - lifted::TyConstructorEnc, - } -}, trait_support::is_function_with_body}; +use crate::{ + encoders::{ + ConstEnc, FunctionCallEnc, MirLocalDefEnc, MirLocalDefEncTask, MirSpecEnc, Pure, + r#const::ConstEncTask, + mir_fn::{CallTaskDescription, RustSignature}, + pure::spec::MirSpecEncMode, + ty::{ + RustTyDecomposition, + generics::{ + GArgs, GArgsCastEnc, GArgsTyEnc, GParams, GenericParamsEnc, r#trait::TraitEnc, + trait_fn::TraitFnEnc, + }, + lifted::TyConstructorEnc, + }, + }, + trait_support::is_function_with_body, +}; pub struct TraitImplEnc; @@ -83,8 +97,7 @@ impl TaskEncoder for TraitImplEnc { // parameters of assoc item include already substituted arguments let impl_item_context = GParams::from(impl_item_def_id); - let impl_item_params = deps - .require_dep::(impl_item_context)?; + let impl_item_params = deps.require_dep::(impl_item_context)?; // The ty and const decls of the trait items are the decls of // the item itself prefixed by the decls of the impl itself. @@ -133,9 +146,12 @@ impl TaskEncoder for TraitImplEnc { ), )?; axioms.push(vcx.mk_domain_axiom( - vir_format_identifier!(vcx, "{trait_name}_impl_{implementing_ty}_{idx}_assoc_type_{item_name}"), - vir::expr! {forall ..[trait_ty_decls], ..[trait_const_decls] :: - {[assoc_type(trait_tys, trait_consts)]} + vir_format_identifier!( + vcx, + "{trait_name}_impl_{implementing_ty}_{idx}_assoc_type_{item_name}" + ), + vir::expr! {forall ..[trait_ty_decls], ..[trait_const_decls] :: + {[assoc_type(trait_tys, trait_consts)]} ([assoc_type(trait_tys, trait_consts)]) == (assoc_type_expr)}, )); } @@ -417,7 +433,6 @@ impl TaskEncoder for TraitImplEnc { } } - let trait_ref = tcx.impl_trait_ref(task_key).unwrap().instantiate_identity(); let impl_condition = Self::impl_block_check(vcx, deps, GParams::from(*task_key), trait_ref)?; @@ -550,8 +565,7 @@ impl TraitImplEnc { let trait_ctx = TraitEnc::trait_params(trait_ref.def_id); let trait_params = deps.require_dep::(trait_ctx)?; - let args = deps - .require_dep::(GArgs::new(impl_ctx, trait_ref.args))?; + let args = deps.require_dep::(GArgs::new(impl_ctx, trait_ref.args))?; let generics_count = impl_ctx.rust_params().len(); @@ -616,20 +630,16 @@ impl TraitImplEnc { let (projection, expr): (vir::ExprDyn, vir::ExprDyn) = match proj_pred.term.kind() { ty::TermKind::Ty(ty) => { - let projection = trait_.assoc_types[&proj_pred.def_id()]( - gargs.get_ty(), - gargs.get_const(), - ); + let projection = + trait_.assoc_types[&proj_pred.def_id()](gargs.get_ty(), gargs.get_const()); let decomp = RustTyDecomposition::from_ty(ty, impl_ctx); let ty_expr = impl_params.ty_expr(deps, decomp)?; Self::discover_bind_points(deps, &mut generics_map, impl_ctx, projection, ty)?; (projection.upcast_ty(), ty_expr.upcast_ty()) } ty::TermKind::Const(const_) => { - let projection = trait_.assoc_consts[&proj_pred.def_id()]( - gargs.get_ty(), - gargs.get_const(), - ); + let projection = + trait_.assoc_consts[&proj_pred.def_id()](gargs.get_ty(), gargs.get_const()); let ty = tcx.type_of(proj_pred.def_id()).instantiate_identity(); let const_task = ConstEncTask::Ty { const_, @@ -692,9 +702,7 @@ impl<'vir> GenericsMap<'vir> { } /// Collect the bindings in their insertion order. - fn insertion_ordered( - &self, - ) -> impl DoubleEndedIterator)> { + fn insertion_ordered(&self) -> impl DoubleEndedIterator)> { let mut ordered = self .map .iter() From 922bcd881540f2ef71c04945c89ccaec0b31f67d Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Mon, 16 Mar 2026 21:10:55 +0100 Subject: [PATCH 63/70] Implement the `Tuple` trait encoder --- .../src/encoders/ty/generics/mod.rs | 1 + .../src/encoders/ty/generics/trait.rs | 9 +- .../src/encoders/ty/generics/tuple_trait.rs | 117 ++++++++++++++++++ .../src/encoders/ty/lifted/ty_constructor.rs | 5 +- prusti-encoder/src/lib.rs | 3 +- 5 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 prusti-encoder/src/encoders/ty/generics/tuple_trait.rs diff --git a/prusti-encoder/src/encoders/ty/generics/mod.rs b/prusti-encoder/src/encoders/ty/generics/mod.rs index dfbddfb2ac2..12c7e2de329 100644 --- a/prusti-encoder/src/encoders/ty/generics/mod.rs +++ b/prusti-encoder/src/encoders/ty/generics/mod.rs @@ -4,6 +4,7 @@ mod casters; mod args_ty; mod args; pub mod sized_trait; +pub mod tuple_trait; pub mod r#trait; pub mod trait_fn; pub mod trait_impls; diff --git a/prusti-encoder/src/encoders/ty/generics/trait.rs b/prusti-encoder/src/encoders/ty/generics/trait.rs index a5a5f900495..f531dbb364e 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait.rs @@ -121,8 +121,13 @@ impl TaskEncoder for TraitEnc { }, )?; - // When encoding `Sized`, emitting of the impl function is handled by `SizedTraitEnc` - if tcx.lang_items().sized_trait() == Some(*task_key) { + // When encoding traits without explicit impl blocks, emitting of the impl block is + // handled by their respective encoders + let special_traits = { + let items = tcx.lang_items(); + [items.sized_trait().unwrap(), items.tuple_trait().unwrap()] + }; + if special_traits.contains(task_key) { return Ok((None, ())); } diff --git a/prusti-encoder/src/encoders/ty/generics/tuple_trait.rs b/prusti-encoder/src/encoders/ty/generics/tuple_trait.rs new file mode 100644 index 00000000000..5aafc12e579 --- /dev/null +++ b/prusti-encoder/src/encoders/ty/generics/tuple_trait.rs @@ -0,0 +1,117 @@ +use crate::{ + TaskEncoder, + encoders::ty::{ + RustTy, + generics::{r#trait::TraitEnc, trait_impls::TraitImplEnc}, + lifted::TyConstructorEnc, + }, +}; +use prusti_rustc_interface::middle::ty; + +pub struct TupleTraitEnc; + +const TUPLE_TRAIT_NAME: &str = "Tuple"; +const TUPLE_TRAIT_ARGS: <(vir::ManyTyVal, vir::ManyCSnap) as vir::Arity>::Tys<'static> = + (&[vir::TYPE_TYVAL], &[]); + +impl TaskEncoder for TupleTraitEnc { + task_encoder::encoder_cache!(TupleTraitEnc); + type TaskDescription<'vir> = RustTy<'vir>; + + type OutputFullLocal<'vir> = Option>; + + fn task_to_key<'vir>(task: &Self::TaskDescription<'vir>) -> Self::TaskKey<'vir> { + *task + } + + fn do_encode_full<'vir>( + task_key: &Self::TaskKey<'vir>, + deps: &mut task_encoder::TaskEncoderDependencies<'vir, Self>, + ) -> task_encoder::EncodeFullResult<'vir, Self> { + assert!(!task_key.specifics.is_param()); + deps.emit_output_ref(*task_key, ())?; + + vir::with_vcx(|vcx| { + let tcx = vcx.tcx(); + let ty = task_key.erased_ty_for_sizedness(); + + let tuple_trait_did = tcx.lang_items().tuple_trait().unwrap(); + + let check = if matches!(ty.kind(), ty::TyKind::Tuple(..)) { + let impls_tuple = ty::TraitRef::new_from_args( + tcx, + tuple_trait_did, + tcx.mk_args_trait(ty, std::iter::empty()), + ); + Some(TraitImplEnc::impl_block_check( + vcx, + deps, + task_key.params, + impls_tuple, + )?) + } else { + None + }; + + Ok((check, ())) + }) + } + + fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { + vir::with_vcx(|vcx| { + let mut checks: Vec<_> = Self::all_outputs_local_no_errors() + .into_iter() + .flatten() + .collect(); + + let tuple_impl_idn = TraitEnc::trait_impl_idn(vcx, TUPLE_TRAIT_NAME, TUPLE_TRAIT_ARGS); + let tuple_impl_unknown_idn = + TraitEnc::trait_unknown_impl_idn(vcx, TUPLE_TRAIT_NAME, TUPLE_TRAIT_ARGS); + + let self_decl = Self::tuple_self_decl(vcx); + let self_expr = vcx.mk_local_ex(self_decl); + + let unknown_check = { + let is_unknown = + vcx.mk_adt_discriminator_expr(self_expr, TyConstructorEnc::UNKNOWN_TYPE_NAME); + let unknown_id = TyConstructorEnc::unknown_type_id_accessor(vcx).call()(self_expr); + + let unknown_impls = tuple_impl_unknown_idn.call()(unknown_id, &[], &[]); + + vir::expr! {vcx; (is_unknown) && (unknown_impls) } + }; + + checks.push(unknown_check); + + let ensures = vcx.mk_eq_expr(vcx.mk_result(vir::TYPE_BOOL), vcx.mk_disj(&checks)); + + let tuple_impl_idn = vcx.mk_function( + tuple_impl_idn, + (&[self_decl], &[]), + &[], + vcx.alloc_slice(&[ensures]), + Some(&vir::DecreasesGenData::Star), + None, + ); + program.add_function(tuple_impl_idn); + + let tuple_impl_unknown_fun = + vcx.mk_domain_function(tuple_impl_unknown_idn, false, None); + + let sized_domain = vcx.mk_domain( + TraitEnc::trait_domain_idn(vcx, TUPLE_TRAIT_NAME), + &[], + &[], + vcx.alloc_slice(&[tuple_impl_unknown_fun]), + None, + ); + program.add_domain(sized_domain); + }); + } +} + +impl TupleTraitEnc { + fn tuple_self_decl<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::LocalDecl<'vir, vir::TyVal> { + vcx.mk_local_decl("Self$0_trait", vir::TYPE_TYVAL) + } +} diff --git a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs index c39e8bfbf43..1c1ef71eda1 100644 --- a/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs +++ b/prusti-encoder/src/encoders/ty/lifted/ty_constructor.rs @@ -3,7 +3,7 @@ use vir::{CallableIdn, CastType, FunctionIdn, HasType}; use crate::encoders::ty::{ RustTy, - generics::{GenericParamsEnc, sized_trait::SizedTraitEnc}, + generics::{GenericParamsEnc, sized_trait::SizedTraitEnc, tuple_trait::TupleTraitEnc}, }; use super::r#typeof::{TypeOfEnc, TypeOfEncOutputRef}; @@ -132,8 +132,9 @@ impl TaskEncoder for TyConstructorEnc { let variant = vcx.mk_adt_constructor(type_function_ident.name().to_str(), vcx.alloc_slice(&args)); - // NOTE: This call depends on the ref output of this encoder + // NOTE: These calls depend on the ref output of this encoder deps.require_dep::(task_key)?; + deps.require_dep::(task_key)?; Ok((variant, ())) }) diff --git a/prusti-encoder/src/lib.rs b/prusti-encoder/src/lib.rs index 6a6f36df87c..beb29d7ca53 100644 --- a/prusti-encoder/src/lib.rs +++ b/prusti-encoder/src/lib.rs @@ -25,7 +25,7 @@ use crate::encoders::{ ty::{ generics::{ GArgsCastEnc, sized_trait::SizedTraitEnc, r#trait::TraitEnc, trait_fn::TraitFnEnc, - trait_impls::TraitImplEnc, + trait_impls::TraitImplEnc, tuple_trait::TupleTraitEnc, }, interpretation::bitvec::BitVecEnc, lifted::{TyConstructorEnc, TypeOfEnc}, @@ -115,6 +115,7 @@ pub fn test_entrypoint<'tcx>( TraitFnEnc::emit_outputs(&mut program); TraitImplEnc::emit_outputs(&mut program); SizedTraitEnc::emit_outputs(&mut program); + TupleTraitEnc::emit_outputs(&mut program); if std::env::var("LOCAL_TESTING").is_ok() { std::fs::write("local-testing/simple.vpr", program.code()).unwrap(); From fd22779cfec6366460655bb275d364c6be93dee2 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 17 Mar 2026 10:27:25 +0100 Subject: [PATCH 64/70] Move special traits to a separate module --- prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs | 2 ++ .../encoders/ty/generics/{ => builtin_trait}/sized_trait.rs | 0 .../encoders/ty/generics/{ => builtin_trait}/tuple_trait.rs | 0 prusti-encoder/src/encoders/ty/generics/mod.rs | 4 ++-- 4 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs rename prusti-encoder/src/encoders/ty/generics/{ => builtin_trait}/sized_trait.rs (100%) rename prusti-encoder/src/encoders/ty/generics/{ => builtin_trait}/tuple_trait.rs (100%) diff --git a/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs b/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs new file mode 100644 index 00000000000..0a10cc40730 --- /dev/null +++ b/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs @@ -0,0 +1,2 @@ +pub mod sized_trait; +pub mod tuple_trait; diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/builtin_trait/sized_trait.rs similarity index 100% rename from prusti-encoder/src/encoders/ty/generics/sized_trait.rs rename to prusti-encoder/src/encoders/ty/generics/builtin_trait/sized_trait.rs diff --git a/prusti-encoder/src/encoders/ty/generics/tuple_trait.rs b/prusti-encoder/src/encoders/ty/generics/builtin_trait/tuple_trait.rs similarity index 100% rename from prusti-encoder/src/encoders/ty/generics/tuple_trait.rs rename to prusti-encoder/src/encoders/ty/generics/builtin_trait/tuple_trait.rs diff --git a/prusti-encoder/src/encoders/ty/generics/mod.rs b/prusti-encoder/src/encoders/ty/generics/mod.rs index 12c7e2de329..961291e527b 100644 --- a/prusti-encoder/src/encoders/ty/generics/mod.rs +++ b/prusti-encoder/src/encoders/ty/generics/mod.rs @@ -3,13 +3,13 @@ mod params; mod casters; mod args_ty; mod args; -pub mod sized_trait; -pub mod tuple_trait; +pub mod builtin_trait; pub mod r#trait; pub mod trait_fn; pub mod trait_impls; pub use args::*; pub use args_ty::*; +pub use builtin_trait::*; pub use params::*; pub use use_casters::*; From bbb9e81d6ec78b93c938f7b88ed080538dd7361e Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 17 Mar 2026 12:13:13 +0100 Subject: [PATCH 65/70] Extract shared logic between special trait encoders --- .../encoders/ty/generics/builtin_trait/mod.rs | 64 ++++++++++++++++ .../ty/generics/builtin_trait/sized_trait.rs | 70 +++-------------- .../ty/generics/builtin_trait/tuple_trait.rs | 75 +++---------------- 3 files changed, 86 insertions(+), 123 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs b/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs index 0a10cc40730..67d0c21a1b6 100644 --- a/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs +++ b/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs @@ -1,2 +1,66 @@ +use crate::encoders::ty::{generics::r#trait::TraitEnc, lifted::TyConstructorEnc}; + +use task_encoder::TaskEncoder; pub mod sized_trait; pub mod tuple_trait; + +trait BuiltinTrait { + const NAME: &'static str; + const ARGS: <(vir::ManyTyVal, vir::ManyCSnap) as vir::Arity>::Tys<'static>; + type Encoder: for<'vir> TaskEncoder = Option>>; +} + +fn emit_builtin_trait_outputs<'vir, T>(program: &mut task_encoder::Program<'vir>) +where + T: BuiltinTrait, + T::Encoder: 'vir, +{ + vir::with_vcx(|vcx| { + let mut checks: Vec<_> = T::Encoder::all_outputs_local_no_errors() + .into_iter() + .flatten() + .collect(); + + let impl_idn = TraitEnc::trait_impl_idn(vcx, T::NAME, T::ARGS); + let impl_unknown_idn = TraitEnc::trait_unknown_impl_idn(vcx, T::NAME, T::ARGS); + + let self_decl = vcx.mk_local_decl("Self$0_trait", vir::TYPE_TYVAL); + + let self_expr = vcx.mk_local_ex(self_decl); + + let unknown_check = { + let is_unknown = + vcx.mk_adt_discriminator_expr(self_expr, TyConstructorEnc::UNKNOWN_TYPE_NAME); + let unknown_id = TyConstructorEnc::unknown_type_id_accessor(vcx).call()(self_expr); + + let unknown_impls = impl_unknown_idn.call()(unknown_id, &[], &[]); + + vir::expr! {vcx; (is_unknown) && (unknown_impls) } + }; + + checks.push(unknown_check); + + let ensures = vcx.mk_eq_expr(vcx.mk_result(vir::TYPE_BOOL), vcx.mk_disj(&checks)); + + let impl_fun = vcx.mk_function( + impl_idn, + (&[self_decl], &[]), + &[], + vcx.alloc_slice(&[ensures]), + Some(&vir::DecreasesGenData::Star), + None, + ); + program.add_function(impl_fun); + + let impl_unknown_fun = vcx.mk_domain_function(impl_unknown_idn, false, None); + + let domain = vcx.mk_domain( + TraitEnc::trait_domain_idn(vcx, T::NAME), + &[], + &[], + vcx.alloc_slice(&[impl_unknown_fun]), + None, + ); + program.add_domain(domain); + }); +} diff --git a/prusti-encoder/src/encoders/ty/generics/builtin_trait/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/builtin_trait/sized_trait.rs index a2a10dc993b..9af6aa9e7b3 100644 --- a/prusti-encoder/src/encoders/ty/generics/builtin_trait/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/builtin_trait/sized_trait.rs @@ -2,18 +2,22 @@ use crate::{ TaskEncoder, encoders::ty::{ RustTy, - generics::{GParams, r#trait::TraitEnc, trait_impls::TraitImplEnc}, - lifted::TyConstructorEnc, + generics::{GParams, trait_impls::TraitImplEnc}, }, }; use prusti_rustc_interface::middle::{ty, ty::Upcast}; use task_encoder::EncodeFullError; -pub struct SizedTraitEnc; +struct SizedTrait; + +impl super::BuiltinTrait for SizedTrait { + const NAME: &'static str = "Sized"; + const ARGS: <(vir::ManyTyVal, vir::ManyCSnap) as vir::Arity>::Tys<'static> = + (&[vir::TYPE_TYVAL], &[]); + type Encoder = SizedTraitEnc; +} -const SIZED_TRAIT_NAME: &str = "Sized"; -const SIZED_ARGS: <(vir::ManyTyVal, vir::ManyCSnap) as vir::Arity>::Tys<'static> = - (&[vir::TYPE_TYVAL], &[]); +pub struct SizedTraitEnc; impl TaskEncoder for SizedTraitEnc { task_encoder::encoder_cache!(SizedTraitEnc); @@ -54,63 +58,11 @@ impl TaskEncoder for SizedTraitEnc { } fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { - vir::with_vcx(|vcx| { - let mut checks: Vec<_> = Self::all_outputs_local_no_errors() - .into_iter() - .flatten() - .collect(); - - let sized_impl_idn = TraitEnc::trait_impl_idn(vcx, SIZED_TRAIT_NAME, SIZED_ARGS); - let sized_impl_unknown_idn = - TraitEnc::trait_unknown_impl_idn(vcx, SIZED_TRAIT_NAME, SIZED_ARGS); - - let self_decl = Self::sized_self_decl(vcx); - let self_expr = vcx.mk_local_ex(self_decl); - - let unknown_check = { - let is_unknown = - vcx.mk_adt_discriminator_expr(self_expr, TyConstructorEnc::UNKNOWN_TYPE_NAME); - let unknown_id = TyConstructorEnc::unknown_type_id_accessor(vcx).call()(self_expr); - - let unknown_impls = sized_impl_unknown_idn.call()(unknown_id, &[], &[]); - - vir::expr! {vcx; (is_unknown) && (unknown_impls) } - }; - - checks.push(unknown_check); - - let ensures = vcx.mk_eq_expr(vcx.mk_result(vir::TYPE_BOOL), vcx.mk_disj(&checks)); - - let sized_impl_fun = vcx.mk_function( - sized_impl_idn, - (&[self_decl], &[]), - &[], - vcx.alloc_slice(&[ensures]), - Some(&vir::DecreasesGenData::Star), - None, - ); - program.add_function(sized_impl_fun); - - let sized_impl_unknown_fun = - vcx.mk_domain_function(sized_impl_unknown_idn, false, None); - - let sized_domain = vcx.mk_domain( - TraitEnc::trait_domain_idn(vcx, SIZED_TRAIT_NAME), - &[], - &[], - vcx.alloc_slice(&[sized_impl_unknown_fun]), - None, - ); - program.add_domain(sized_domain); - }); + super::emit_builtin_trait_outputs::(program); } } impl SizedTraitEnc { - fn sized_self_decl<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::LocalDecl<'vir, vir::TyVal> { - vcx.mk_local_decl("Self$0_trait", vir::TYPE_TYVAL) - } - fn sizedness_check<'vir>( vcx: &'vir vir::VirCtxt<'vir>, deps: &mut task_encoder::TaskEncoderDependencies<'vir, SizedTraitEnc>, diff --git a/prusti-encoder/src/encoders/ty/generics/builtin_trait/tuple_trait.rs b/prusti-encoder/src/encoders/ty/generics/builtin_trait/tuple_trait.rs index 5aafc12e579..479543513c1 100644 --- a/prusti-encoder/src/encoders/ty/generics/builtin_trait/tuple_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/builtin_trait/tuple_trait.rs @@ -1,18 +1,19 @@ use crate::{ TaskEncoder, - encoders::ty::{ - RustTy, - generics::{r#trait::TraitEnc, trait_impls::TraitImplEnc}, - lifted::TyConstructorEnc, - }, + encoders::ty::{RustTy, generics::trait_impls::TraitImplEnc}, }; use prusti_rustc_interface::middle::ty; -pub struct TupleTraitEnc; +struct TupleTrait; -const TUPLE_TRAIT_NAME: &str = "Tuple"; -const TUPLE_TRAIT_ARGS: <(vir::ManyTyVal, vir::ManyCSnap) as vir::Arity>::Tys<'static> = - (&[vir::TYPE_TYVAL], &[]); +impl super::BuiltinTrait for TupleTrait { + const NAME: &'static str = "Tuple"; + const ARGS: <(vir::ManyTyVal, vir::ManyCSnap) as vir::Arity>::Tys<'static> = + (&[vir::TYPE_TYVAL], &[]); + type Encoder = TupleTraitEnc; +} + +pub struct TupleTraitEnc; impl TaskEncoder for TupleTraitEnc { task_encoder::encoder_cache!(TupleTraitEnc); @@ -58,60 +59,6 @@ impl TaskEncoder for TupleTraitEnc { } fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { - vir::with_vcx(|vcx| { - let mut checks: Vec<_> = Self::all_outputs_local_no_errors() - .into_iter() - .flatten() - .collect(); - - let tuple_impl_idn = TraitEnc::trait_impl_idn(vcx, TUPLE_TRAIT_NAME, TUPLE_TRAIT_ARGS); - let tuple_impl_unknown_idn = - TraitEnc::trait_unknown_impl_idn(vcx, TUPLE_TRAIT_NAME, TUPLE_TRAIT_ARGS); - - let self_decl = Self::tuple_self_decl(vcx); - let self_expr = vcx.mk_local_ex(self_decl); - - let unknown_check = { - let is_unknown = - vcx.mk_adt_discriminator_expr(self_expr, TyConstructorEnc::UNKNOWN_TYPE_NAME); - let unknown_id = TyConstructorEnc::unknown_type_id_accessor(vcx).call()(self_expr); - - let unknown_impls = tuple_impl_unknown_idn.call()(unknown_id, &[], &[]); - - vir::expr! {vcx; (is_unknown) && (unknown_impls) } - }; - - checks.push(unknown_check); - - let ensures = vcx.mk_eq_expr(vcx.mk_result(vir::TYPE_BOOL), vcx.mk_disj(&checks)); - - let tuple_impl_idn = vcx.mk_function( - tuple_impl_idn, - (&[self_decl], &[]), - &[], - vcx.alloc_slice(&[ensures]), - Some(&vir::DecreasesGenData::Star), - None, - ); - program.add_function(tuple_impl_idn); - - let tuple_impl_unknown_fun = - vcx.mk_domain_function(tuple_impl_unknown_idn, false, None); - - let sized_domain = vcx.mk_domain( - TraitEnc::trait_domain_idn(vcx, TUPLE_TRAIT_NAME), - &[], - &[], - vcx.alloc_slice(&[tuple_impl_unknown_fun]), - None, - ); - program.add_domain(sized_domain); - }); - } -} - -impl TupleTraitEnc { - fn tuple_self_decl<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::LocalDecl<'vir, vir::TyVal> { - vcx.mk_local_decl("Self$0_trait", vir::TYPE_TYVAL) + super::emit_builtin_trait_outputs::(program); } } From 048c487e39987eeb49c43255c36b0f07bcd9998b Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Fri, 20 Mar 2026 11:50:12 +0100 Subject: [PATCH 66/70] Fix type erasure for closures --- prusti-encoder/src/encoders/ty/rust_ty.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index 78b0f3735bd..f78cedaca9f 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -350,9 +350,12 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { match ty.kind() { _ if ty.is_primitive() => Self::prim_ty_name(ty), ty::TyKind::Str => String::from("Str"), - ty::TyKind::Adt(adt, _) => { - vir::with_vcx(|vcx| vcx.tcx().item_name(adt.did()).to_ident_string()) - } + ty::TyKind::Adt(adt, _) => vir::with_vcx(|vcx| { + let did = adt.did(); + let base_name = vcx.tcx().item_name(did).to_ident_string(); + let hash = vcx.tcx().def_path_hash(did).0.to_smaller_hash().as_u64(); + format!("{base_name}_{hash:x}") + }), ty::TyKind::Tuple(params) => format!("{}_Tuple", params.len()), ty::TyKind::Never => String::from("Never"), ty::TyKind::Ref(_, _, ty::Mutability::Not) => String::from("Ref_immutable"), @@ -464,10 +467,19 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { } ty::TyKind::Closure(did, args) => { let identity = ty::List::identity_for_item(tcx, did); + // We only want to fully erase the parent information + let parts = ty::ClosureArgsParts { + parent_args: identity.as_closure().parent_args(), + closure_kind_ty: tcx.erase_regions(args.as_closure().kind_ty()), + closure_sig_as_fn_ptr_ty: tcx + .erase_regions(args.as_closure().sig_as_fn_ptr_ty()), + tupled_upvars_ty: tcx.erase_regions(args.as_closure().tupled_upvars_ty()), + }; + let erased = ty::ClosureArgs::new(tcx, parts); let gargs = tcx.mk_args(identity.as_closure().parent_args()); let args = tcx.mk_args(args.as_closure().parent_args()); ( - ty::Ty::new_closure(tcx, did, identity), + ty::Ty::new_closure(tcx, did, erased.args), GParams::new(gargs, tcx.param_env(did), is_trait_extern_spec), args, ) From 22e7486c7dad09fd7527b42d3ad067fb77f9bef8 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Fri, 20 Mar 2026 11:59:37 +0100 Subject: [PATCH 67/70] Update the erased_ty accessor --- prusti-encoder/src/encoders/ty/generics/sized_trait.rs | 2 +- prusti-encoder/src/encoders/ty/rust_ty.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index a2a10dc993b..05663bbfe9c 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -33,7 +33,7 @@ impl TaskEncoder for SizedTraitEnc { deps.emit_output_ref(*task_key, ())?; vir::with_vcx(|vcx| { - let ty = task_key.erased_ty_for_sizedness(); + let ty = task_key.erased_ty_for_special_traits(); let sizedness = sizedness_for_ty(vcx.tcx(), ty); let check = match sizedness { Sizedness::Unsized => None, diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index f78cedaca9f..b737331427f 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -252,11 +252,11 @@ impl<'tcx> RustTyData<'tcx> { self.name.as_str() } - /// NOTE: this is a temporary hack to get the `ty::Ty` for the `SizedTraitEnc`. Should not be - /// used in other places - pub(super) fn erased_ty_for_sizedness(&self) -> ty::Ty<'tcx> { + /// NOTE: a hack to get the `ty::Ty` to the encoders for special traits like `Sized` or `Tuple`. + /// Should not be used for other purposes + pub(super) fn erased_ty_for_special_traits(&self) -> ty::Ty<'tcx> { self.erased_ty - .expect("erased_ty should be Some for the `Sized` trait encoder") + .expect("should be `Some` when called in special trait encoders") } } From 68bbc87d53b012bf303b94989cc8f8cf67445bd8 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Fri, 20 Mar 2026 14:26:38 +0100 Subject: [PATCH 68/70] Use trait enc output for encoding sized --- .../src/encoders/ty/generics/sized_trait.rs | 67 ++++++++++------ .../src/encoders/ty/generics/trait.rs | 77 +++++++------------ prusti-encoder/src/encoders/ty/rust_ty.rs | 9 +-- prusti-encoder/src/encoders/ty/use_impure.rs | 13 ++-- 4 files changed, 79 insertions(+), 87 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs index 05663bbfe9c..647c9f2c627 100644 --- a/prusti-encoder/src/encoders/ty/generics/sized_trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/sized_trait.rs @@ -2,24 +2,29 @@ use crate::{ TaskEncoder, encoders::ty::{ RustTy, - generics::{GParams, r#trait::TraitEnc, trait_impls::TraitImplEnc}, + generics::{ + GParams, GenericParams, GenericParamsEnc, r#trait::TraitEnc, trait_impls::TraitImplEnc, + }, lifted::TyConstructorEnc, }, }; use prusti_rustc_interface::middle::{ty, ty::Upcast}; use task_encoder::EncodeFullError; +use vir::vir_format_identifier; pub struct SizedTraitEnc; -const SIZED_TRAIT_NAME: &str = "Sized"; -const SIZED_ARGS: <(vir::ManyTyVal, vir::ManyCSnap) as vir::Arity>::Tys<'static> = - (&[vir::TYPE_TYVAL], &[]); - impl TaskEncoder for SizedTraitEnc { task_encoder::encoder_cache!(SizedTraitEnc); type TaskDescription<'vir> = RustTy<'vir>; - type OutputFullLocal<'vir> = Option>; + type OutputFullLocal<'vir> = ( + // This will be unfortunately copied with every type that the `Sized` encoder is called + // with + ::OutputRef<'vir>, + GenericParams<'vir>, + Option>, + ); fn task_to_key<'vir>(task: &Self::TaskDescription<'vir>) -> Self::TaskKey<'vir> { *task @@ -33,6 +38,14 @@ impl TaskEncoder for SizedTraitEnc { deps.emit_output_ref(*task_key, ())?; vir::with_vcx(|vcx| { + let sized_did = vcx.tcx().lang_items().sized_trait().unwrap(); + let sized_trait = deps.require_ref::(sized_did)?; + + let trait_generics = { + let params = TraitEnc::trait_params(sized_did); + deps.require_dep::(params)? + }; + let ty = task_key.erased_ty_for_special_traits(); let sizedness = sizedness_for_ty(vcx.tcx(), ty); let check = match sizedness { @@ -49,53 +62,61 @@ impl TaskEncoder for SizedTraitEnc { )?), }; - Ok((check, ())) + Ok(((sized_trait, trait_generics, check), ())) }) } fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) { + let outputs = Self::all_outputs_local_no_errors(); + let Some((sized_trait, sized_generics, _)) = outputs.first() else { + return; + }; vir::with_vcx(|vcx| { let mut checks: Vec<_> = Self::all_outputs_local_no_errors() .into_iter() - .flatten() + .filter_map(|(.., check)| check) .collect(); - let sized_impl_idn = TraitEnc::trait_impl_idn(vcx, SIZED_TRAIT_NAME, SIZED_ARGS); - let sized_impl_unknown_idn = - TraitEnc::trait_unknown_impl_idn(vcx, SIZED_TRAIT_NAME, SIZED_ARGS); + let sized_impl_fun = sized_trait.impl_fun; + let sized_impl_for_unknown_fun = sized_trait.impl_for_unknown_fun; - let self_decl = Self::sized_self_decl(vcx); - let self_expr = vcx.mk_local_ex(self_decl); + let unknown_type_check = { + let self_expr = sized_generics.ty_exprs()[0]; - let unknown_check = { let is_unknown = vcx.mk_adt_discriminator_expr(self_expr, TyConstructorEnc::UNKNOWN_TYPE_NAME); - let unknown_id = TyConstructorEnc::unknown_type_id_accessor(vcx).call()(self_expr); + let extracted_id = + TyConstructorEnc::unknown_type_id_accessor(vcx).call()(self_expr); - let unknown_impls = sized_impl_unknown_idn.call()(unknown_id, &[], &[]); + let unknown_impls = sized_impl_for_unknown_fun.call()( + extracted_id, + &sized_generics.ty_exprs()[1..], + sized_generics.const_exprs(), + ); vir::expr! {vcx; (is_unknown) && (unknown_impls) } }; - checks.push(unknown_check); + checks.push(unknown_type_check); let ensures = vcx.mk_eq_expr(vcx.mk_result(vir::TYPE_BOOL), vcx.mk_disj(&checks)); let sized_impl_fun = vcx.mk_function( - sized_impl_idn, - (&[self_decl], &[]), + sized_impl_fun, + (sized_generics.ty_decls(), sized_generics.const_decls()), &[], vcx.alloc_slice(&[ensures]), Some(&vir::DecreasesGenData::Star), None, ); + program.add_function(sized_impl_fun); let sized_impl_unknown_fun = - vcx.mk_domain_function(sized_impl_unknown_idn, false, None); + vcx.mk_domain_function(sized_impl_for_unknown_fun, false, None); let sized_domain = vcx.mk_domain( - TraitEnc::trait_domain_idn(vcx, SIZED_TRAIT_NAME), + vir_format_identifier!(vcx, "trait_{}", sized_trait.trait_name), &[], &[], vcx.alloc_slice(&[sized_impl_unknown_fun]), @@ -107,10 +128,6 @@ impl TaskEncoder for SizedTraitEnc { } impl SizedTraitEnc { - fn sized_self_decl<'vir>(vcx: &'vir vir::VirCtxt<'vir>) -> vir::LocalDecl<'vir, vir::TyVal> { - vcx.mk_local_decl("Self$0_trait", vir::TYPE_TYVAL) - } - fn sizedness_check<'vir>( vcx: &'vir vir::VirCtxt<'vir>, deps: &mut task_encoder::TaskEncoderDependencies<'vir, SizedTraitEnc>, diff --git a/prusti-encoder/src/encoders/ty/generics/trait.rs b/prusti-encoder/src/encoders/ty/generics/trait.rs index a5a5f900495..8687aabd57b 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait.rs @@ -1,7 +1,7 @@ use prusti_rustc_interface::{middle::ty, span::def_id::DefId}; use rustc_hash::FxHashMap; use task_encoder::{EncodeFullResult, OutputRefAny, TaskEncoder, TaskEncoderDependencies}; -use vir::{Arity, FunctionIdn, vir_format_identifier}; +use vir::{FunctionIdn, vir_format_identifier}; use crate::encoders::ty::{ RustTyDecomposition, @@ -20,6 +20,8 @@ pub struct TraitEncOutputRef<'vir> { pub assoc_consts: FxHashMap>, pub impl_fun: FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, + pub impl_for_unknown_fun: + FunctionIdn<'vir, (vir::Int, vir::ManyTyVal, vir::ManyCSnap), vir::Bool>, } #[derive(Debug, Clone)] @@ -103,11 +105,21 @@ impl TaskEncoder for TraitEnc { } } - let impl_fun = Self::trait_impl_idn( - vcx, - trait_name, - (trait_generics.ty_args(), trait_generics.const_args()), + let impl_fun = FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_impl"), + trait_args, + vir::TYPE_BOOL, ); + let impl_for_unknown_fun = { + // Omit the `Self` type as it is known to be the unknown type + let ty_args = &trait_args.0[1..]; + let const_args = trait_args.1; + FunctionIdn::new( + vir_format_identifier!(vcx, "{trait_name}_impl_for_unknown"), + (vir::TYPE_INT, ty_args, const_args), + vir::TYPE_BOOL, + ) + }; // Emit the impl function reference early, so that it can be used to encode caller // bounds without causing dependency cycles. @@ -118,6 +130,7 @@ impl TaskEncoder for TraitEnc { assoc_types, assoc_consts, impl_fun, + impl_for_unknown_fun, }, )?; @@ -126,8 +139,6 @@ impl TaskEncoder for TraitEnc { return Ok((None, ())); } - let impl_for_unknown_idn = Self::trait_unknown_impl_idn(vcx, trait_name, trait_args); - let impl_fun_body = { let mut trait_impl_checks: Vec<_> = tcx .all_impls(*task_key) @@ -138,8 +149,7 @@ impl TaskEncoder for TraitEnc { }) .collect(); - // Case for unknown types - { + let unknown_type_check = { let self_expr = trait_generics.ty_exprs()[0]; let is_unknown_type = vcx @@ -148,18 +158,17 @@ impl TaskEncoder for TraitEnc { let extracted_id = TyConstructorEnc::unknown_type_id_accessor(vcx).call()(self_expr); - let unknown_impls = impl_for_unknown_idn( + let unknown_impls = impl_for_unknown_fun( extracted_id, &trait_generics.ty_exprs()[1..], trait_generics.const_exprs(), ); - let unknown_check = vir::expr! { vcx; + vir::expr! { vcx; (is_unknown_type) && (unknown_impls) - }; - - trait_impl_checks.push(unknown_check); - } + } + }; + trait_impl_checks.push(unknown_type_check); vcx.mk_disj(&trait_impl_checks) }; @@ -175,11 +184,11 @@ impl TaskEncoder for TraitEnc { None, ); - let impl_for_unknown_fun = vcx.mk_domain_function(impl_for_unknown_idn, false, None); + let impl_for_unknown_fun = vcx.mk_domain_function(impl_for_unknown_fun, false, None); dom_funcs.push(impl_for_unknown_fun); let trait_domain = vcx.mk_domain( - Self::trait_domain_idn(vcx, trait_name), + vir_format_identifier!(vcx, "trait_{trait_name}"), &[], &[], vcx.alloc_slice(&dom_funcs), @@ -198,40 +207,6 @@ impl TaskEncoder for TraitEnc { } impl TraitEnc { - pub(super) fn trait_impl_idn<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - trait_name: &'a str, - args: <(vir::ManyTyVal, vir::ManyCSnap) as Arity>::Tys<'vir>, - ) -> FunctionIdn<'vir, (vir::ManyTyVal, vir::ManyCSnap), vir::Bool> { - FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_impl"), - args, - vir::TYPE_BOOL, - ) - } - - pub(super) fn trait_unknown_impl_idn<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - trait_name: &'a str, - args: <(vir::ManyTyVal, vir::ManyCSnap) as Arity>::Tys<'vir>, - ) -> FunctionIdn<'vir, (vir::Int, vir::ManyTyVal, vir::ManyCSnap), vir::Bool> { - // Omit the `Self` type as it is known to be the unknown type - let ty_args = &args.0[1..]; - let const_args = args.1; - FunctionIdn::new( - vir_format_identifier!(vcx, "{trait_name}_impl_for_unknown"), - (vir::TYPE_INT, ty_args, const_args), - vir::TYPE_BOOL, - ) - } - - pub(super) fn trait_domain_idn<'vir, 'a>( - vcx: &'vir vir::VirCtxt<'vir>, - trait_name: &'a str, - ) -> vir::ViperIdent<'vir> { - vir_format_identifier!(vcx, "trait_{trait_name}") - } - pub(super) fn trait_params<'tcx>(trait_did: DefId) -> GParams<'tcx> { GParams::from(trait_did).with_suffix("trait") } diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index b737331427f..f84f9fa4099 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -350,12 +350,9 @@ impl<'tcx> TyData<'tcx, RustTyDatas> { match ty.kind() { _ if ty.is_primitive() => Self::prim_ty_name(ty), ty::TyKind::Str => String::from("Str"), - ty::TyKind::Adt(adt, _) => vir::with_vcx(|vcx| { - let did = adt.did(); - let base_name = vcx.tcx().item_name(did).to_ident_string(); - let hash = vcx.tcx().def_path_hash(did).0.to_smaller_hash().as_u64(); - format!("{base_name}_{hash:x}") - }), + ty::TyKind::Adt(adt, _) => { + vir::with_vcx(|vcx| vcx.tcx().item_name(adt.did()).to_ident_string()) + } ty::TyKind::Tuple(params) => format!("{}_Tuple", params.len()), ty::TyKind::Never => String::from("Never"), ty::TyKind::Ref(_, _, ty::Mutability::Not) => String::from("Ref_immutable"), diff --git a/prusti-encoder/src/encoders/ty/use_impure.rs b/prusti-encoder/src/encoders/ty/use_impure.rs index 48c069bead0..11dc83327ce 100644 --- a/prusti-encoder/src/encoders/ty/use_impure.rs +++ b/prusti-encoder/src/encoders/ty/use_impure.rs @@ -1,5 +1,5 @@ use prusti_rustc_interface::abi; -use task_encoder::{EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; +use task_encoder::{EncodeFullError, EncodeFullResult, TaskEncoder, TaskEncoderDependencies}; use vir::{CastType, PredicateIdn}; use crate::encoders::{ @@ -119,7 +119,7 @@ impl TaskEncoder for TyUseImpureEnc { deps.emit_output_ref(*task_key, ())?; let ty_impure = deps.require_dep::(task_key.ty)?; - let mut walker = TyUseImpureWalker::new(deps, task_key.args); + let mut walker = TyUseImpureWalker::new(deps, task_key.args)?; let ty_use_impure = walker.encode_ty(task_key.ty.zip(ty_impure), task_key.maybe_inhabited); Ok(((), ty_use_impure.alloc())) } @@ -136,9 +136,12 @@ struct TyUseImpureWalker<'a, 'vir> { } impl<'a, 'vir> TyUseImpureWalker<'a, 'vir> { - fn new(deps: &'a mut TaskEncoderDependencies<'vir, TyUseImpureEnc>, args: GArgs<'vir>) -> Self { - let args_t = deps.require_dep::(args).unwrap(); - Self { deps, args_t, args } + fn new( + deps: &'a mut TaskEncoderDependencies<'vir, TyUseImpureEnc>, + args: GArgs<'vir>, + ) -> Result> { + let args_t = deps.require_dep::(args)?; + Ok(Self { deps, args_t, args }) } fn encode_ty( From 02ba27cb1f60374f0d44ea74fe4151461b3a39bc Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 24 Mar 2026 12:11:11 +0100 Subject: [PATCH 69/70] Fix typo + move lifetime bound --- .../src/encoders/ty/generics/builtin_trait/mod.rs | 10 +++++----- prusti-encoder/src/encoders/ty/rust_ty.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs b/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs index 7026cc87b63..8300b470f56 100644 --- a/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs +++ b/prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs @@ -18,7 +18,7 @@ pub type TupleTraitEnc = BuiltinTraitEnc; /// /// This trait defines the interface that marker types must implement to work with /// `SpecialTraitEnc`. The marker types should be zero-sized structs that are `'static`. -trait BuiltinTrait: 'static { +trait BuiltinTrait { /// Returns the DefId of this builtin trait. fn def_id() -> DefId; @@ -36,14 +36,14 @@ trait BuiltinTrait: 'static { ty: ty::Ty<'vir>, ) -> Result>, EncodeFullError<'vir, BuiltinTraitEnc>> where - Self: Sized; + Self: Sized + 'static; /// Provides access to the encoder cache for this builtin trait. /// /// This should be implemented using the `task_encoder::encoder_cache!` macro. fn with_cache<'vir, F, R>(f: F) -> R where - Self: Sized, + Self: Sized + 'static, F: FnOnce(&'vir CacheRef<'vir, BuiltinTraitEnc>) -> R; } @@ -83,7 +83,7 @@ pub enum BuiltinTraitEncOutput<'a> { TypeCheck(Option>), } -impl TaskEncoder for BuiltinTraitEnc { +impl TaskEncoder for BuiltinTraitEnc { const ENCODER_NAME: &'static str = "builtin trait encoder"; // Need to delegate to the `BuiltinTrait` to implement the `with_cache` due to issues // described in `task_encoder::encoder_cache!` @@ -124,7 +124,7 @@ impl TaskEncoder for BuiltinTraitEnc { BuiltinTraitEncTask::Encode(rust_ty) => { assert!(!rust_ty.specifics.is_param()); - let ty = rust_ty.erased_ty_for_buitin_traits(); + let ty = rust_ty.erased_ty_for_builtin_traits(); let check = T::does_impl(deps, rust_ty.params, ty)?; diff --git a/prusti-encoder/src/encoders/ty/rust_ty.rs b/prusti-encoder/src/encoders/ty/rust_ty.rs index fc1486ec80d..ba9d076c999 100644 --- a/prusti-encoder/src/encoders/ty/rust_ty.rs +++ b/prusti-encoder/src/encoders/ty/rust_ty.rs @@ -254,7 +254,7 @@ impl<'tcx> RustTyData<'tcx> { /// NOTE: a hack to get the `ty::Ty` to the encoders for builtin traits like `Sized` or `Tuple`. /// Should not be used for other purposes - pub(super) fn erased_ty_for_buitin_traits(&self) -> ty::Ty<'tcx> { + pub(super) fn erased_ty_for_builtin_traits(&self) -> ty::Ty<'tcx> { self.erased_ty .expect("should be `Some` when called in special trait encoders") } From 42720c1a57f486ec422cbb924c805aa3fe0af0a1 Mon Sep 17 00:00:00 2001 From: Trzyq0712 Date: Tue, 24 Mar 2026 12:14:16 +0100 Subject: [PATCH 70/70] Fmt --- prusti-encoder/src/encoders/ty/generics/trait.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prusti-encoder/src/encoders/ty/generics/trait.rs b/prusti-encoder/src/encoders/ty/generics/trait.rs index f4ea6f2b715..70b07be215c 100644 --- a/prusti-encoder/src/encoders/ty/generics/trait.rs +++ b/prusti-encoder/src/encoders/ty/generics/trait.rs @@ -6,8 +6,8 @@ use vir::{FunctionIdn, vir_format_identifier}; use crate::encoders::ty::{ RustTyDecomposition, generics::{ - GParams, GenericParamsEnc, - builtin_trait::{SizedTraitEnc, TupleTraitEnc, BuiltinTraitEncTask}, + GParams, GenericParamsEnc, + builtin_trait::{BuiltinTraitEncTask, SizedTraitEnc, TupleTraitEnc}, trait_impls::TraitImplEnc, }, lifted::TyConstructorEnc,