Skip to content

Unsizing for trait objects#150

Merged
Aurel300 merged 20 commits into
Aurel300:rewrite-2023from
rgwohlbold:unsizing-for-trait-objects
May 13, 2026
Merged

Unsizing for trait objects#150
Aurel300 merged 20 commits into
Aurel300:rewrite-2023from
rgwohlbold:unsizing-for-trait-objects

Conversation

@rgwohlbold
Copy link
Copy Markdown

PR #149 implements trait objects as opaque types in Viper. However, to pass references to trait objects to functions, these are unsized before being borrowed, which is currently not supported:

trait MyTrait {}

struct S { x: i32 }

impl MyTrait for S {}

fn consume(_v: &dyn MyTrait) {}

#[ensures(result == 42)]
fn function() -> i32 {
    let s = S { x: 42 };
    consume(&s); // unsizing &S -> &dyn MyTrait happens here
    s.x
}

The handler in MirBuiltinEnc::handle_unsize currently only supports unsizing from arrays/slices to slices and panics on everything else. This PR enables unsizing of structs to trait objects and trait objects to themselves: Given a struct MyStruct that implements Trait MyTrait, this PR allows for unsizing &MyStruct -> &dyn MyTrait (as well as the &mut versions). It also allows for unsizing &dyn MyTrait -> &dyn MyTrait (as well as the mut versions) which happens in functions which take &dyn MyTrait and pass it into other functions.

Since trait objects are opaque, no postconditions about their content are encoded.

Supporting this unsizing has the effect of enabling significant standard library functionality, like #[derive(Debug)], making the following snippet not crash anymore:

use prusti_contracts::*;

#[derive(Debug)]
struct S {
    x: i32,
}

fn main() {
    let s = S { x: 42 };
}

TODO

I would like to add a test case with &mut dyn MyTrait. However, I get an error "verification error could not be backtranslated". However, this error also occurs outside of test cases that have to do with this PR, so it's maybe unrelated?

@rgwohlbold rgwohlbold marked this pull request as draft March 13, 2026 14:17
@rgwohlbold rgwohlbold force-pushed the unsizing-for-trait-objects branch from b738977 to cd62265 Compare March 17, 2026 15:00
@Aurel300
Copy link
Copy Markdown
Owner

I would like to add a test case with &mut dyn MyTrait. However, I get an error "verification error could not be backtranslated". However, this error also occurs outside of test cases that have to do with this PR, so it's maybe unrelated?

This might still be an issue with your encoding (or something related to mutrefs in the testcase?) -- Prusti is simply saying that Viper returned an error, but we did not expect that error to happen (i.e., it is not an error that can be back-translated to the violation of a particular assertion or contract). Usually, I see these errors say there is a permission error in a precondition. What does Viper actually report? Does the generated vpr file make sense and is the error something that comes from your encoding?

@rgwohlbold
Copy link
Copy Markdown
Author

So there are multiple problems here:

  • First, the function signature fn consume(&mut dyn MyTrait) desugars to fn(&'a mut (dyn MyTrait + 'a)) which has two lifetime annotations. This makes PCG create self-edges a0↓'a -> a0↓'a (one per position, so two overall) as well as cross-edges a0↓0 -> a0↓1 and a0↓1 -> a0↓0. This results in the following wand being created:
  ensures (let wand_result ==
      (p_0_Tuple_snap(_0p)) in
      acc(p_Param(old(p_Ref_mutable_snap(_1p, s_Dyn_type())).s_Ref_mutable_0,
      s_Dyn_type()), write) --*
      acc(p_Param(old(p_Ref_mutable_snap(_1p, s_Dyn_type())).s_Ref_mutable_0,
      s_Dyn_type()), write))

This can be fixed by patching PCG to only create edges when input != output (not sure if this is fully right though)

  • After patching PCG, Viper brings up an error on the undo_unsize call: There might be insufficient permission to access p_Ref_mutable(old[_before_0_11](_3p), s_Dyn_type()). This is since undo_unsize has
requires acc(p_Ref_mutable(dst, s_Dyn_type()), write)
requires acc(p_Param(p_Ref_mutable_snap(dst, s_Dyn_type()).s_Ref_mutable_0, s_Dyn_type()), write)

but m_consume only returns ensures acc(p_Param(old(p_Ref_mutable_snap(_1p, s_Dyn_type())).s_Ref_mutable_0, s_Dyn_type()), write). This could in principle be fixed by adding ensures acc(p_Ref_mutable(_1p, T), write) and a snap equality postcondition for each &mut T argument in the generated Viper method signature, but I'm a bit hesitant to change it since I don't fully understand it yet.

  • Since the first argument to consume actually contains two lifetimes, PCG creates two edges when processing the unsize coercion, and undo_unsize is called twice. Even when patching the other two issues, the second call to undo_unsize doesn't have enough permissions. I guess we should deduplicate these edges somewhere in the code?

@rgwohlbold
Copy link
Copy Markdown
Author

The second of these points is actually also the case when unsizing arrays to slices, and mutably borrowing them. Unlike tait objects, these only have one lifetime parameter. Example:

fn consume(v: &mut [i32]) {
}

fn main() {
    let mut arr = [1, 2, 3];
    consume(&mut arr);
}
The precondition of method mir_undo_unsize_$amp$$sq$$qm$6$sp$mut$sp$$lb$i32$sc$$sp$3_usize$rb$_to_$amp$$sq$$qm$5$sp$mut$sp$$lb$i32$rb$ might not hold. There might be insufficient permission to access p_Ref_mutable(old[_before_0_11](_3p), s_Slice_type(s_Int_i32_type()))

@rgwohlbold rgwohlbold force-pushed the unsizing-for-trait-objects branch from cd62265 to 70b82b6 Compare March 30, 2026 09:27
@rgwohlbold
Copy link
Copy Markdown
Author

rgwohlbold commented Mar 30, 2026

@rgwohlbold rgwohlbold changed the title Draft: Unsizing for trait objects [blocked]: Unsizing for trait objects Mar 30, 2026
@rgwohlbold rgwohlbold changed the title [blocked]: Unsizing for trait objects [Blocked]: Unsizing for trait objects Mar 30, 2026
@rgwohlbold
Copy link
Copy Markdown
Author

In principle, this PR is ready to review. It does depend on #162, so we should merge that one first.

@rgwohlbold rgwohlbold marked this pull request as ready for review March 30, 2026 16:11
@rgwohlbold rgwohlbold changed the title [Blocked]: Unsizing for trait objects [Blocked] Unsizing for trait objects Mar 30, 2026
@rgwohlbold rgwohlbold force-pushed the unsizing-for-trait-objects branch 3 times, most recently from 7baea86 to be5b76b Compare April 2, 2026 08:53
@rgwohlbold rgwohlbold changed the title [Blocked] Unsizing for trait objects Unsizing for trait objects Apr 2, 2026
@rgwohlbold
Copy link
Copy Markdown
Author

Since #162 is now merged, this PR is ready for review

@rgwohlbold rgwohlbold force-pushed the unsizing-for-trait-objects branch 2 times, most recently from 2703f69 to 4dc255f Compare May 6, 2026 16:36
Comment thread prusti-encoder/src/encoders/mir_impure.rs Outdated
@rgwohlbold rgwohlbold force-pushed the unsizing-for-trait-objects branch from 5b3530d to 1c7afcc Compare May 11, 2026 12:39
@rgwohlbold rgwohlbold force-pushed the unsizing-for-trait-objects branch from a49d926 to ba75f19 Compare May 12, 2026 12:39
@Aurel300 Aurel300 merged commit 0ffb801 into Aurel300:rewrite-2023 May 13, 2026
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants