Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
a8eeb11
Trait impl check for simple types
Trzyq0712 Feb 15, 2026
4a99e61
Trait impl check for unknown types
Trzyq0712 Feb 15, 2026
5a59172
Change the encoding to use exists clauses
Trzyq0712 Feb 16, 2026
ec81bef
Fix impl_unknown to contain the trait generics
Trzyq0712 Feb 17, 2026
25655ff
Include the check for the unknown type in the impl function
Trzyq0712 Feb 17, 2026
5bd4313
Encode trait bound requirements
Trzyq0712 Feb 17, 2026
7abcedd
Emit early impl_fun ref to avoid dependency cycles
Trzyq0712 Feb 17, 2026
464843f
Modify encoding to avoid using existentials
Trzyq0712 Feb 18, 2026
16f06fd
Clean up the type check encoding function
Trzyq0712 Feb 18, 2026
20ebaac
Clean up of the impl_fun_body
Trzyq0712 Feb 18, 2026
430abcc
Fix wrong encoding of trait generic arguments
Trzyq0712 Feb 18, 2026
182b988
Add corrected trait bound checks
Trzyq0712 Feb 18, 2026
2386ad0
Remove existential from unknown type check
Trzyq0712 Feb 18, 2026
fbda835
Add initial special encoding for the `Sized` trait
Trzyq0712 Feb 18, 2026
df3b682
Add const generics to trait impl function defs
Trzyq0712 Feb 19, 2026
c4c9625
Implement handling of const generics in trait impls
Trzyq0712 Feb 19, 2026
b8f07f6
Correctly handle concretely valued const generics
Trzyq0712 Feb 19, 2026
bd9383e
Implement the Sized check without opaque and unknown
Trzyq0712 Feb 20, 2026
88e31a4
Move sizedness resolution to RustTy
Trzyq0712 Feb 21, 2026
1f57cfb
Add unknown type check for the sized trait
Trzyq0712 Feb 22, 2026
36119b0
Make sizedness check work in general case
Trzyq0712 Feb 22, 2026
f9366a3
Initial trait associated type predicates
Trzyq0712 Feb 22, 2026
ce008f0
Const generics in trait impls
Trzyq0712 Feb 22, 2026
85ac4ec
Remove full dep output for trait encoder
Trzyq0712 Feb 22, 2026
94802de
Make assoc const Snap + more assoc type bound cases
Trzyq0712 Feb 23, 2026
8cbb1a6
Discover generics inside projection terms
Trzyq0712 Feb 23, 2026
d26a9ff
Fix GATs
Trzyq0712 Feb 24, 2026
07989fe
Clean up the implementation
Trzyq0712 Feb 24, 2026
fa04be0
Collect const generics from projections
Trzyq0712 Feb 24, 2026
9697d20
Naming consistency fix
Trzyq0712 Feb 24, 2026
f38ebd7
Remove a dbg
Trzyq0712 Feb 25, 2026
3725126
Integrate context erasure for types into `identity_for_ty`
Trzyq0712 Feb 25, 2026
09b19b1
Resolve some issues and clean up in `rust_ty.rs`
JonasAlaif Feb 25, 2026
60f43b0
Revert unnecessary change
JonasAlaif Feb 25, 2026
16c5294
Add support of checking sizedness of aliases
Trzyq0712 Feb 25, 2026
2377ab6
Move trait impl check to impl encoder
Trzyq0712 Feb 25, 2026
bb78ada
Seperate encoder for the sized trait
Trzyq0712 Feb 26, 2026
ee87b3e
Remove type aliases
Trzyq0712 Feb 26, 2026
1826fbc
Skip emitting local output for `Sized` trait
Trzyq0712 Feb 26, 2026
a478d40
Clippy
Trzyq0712 Feb 26, 2026
9a6a3ed
Revert some unnecessary changes
Trzyq0712 Feb 26, 2026
1cfc892
Match parts of trait_impls with #127
Trzyq0712 Feb 26, 2026
4a133a8
Remove unnecessary rename
Trzyq0712 Feb 26, 2026
7ebc976
Omit dependent sizedness check if it is known to be sized
Trzyq0712 Feb 26, 2026
48ae48e
Revert unnecessary change
Trzyq0712 Feb 26, 2026
4efe4a0
Cleanup, restructuring and renaming
JonasAlaif Feb 26, 2026
230fbb0
Emit the domain for the Sized trait
Trzyq0712 Feb 27, 2026
bd641ce
Correctly implement the `Alias` case of `ty_expr`
JonasAlaif Feb 27, 2026
abf0947
Simplify the task for SizedTraitEnc
Trzyq0712 Feb 27, 2026
42d860c
Add optional suffix for generics renaming
Trzyq0712 Mar 3, 2026
01bf445
Simplified clauses encoding prototype
Trzyq0712 Mar 3, 2026
1f6bdb1
Let binding encoding
Trzyq0712 Mar 4, 2026
f42b721
Clean up trait impl check
Trzyq0712 Mar 4, 2026
acb6357
Simplify encoding of Sized
Trzyq0712 Mar 4, 2026
489f3e9
Remove sizedness from `RustTy`
Trzyq0712 Mar 9, 2026
96e7f3b
Merge branch 'rewrite-2023' into trait_impls
Trzyq0712 Mar 10, 2026
1a27b01
Merge imports
Trzyq0712 Mar 10, 2026
60b36a4
Remove unnecessary changes in `TyConstructorEnc`
Trzyq0712 Mar 10, 2026
7bb452a
Return `Result`s instead of unwrapping to avoid cyclic errors
Trzyq0712 Mar 10, 2026
8288aa3
Move trait check into an `ensures` instead of the body
Trzyq0712 Mar 10, 2026
687f26d
Accessor for the `erased_ty`
Trzyq0712 Mar 11, 2026
bf3bbf2
Clippy
Trzyq0712 Mar 11, 2026
392e097
fmt
Trzyq0712 Mar 15, 2026
922bcd8
Implement the `Tuple` trait encoder
Trzyq0712 Mar 16, 2026
fd22779
Move special traits to a separate module
Trzyq0712 Mar 17, 2026
bbb9e81
Extract shared logic between special trait encoders
Trzyq0712 Mar 17, 2026
132a274
Merge branch 'rewrite-2023' into trait_impls
Trzyq0712 Mar 20, 2026
048c487
Fix type erasure for closures
Trzyq0712 Mar 20, 2026
22e7486
Update the erased_ty accessor
Trzyq0712 Mar 20, 2026
68bbc87
Use trait enc output for encoding sized
Trzyq0712 Mar 20, 2026
8f9201d
Merge branch 'trait_impls' into tuple_trait
Trzyq0712 Mar 21, 2026
a095e11
Merge remote-tracking branch 'upstream/rewrite-2023' into tuple_trait
Trzyq0712 Mar 24, 2026
02ba27c
Fix typo + move lifetime bound
Trzyq0712 Mar 24, 2026
42720c1
Fmt
Trzyq0712 Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion prusti-encoder/src/encoders/ty/generics/args_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl TaskEncoder for GArgsTyEnc {
let decomp = RustTyDecomposition::from_ty(arg, task_key.context);
params.ty_expr(deps, decomp)
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, _>>()?;
let const_args = task_key
.args
.iter()
Expand Down
206 changes: 206 additions & 0 deletions prusti-encoder/src/encoders/ty/generics/builtin_trait/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
use crate::encoders::ty::{
RustTy,
generics::{GParams, GenericParams, GenericParamsEnc, r#trait::TraitEnc},
lifted::TyConstructorEnc,
};
use prusti_rustc_interface::{middle::ty, span::def_id::DefId};

use task_encoder::{CacheRef, EncodeFullError, TaskEncoder};
use vir::vir_format_identifier;

pub mod sized_trait;
pub mod tuple_trait;

pub type SizedTraitEnc = BuiltinTraitEnc<sized_trait::SizedTrait>;
pub type TupleTraitEnc = BuiltinTraitEnc<tuple_trait::TupleTrait>;

/// Trait that must be implemented by all builtin trait markers.
///
/// This trait defines the interface that marker types must implement to work with
/// `SpecialTraitEnc<T>`. The marker types should be zero-sized structs that are `'static`.
trait BuiltinTrait {
/// Returns the DefId of this builtin trait.
fn def_id() -> DefId;

/// Returns the expression representing whether the given type implements this trait.
///
/// # Returns
///
/// - `Ok(Some(expr))` - The type implements the trait; `expr` is a boolean expression
/// that evaluates to true when the trait is implemented
/// - `Ok(None)` - The type definitely does not implement this trait
/// - `Err(_)` - An error occurred during encoding
fn does_impl<'vir>(
deps: &mut task_encoder::TaskEncoderDependencies<'vir, BuiltinTraitEnc<Self>>,
ctx: GParams<'vir>,
ty: ty::Ty<'vir>,
) -> Result<Option<vir::ExprBool<'vir>>, EncodeFullError<'vir, BuiltinTraitEnc<Self>>>
where
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 + 'static,
F: FnOnce(&'vir CacheRef<'vir, BuiltinTraitEnc<Self>>) -> R;
}

/// Generic wrapper for encoding builtin traits.
///
/// This struct wraps a marker type `T` that implements `BuiltinTrait` and provides
/// a `TaskEncoder` implementation for it. The wrapper handles the common encoding
/// logic while delegating trait-specific decisions to the marker type.
pub struct BuiltinTraitEnc<T>(std::marker::PhantomData<T>);

#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
pub enum BuiltinTraitEncTask<'a> {
Activate,
Encode(RustTy<'a>),
}

#[derive(Clone, Debug)]
pub struct TraitData<'a> {
trait_: <TraitEnc as TaskEncoder>::OutputRef<'a>,
trait_generics: GenericParams<'a>,
}
impl<'a> TraitData<'a> {
fn new(
trait_: <TraitEnc as TaskEncoder>::OutputRef<'a>,
trait_generics: GenericParams<'a>,
) -> Box<Self> {
Box::new(Self {
trait_,
trait_generics,
})
}
}

#[derive(Clone, Debug)]
pub enum BuiltinTraitEncOutput<'a> {
Activated(Box<TraitData<'a>>),
TypeCheck(Option<vir::ExprBool<'a>>),
}

impl<T: BuiltinTrait + 'static> TaskEncoder for BuiltinTraitEnc<T> {
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!`
fn with_cache<'vir, F, R>(f: F) -> R
where
F: FnOnce(&'vir task_encoder::CacheRef<'vir, Self>) -> R,
T: 'vir,
{
T::with_cache(f)
}

type TaskDescription<'vir> = BuiltinTraitEncTask<'vir>;
type OutputFullLocal<'vir> = BuiltinTraitEncOutput<'vir>;

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, ())?;
match task_key {
BuiltinTraitEncTask::Activate => {
let trait_did = T::def_id();
let trait_ = deps.require_ref::<TraitEnc>(trait_did)?;

let trait_generics = {
let params = TraitEnc::trait_params(trait_did);
deps.require_dep::<GenericParamsEnc>(params)?
};
Ok((
(BuiltinTraitEncOutput::Activated(TraitData::new(trait_, trait_generics))),
(),
))
}
BuiltinTraitEncTask::Encode(rust_ty) => {
assert!(!rust_ty.specifics.is_param());

let ty = rust_ty.erased_ty_for_builtin_traits();

let check = T::does_impl(deps, rust_ty.params, ty)?;

Ok(((BuiltinTraitEncOutput::TypeCheck(check)), ()))
}
}
}

fn emit_outputs<'vir>(program: &mut task_encoder::Program<'vir>) {
let outputs = Self::all_outputs_local_no_errors(program);

let mut trait_info = None;
let mut checks = Vec::new();

for output in outputs {
match output {
BuiltinTraitEncOutput::Activated(box data) => {
trait_info = Some((data.trait_, data.trait_generics));
}
BuiltinTraitEncOutput::TypeCheck(Some(check)) => {
checks.push(check);
}
_ => {}
}
}
let Some((trait_, trait_generics)) = trait_info else {
return;
};

vir::with_vcx(|vcx| {
let trait_impl_fun = trait_.impl_fun;
let trait_impl_for_unknown_fun = trait_.impl_for_unknown_fun;

let unknown_type_check = {
let self_expr = trait_generics.ty_exprs()[0];

let is_unknown =
vcx.mk_adt_discriminator_expr(self_expr, TyConstructorEnc::UNKNOWN_TYPE_NAME);
let extracted_id =
TyConstructorEnc::unknown_type_id_accessor(vcx).call()(self_expr);

let unknown_impls = trait_impl_for_unknown_fun.call()(
extracted_id,
&trait_generics.ty_exprs()[1..],
trait_generics.const_exprs(),
);

vir::expr! {vcx; (is_unknown) && (unknown_impls) }
};

checks.push(unknown_type_check);

let ensures = vcx.mk_eq_expr(vcx.mk_result(vir::TYPE_BOOL), vcx.mk_disj(&checks));

let trait_impl_fun = vcx.mk_function(
trait_impl_fun,
(trait_generics.ty_decls(), trait_generics.const_decls()),
&[],
vcx.alloc_slice(&[ensures]),
Some(&vir::DecreasesGenData::Star),
None,
);

program.add_function(trait_impl_fun);

let trait_impl_unknown_fun =
vcx.mk_domain_function(trait_impl_for_unknown_fun, false, None);

let trait_domain = vcx.mk_domain(
vir_format_identifier!(vcx, "trait_{}", trait_.trait_name),
&[],
&[],
vcx.alloc_slice(&[trait_impl_unknown_fun]),
None,
);
program.add_domain(trait_domain);
});
}
}
138 changes: 138 additions & 0 deletions prusti-encoder/src/encoders/ty/generics/builtin_trait/sized_trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use crate::encoders::ty::generics::{
GParams, builtin_trait::BuiltinTraitEnc, trait_impls::TraitImplEnc,
};
use prusti_rustc_interface::middle::{ty, ty::Upcast};
use task_encoder::EncodeFullError;

use prusti_rustc_interface::span::def_id::DefId;
pub struct SizedTrait;

impl super::BuiltinTrait for SizedTrait {
task_encoder::encoder_cache!(BuiltinTraitEnc<SizedTrait>);

fn def_id() -> DefId {
vir::with_vcx(|vcx| vcx.tcx().lang_items().sized_trait().unwrap())
}

fn does_impl<'vir>(
deps: &mut task_encoder::TaskEncoderDependencies<'vir, BuiltinTraitEnc<Self>>,
ctx: GParams<'vir>,
ty: ty::Ty<'vir>,
) -> Result<Option<vir::ExprBool<'vir>>, EncodeFullError<'vir, BuiltinTraitEnc<Self>>> {
vir::with_vcx(|vcx| {
let sizedness = sizedness_for_ty(vcx.tcx(), ty);
let check = match sizedness {
Sizedness::Unsized => None,
Sizedness::Sized => Some(sizedness_check(vcx, deps, ctx, ty, None)?),
Sizedness::Dependent(dep_ty) => {
Some(sizedness_check(vcx, deps, ctx, ty, Some(dep_ty))?)
}
};

Ok(check)
})
}
}

fn sizedness_check<'vir>(
vcx: &'vir vir::VirCtxt<'vir>,
deps: &mut task_encoder::TaskEncoderDependencies<'vir, BuiltinTraitEnc<SizedTrait>>,
impl_ctx: GParams<'vir>,
impl_ty: ty::Ty<'vir>,
depended_on: Option<ty::Ty<'vir>>,
) -> Result<vir::ExprBool<'vir>, EncodeFullError<'vir, BuiltinTraitEnc<SizedTrait>>> {
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)
}

#[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`")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::encoders::ty::generics::{
GParams, builtin_trait::BuiltinTraitEnc, trait_impls::TraitImplEnc,
};
use prusti_rustc_interface::{middle::ty, span::def_id::DefId};
use task_encoder::EncodeFullError;

pub struct TupleTrait;

impl super::BuiltinTrait for TupleTrait {
task_encoder::encoder_cache!(BuiltinTraitEnc<TupleTrait>);

fn def_id() -> DefId {
vir::with_vcx(|vcx| vcx.tcx().lang_items().tuple_trait().unwrap())
}

fn does_impl<'vir>(
deps: &mut task_encoder::TaskEncoderDependencies<'vir, BuiltinTraitEnc<Self>>,
ctx: GParams<'vir>,
ty: ty::Ty<'vir>,
) -> Result<Option<vir::ExprBool<'vir>>, EncodeFullError<'vir, BuiltinTraitEnc<Self>>> {
vir::with_vcx(|vcx| {
let tcx = vcx.tcx();

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, ctx, impls_tuple)?)
} else {
None
};

Ok(check)
})
}
}
Loading