Skip to content

Avoid materializing opaque string literals - #179

Open
nikswamy wants to merge 1 commit into
mainfrom
nswamy/pal-opaque-string-literals
Open

Avoid materializing opaque string literals#179
nikswamy wants to merge 1 commit into
mainfrom
nswamy/pal-opaque-string-literals

Conversation

@nikswamy

Copy link
Copy Markdown
Contributor

PAL intentionally models a string literal decayed to an ownership-free _plain pointer as an opaque address. The resulting ref carries no points-to resource, so verified code may pass, return, or compare it but cannot dereference it; callers that need contents must use an explicit ownership-bearing model such as _array.

The previous primitive accepted the complete literal array specification even though its result exposed no relation to those contents. Building that unused argument generated character-conversion terms and proof obligations for every literal. Replace it with a compact identity token assigned deterministically to each literal AST node during emission.

Because string_literal_to_ref is an abstract pure function, repeated evaluations of one token produce a stable address, while references from different tokens have no asserted equality or inequality, permitting implementation-defined literal merging without collapsing distinct literals to one term. Expose the C guarantee that literal addresses are non-null, but still provide no ownership.

Extend stringlit coverage with direct decay to a _plain argument, non-null borrowed returns, a switch returning several independently identified literals, and two literals from one macro expansion that must receive distinct tokens.

PAL intentionally models a string literal decayed to an ownership-free _plain pointer as an opaque address. The resulting ref carries no points-to resource, so verified code may pass, return, or compare it but cannot dereference it; callers that need contents must use an explicit ownership-bearing model such as _array.

The previous primitive accepted the complete literal array specification even though its result exposed no relation to those contents. Building that unused argument generated character-conversion terms and proof obligations for every literal. Replace it with a compact identity token assigned deterministically to each literal AST node during emission.

Because string_literal_to_ref is an abstract pure function, repeated evaluations of one token produce a stable address, while references from different tokens have no asserted equality or inequality, permitting implementation-defined literal merging without collapsing distinct literals to one term. Expose the C guarantee that literal addresses are non-null, but still provide no ownership.

Extend stringlit coverage with direct decay to a _plain argument, non-null borrowed returns, a switch returning several independently identified literals, and two literals from one macro expansion that must receive distinct tokens.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 981f7d3c-6a91-47ad-84d7-7aadf747123d
@gebner

gebner commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

This seems like a bad idea. We want to be able to reason about the contents of string literals in the future, and this pretty much blocks the obvious way there.

Building that unused argument generated character-conversion terms and proof obligations for every literal.

Does this happen once when we use the string literal, or every single time we run the Pulse prover?

I would do the following: generate an auxiliary (F*-)global variable let string_lit_1234 : full_array_lspec ... = ... and then emit array_literal_to_ref string_lit_1234 for the array literal use sites. We could then add ghost lemmas that provide fractional permission to these literals very easily.

We already know that we can handle large arrays (with a couple thousand elements) efficiently, see the global array tactics example.

If the character literals generate a VC, then we should fix that so that they emit 123uy instead (which should be efficient).

Comment thread src/pass/emit.rs
/// Maps typedef names that are OpaqueTypeDecls to their Type_* module (overrides Typedef_*).
typedef_override_map: HashMap<Rc<str>, String>,
tmp_counter: usize,
string_literal_ids: HashMap<*const Expr, usize>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
string_literal_ids: HashMap<*const Expr, usize>,
string_literal_ids: HashMap<Rc<Expr>, usize>,

We should not rely on pointer equality

@gebner

gebner commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Alternatively we could also translate string literals to pure const global arrays in an IR pass.

@gebner

gebner commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Just to be clear: adding an identity argument to array_literal_to_ref to avoid spurious proofs of pointer equality is great, it's the other changes that need work.

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