Skip to content

Comments

MIR: Add mir_field_{ref,value}#3056

Merged
qsctr merged 12 commits intomasterfrom
T1983-mir-field-ref-value
Feb 23, 2026
Merged

MIR: Add mir_field_{ref,value}#3056
qsctr merged 12 commits intomasterfrom
T1983-mir-field-ref-value

Conversation

@qsctr
Copy link
Collaborator

@qsctr qsctr commented Feb 19, 2026

I needed to add a few functions to crucible-mir, so this includes a submodule bump for crucible. The corresponding crucible PR is GaloisInc/crucible#1754.

Closes #1983.

@qsctr qsctr self-assigned this Feb 19, 2026
@qsctr qsctr added the PR: submodule bump Pull requests that include a submodule bump label Feb 19, 2026
Comment on lines +398 to +404
show (MIRAccessTransparentSecondaryField structTy primaryField secondaryField) =
unlines
[ "Cannot access zero-sized field '" ++ Text.unpack secondaryField
++ "' of #[repr(transparent)] struct:"
, "On #[repr(transparent)] struct type: " ++ show (PP.pretty structTy)
, "The inner field that can be accessed is: " ++ Text.unpack primaryField
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit surprised that we would restrict field accesses on repr(transparent) structs to just the primary field, since as far as I can tell, this is not a requirement that the Rust language itself imposes. Is there an implementation reason for needing this restriction in SAW?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mostly because I'm not sure how I would implement resolveSetupVal and matchArg for the secondary fields.

resolveSetupVal for getting a secondary field by value would need to conjure up a value for the field, since the recursive call to resolveSetupVal for the struct won't contain the secondary field RegValues. Since it's a ZST, this would be doable, but the only code I could find that already does this is initialValue from crucible-mir which is in the MirGenerator monad so I don't think I can directly call it from here. And so given that there is no particular need at the moment to support this, I opted to just make it an error.

resolveSetupVal for getting a secondary field by reference would basically need to return an invalid reference I think, because there's no way of extending the existing MirReferencePath to point to a nonexistent field. And our support for invalid pointers to ZSTs in crucible is very limited (see GaloisInc/crucible#1497), so I don't think we would get much further even if we did that.

matchArg for getting a secondary field by value would also need to conjure up a RegValue for the ZST.

matchArg for getting a secondary field by reference would need to go from a pointer to ZST (which might be invalid) to a pointer to the primary field, which is entirely unrelated, and I don't know how we would do that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's a ZST, this would be doable, but the only code I could find that already does this is initialValue from crucible-mir which is in the MirGenerator monad so I don't think I can directly call it from here.

For what it's worth, you can also call the mirAggregate_zst* family of functions here instead. For instance, mirAggregate_zstIO has an appropriate type for being called from resolveSetupVal.

resolveSetupVal for getting a secondary field by reference would basically need to return an invalid reference I think, because there's no way of extending the existing MirReferencePath to point to a nonexistent field. And our support for invalid pointers to ZSTs in crucible is very limited (see GaloisInc/crucible#1497), so I don't think we would get much further even if we did that.

Ah, I forgot about GaloisInc/crucible#1497. In that case, I think I'm fine with imposing this restriction, although it would be worth citing GaloisInc/crucible#1497 as justification for it in the comments.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what it's worth, you can also call the mirAggregate_zst* family of functions here instead.

I thought that only works for MirAggregate-based ZSTs like (), and not something like a struct whose fields are all ZSTs.

In that case, I think I'm fine with imposing this restriction, although it would be worth citing GaloisInc/crucible#1497 as justification for it in the comments.

Even if GaloisInc/crucible#1497 is resolved, I'm still not sure how we would implement override matching for mir_field_ref on a secondary field of a repr(transparent) struct. To match a pointer to a field of a struct against a mir_field_ref, we match the pointer to the whole struct against the SetupVar for the whole mir_alloc. But if we receive a pointer to a secondary field of a repr(transparent) struct, there's no way of going from that to the struct pointer that it was projected out of, because it isn't actually a projection in crucible-mir. The field pointer would either be an invalid pointer or an entirely separate MirReference with Const_RefRoot.

For instance, if we wanted to verify something like

#[repr(transparent)]
struct S {
  x: i32,
  y: ()
}

fn f(s: &S) -> &() {
  &s.y
}
let f_spec = do {
    s <- mir_fresh_expanded_value "s" (mir_adt S);
    r <- mir_ref_of s;
    mir_execute_func [r];
    mir_return (mir_field_ref r "y");
};

SAW wouldn't be able to tell whether the &() returned from the function came from the &S or not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All fair points. Perhaps citing GaloisInc/crucible#1497 is the wrong justification, in that case. Could you take the points that you raise here and convert them into comments so that we have something to point to for justifying why we impose this restriction on repr(transparent) types?

= panic "getEnumVariantShortName" [
"Malformed enum variant identifier: " <> Text.pack (show $ variant ^. Mir.vname)
]
getEnumVariantShortName variant = fieldOrVariantShortName (variant ^. Mir.vname)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should just delete this function now that it's a very thin wrapper around fieldOrVariantShortName. (There are only a small number of getEnumVariantShortName call sites anyway.)

getShortName field == shortFieldName
case FWI.ifind shortNameMatches fields of
Just (i, field) ->
case Mir.findReprTransparentField col adt of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is quite a bit of rightward code drift here due to indentation—this case expression is nested five levels deep! It would be nice to eliminate some of this drift, either by splitting out certain case expressions into helper functions or by rewriting the code slightly. For instance, the outermost case could be rewritten using a style like:

findStructField col accessMode structTy shortFieldName = do
  adtName <-
    case structTy of
      Mir.TyAdt adtName _ _ -> pure adtName
      _ -> panic ...
  ...

qsctr added 11 commits February 22, 2026 22:04
This check was previously there but missed in the refactor in 8a157aa
outdated comments after migration to MirAggregate for arrays
Bumps crucible to bring in `mirRef_peel{Field,Just}IO` which are needed
for override matching for `mir_field_ref`.

In addition to adding tests for the new commands to test1983, also
changed the existing tests to use `u32` instead of `i32`, because
`mir_fresh_expanded_value` doesn't work with arrays of signed integers
(see #3055).

Completes the second half of #1983.
@qsctr qsctr force-pushed the T1983-mir-field-ref-value branch from e55642a to cb8d832 Compare February 23, 2026 09:17
@qsctr
Copy link
Collaborator Author

qsctr commented Feb 23, 2026

force push: rebase on #3059 and also bump crucible to master after GaloisInc/crucible#1754 was merged

@qsctr qsctr merged commit 9ad49e5 into master Feb 23, 2026
36 of 37 checks passed
@qsctr qsctr deleted the T1983-mir-field-ref-value branch February 23, 2026 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: submodule bump Pull requests that include a submodule bump

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mir_elem_{ref,value} and mir_field_{ref,value}

2 participants