Repository navigation
Global records and arrays - #127
Merged
Merged
Conversation
… places at their addresses.
This was referenced Oct 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Plan item B6 (globals beyond scalars), first part: mutable global records and arrays. Only integral globals were supported; a record or array global stopped its function (
global aggregate access is unsupported,the global array 'table' is unsupported).On the base
The example below (with this PR's output further down) stops all three functions, with 16 errors:
With this PR
Design
As Frama-C models globals: a mutable global of an integral, record, or array type is one object at a fixed address of its own, in the global region above every other object, where scalar globals already were.
objectPlaceroots a global object at its address (globalObjectPlace), soconfig.mode,config.table[i],grid[1][j], andslots[i].keyare places like a promoted local's: cells with their access conditions (each index bounded by its array, kindbounds). Reads, stores,cppverify::old, range-basedfor, andcppverify::modifiesfootprints (the whole object, a field, an element, a range) all go through them.modifies; a call changes only the callee's footprints.history,&config.limit,config.tableare subobject pointers (B3a-2) bounded by the array, field, or record they are formed from. Their address converts the global's literal (aCast) instead of retyping it: the literal's type names the whole object and gives the origin@address/size, which retyping shrank to one element, so every access past the first failed.abstractValiditynow counts the global objects a pointer's origins name as valid storage, so such a pointer may be passed as avalid(p, n)slice; before, only parameters' objects were.cppverify::type_invariant(nothing establishes or maintains it at every function's boundary), a global that is itself a pointer (nothing could state that the pointer it holds is valid), a spec reading a mutable global, ghost or proof code writing one, thread-local and volatile globals. Static locals stay rejected: they are B6's second part, which needs private frames (a function writes its own static locals without naming them, and only calls that may reach it change them).Tests
suite/global_objects.cpp(new): a field written within its frame and outside it (frame); an element read, with a Layer-1 dump of its place; an element in range and one past it (bounds); a global array cleared in a loop with a range frame; a callee writing one field keeping the others; the whole object as a footprint; a decayed global array passed as a slice, and one element too long (bounds); a pointer into a global array, in range and one past (bounds); a field's address given to a writing callee, within the frame and outside it; records in an array and a two-dimensional array (one index past its row fails); a parameter's pointer stored in a field; and every rejection above with its message. A Layer-1 dump shows the store at the global's address plus the field's offset.lookup_tables.cpp(counternow fails its claim that an element is 0, since a mutable global's value at entry is arbitrary, as the test's own comment says),range_for.cpp,pointer_subobjects.cpp,dynamic_loaded_values.cpp;per_function_errors.testuses a pointer global instead;ghost-runtime-mutation-unsupported.cppis rejected for the right reason now (a proof writing a global, instead of aggregate assignment);z3_calls_fail_closed.cpphas the new message.On a release build of this branch, the whole Verify suite passes (510 of 511, one unsupported).
Mutants
Each mutant below, applied alone and rebuilt, is caught by the tests named above (12 of 12):
objectPlacehas no global rootglobal_objects,lookup_tables,range_forglobal_objects,dynamic_loaded_valuesglobal_objectsglobal_objects,ghost-runtime-mutation-unsupportedghost-runtime-mutation-unsupportedglobal_objectsglobal_objectsglobal_objects(Layer-1 dump)global_objectsglobal_objects,dynamic_loaded_valuesglobal_objectsDocs
forover a global array); limitations (supported column, storage row, control flow row);check-doc-examples.pypasses on every changed page.Not done
global invariant).