Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
42ab8a7
verify: Add builtin-argument, the kind of a check that a builtin is d…
SwayamInSync Oct 28, 2026
6680ff3
verify: Add the builtin-argument obligation kind.
SwayamInSync Oct 28, 2026
d3f25b0
verify: Name builtin-argument, and anchor an exit assert apart from i…
SwayamInSync Oct 28, 2026
fb0fb17
verify: Archive builtin-argument as tag 24 from schema v6.
SwayamInSync Oct 28, 2026
5260467
verify: Give an exit assert an anchor apart from its condition.
SwayamInSync Oct 28, 2026
98705b1
verify: Count builtin-argument as definedness, and keep a postconditi…
SwayamInSync Oct 28, 2026
b9aae1c
verify: Pass into any guard of a failed definedness check, and count …
SwayamInSync Oct 28, 2026
a4f2ba8
verify: Declare convertBitBuiltin.
SwayamInSync Oct 28, 2026
fdf9873
verify: Lower the bit builtins to their definitions, and skip express…
SwayamInSync Oct 28, 2026
d49fd99
test: Check the bit builtins' definitions, clz and ctz of 0, and stat…
SwayamInSync Oct 28, 2026
7df6429
test: Expect postcondition checks at their operations in contract_wel…
SwayamInSync Oct 28, 2026
da6afb5
test: Expect postcondition checks at their operations in dynamic_stor…
SwayamInSync Oct 28, 2026
0c8c76b
test: Expect postcondition checks at their operations in out_paramete…
SwayamInSync Oct 28, 2026
fbaade7
docs: Describe the bit builtins' lowering and exit asserts' anchors.
SwayamInSync Oct 28, 2026
c8ba299
docs: State what the bit builtins mean and when they are undefined.
SwayamInSync Oct 28, 2026
3b7e282
docs: Document the bit builtins with an example.
SwayamInSync Oct 28, 2026
1e4edda
docs: List the bit builtins as supported.
SwayamInSync Oct 28, 2026
2ff0d0b
docs: List the builtin-argument kind.
SwayamInSync Oct 28, 2026
6ee6d9c
docs: Show a builtin's argument as undefined behavior in chapter 18.
SwayamInSync Oct 28, 2026
5bc0801
docs: Note the bit builtins and the static_assert crash fix.
SwayamInSync Oct 28, 2026
3167b00
Merge fix/failing-piece into fix/bit-builtins.
SwayamInSync Oct 28, 2026
20fca17
verify: Declare the hoisting of writes whose values expressions use.
SwayamInSync Oct 28, 2026
5b1808d
verify: Lower increments, assignments, and commas used as values wher…
SwayamInSync Oct 28, 2026
5369be0
test: Check writes used as values, their order, and that each runs once.
SwayamInSync Oct 28, 2026
cd3e6fa
docs: Describe writes in expressions, and name writes in the order me…
SwayamInSync Oct 28, 2026
7cb3279
docs: Move writes used as values to the supported column.
SwayamInSync Oct 28, 2026
124b3ec
docs: Note writes in expressions in the release notes.
SwayamInSync Oct 28, 2026
8a3452d
docs: Describe how writes reach the call sink and the order rule for …
SwayamInSync Oct 28, 2026
c05c389
docs: Describe writes used as values in the design.
SwayamInSync Oct 28, 2026
c8fbcc6
verify: Declare models of memcpy, memmove, memset, and memcmp for eac…
SwayamInSync Oct 28, 2026
becf076
verify: Give an assert the note an unsupported obligation shows.
SwayamInSync Oct 28, 2026
18cc6d2
verify: Keep an assert's note when it is cloned.
SwayamInSync Oct 28, 2026
d27c373
verify: Pass an assert's note to its obligation, and restate a callee…
SwayamInSync Oct 28, 2026
ae2f01e
verify: Declare the lowering of the C library's memory functions and …
SwayamInSync Oct 28, 2026
3b470b4
verify: Lower memcpy, memmove, memset, and memcmp to their models and…
SwayamInSync Oct 28, 2026
e3619c9
test: Check the C library's memory functions and abs against their mo…
SwayamInSync Oct 28, 2026
2bea779
test: Check callee quantifiers over slices at one offset and at several.
SwayamInSync Oct 28, 2026
3e65981
test: Add differential cases that run the C library against its models.
SwayamInSync Oct 28, 2026
b720841
test: Run the library cases in the differential suite.
SwayamInSync Oct 28, 2026
220d8d5
docs: Describe the C library's memory functions and abs.
SwayamInSync Oct 28, 2026
d48be80
docs: List the C library functions the verifier models.
SwayamInSync Oct 28, 2026
3d0e2d8
docs: Note the C library's memory functions in the release notes.
SwayamInSync Oct 28, 2026
37effaf
docs: Describe the library models and slice restatements in the archi…
SwayamInSync Oct 28, 2026
f44a10e
docs: Describe the library models in the design.
SwayamInSync Oct 28, 2026
43a8e90
Merge fix/failing-piece into fix/bit-builtins.
SwayamInSync Oct 28, 2026
3018f6c
Merge fix/bit-builtins into fix/side-effect-values.
SwayamInSync Oct 28, 2026
2b3039a
Merge fix/side-effect-values into fix/library-primitives.
SwayamInSync Oct 28, 2026
cd7da14
Merge fix/failing-piece into fix/bit-builtins.
SwayamInSync Oct 28, 2026
e8c636d
Merge fix/bit-builtins into fix/side-effect-values.
SwayamInSync Oct 28, 2026
eac2bfb
Merge fix/side-effect-values into fix/library-primitives.
SwayamInSync Oct 28, 2026
e8179e7
verify: Take a global abs of a system header as std::abs, as libc++ d…
SwayamInSync Oct 28, 2026
ad683c3
test: Add the global abs overloads of libc++'s stdlib.h as an input.
SwayamInSync Oct 28, 2026
4719fca
test: Check std::abs defined in the global namespace, and a user's ow…
SwayamInSync Oct 28, 2026
ac7b7ea
docs: Say that std::abs is modeled as libc++ defines it too.
SwayamInSync Oct 28, 2026
12788a0
docs: Describe libc++'s std::abs in DESIGN.md.
SwayamInSync Oct 28, 2026
4677c98
docs: Describe the global abs of a system header in ARCHITECTURE.md.
SwayamInSync Oct 28, 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
87 changes: 87 additions & 0 deletions clang/lib/Headers/cppverify.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,93 @@ struct map {
};
map map_empty();

/* The meaning the verifier gives to the C library's memcpy, memmove, memset,
* and memcmp. A call with pointers to an integer type T, written before their
* conversion to void *, and a size of k * sizeof(T) bytes is a call of the
* model for T with k: whole objects, which keep their values bit for bit.
* The models are trusted: a proof that uses one names it in [trusts=...].
* Distinct pointer parameters of which one is mutable address separate
* objects (memcpy's ranges must not overlap); memmove's may overlap. */
namespace model {

#define __CPPVERIFY_MEMORY_MODELS(T) \
[[cppverify::trusted]] T *memcpy(T *d, const T *s, __SIZE_TYPE__ k) \
cppverify::pre(cppverify::valid(d, k) && cppverify::valid(s, k)) \
cppverify::modifies(d[0 : k]) \
cppverify::post(cppverify::forall(i, 0, k, d[i] == cppverify::old(s[i])))\
cppverify::post(cppverify::result == d); \
[[cppverify::trusted]] T *memmove(T *d, const T *s, __SIZE_TYPE__ k) \
cppverify::aliases(d, s) \
cppverify::pre(cppverify::valid(d, k) && cppverify::valid(s, k)) \
cppverify::modifies(d[0 : k]) \
cppverify::post(cppverify::forall(i, 0, k, d[i] == cppverify::old(s[i])))\
cppverify::post(cppverify::result == d);

/* memset(d, c, n) stores the byte (unsigned char)c in every byte: v is that
* byte repeated in an object of type T. */
#define __CPPVERIFY_MEMSET_MODEL(T) \
[[cppverify::trusted]] T *memset(T *d, T v, __SIZE_TYPE__ k) \
cppverify::pre(cppverify::valid(d, k)) \
cppverify::modifies(d[0 : k]) \
cppverify::post(cppverify::forall(i, 0, k, d[i] == v)) \
cppverify::post(cppverify::result == d);

/* memcmp is 0 exactly when the objects are equal. Of bytes, its sign is that
* of the first difference, as unsigned char; of wider objects that depends
* on the byte order, and only equality is modeled. */
#define __CPPVERIFY_MEMCMP_MODEL(T) \
[[cppverify::trusted]] int memcmp(const T *a, const T *b, __SIZE_TYPE__ k) \
cppverify::pre(cppverify::valid(a, k) && cppverify::valid(b, k)) \
cppverify::post((cppverify::result == 0) == \
cppverify::forall(i, 0, k, a[i] == b[i]));
#define __CPPVERIFY_BYTE_MEMCMP_MODEL(T) \
[[cppverify::trusted]] int memcmp(const T *a, const T *b, __SIZE_TYPE__ k) \
cppverify::pre(cppverify::valid(a, k) && cppverify::valid(b, k)) \
cppverify::post((cppverify::result == 0) == \
cppverify::forall(i, 0, k, a[i] == b[i])) \
cppverify::post(cppverify::forall( \
j, 0, k, \
!cppverify::forall(i, 0, j, a[i] == b[i]) || a[j] == b[j] || \
(cppverify::result < 0) == \
((a[j] & 255) < (b[j] & 255))));

#define __CPPVERIFY_WIDE_MODELS(T) \
__CPPVERIFY_MEMORY_MODELS(T) \
__CPPVERIFY_MEMSET_MODEL(T) \
__CPPVERIFY_MEMCMP_MODEL(T)
#define __CPPVERIFY_BYTE_MODELS(T) \
__CPPVERIFY_MEMORY_MODELS(T) \
__CPPVERIFY_MEMSET_MODEL(T) \
__CPPVERIFY_BYTE_MEMCMP_MODEL(T)

__CPPVERIFY_MEMORY_MODELS(bool)
__CPPVERIFY_MEMCMP_MODEL(bool)
__CPPVERIFY_BYTE_MODELS(char)
__CPPVERIFY_BYTE_MODELS(signed char)
__CPPVERIFY_BYTE_MODELS(unsigned char)
__CPPVERIFY_WIDE_MODELS(wchar_t)
#if __cplusplus >= 201103L
__CPPVERIFY_WIDE_MODELS(char16_t)
__CPPVERIFY_WIDE_MODELS(char32_t)
#endif
__CPPVERIFY_WIDE_MODELS(short)
__CPPVERIFY_WIDE_MODELS(unsigned short)
__CPPVERIFY_WIDE_MODELS(int)
__CPPVERIFY_WIDE_MODELS(unsigned)
__CPPVERIFY_WIDE_MODELS(long)
__CPPVERIFY_WIDE_MODELS(unsigned long)
__CPPVERIFY_WIDE_MODELS(long long)
__CPPVERIFY_WIDE_MODELS(unsigned long long)

#undef __CPPVERIFY_MEMORY_MODELS
#undef __CPPVERIFY_MEMSET_MODEL
#undef __CPPVERIFY_MEMCMP_MODEL
#undef __CPPVERIFY_BYTE_MEMCMP_MODEL
#undef __CPPVERIFY_WIDE_MODELS
#undef __CPPVERIFY_BYTE_MODELS

} // namespace model

} // namespace cppverify

#endif /* __cplusplus */
Expand Down
35 changes: 23 additions & 12 deletions clang/lib/Verify/Backend/Obligation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ ObligationKind verify::obligationKind(ProofObligationKind Kind) {
return ObligationKind::MissingReturn;
case ProofObligationKind::Unreachable:
return ObligationKind::Unreachable;
case ProofObligationKind::BuiltinArgument:
return ObligationKind::BuiltinArgument;
case ProofObligationKind::Unsupported:
return ObligationKind::Unsupported;
}
Expand Down Expand Up @@ -328,6 +330,8 @@ const char *verify::obligationKindName(ObligationKind Kind) {
return "missing-return";
case ObligationKind::Unreachable:
return "unreachable";
case ObligationKind::BuiltinArgument:
return "builtin-argument";
case ObligationKind::Unsupported:
return "unsupported";
}
Expand Down Expand Up @@ -2515,12 +2519,13 @@ class ObligationBuilder {
Stmt->Focus, Stmt->Clause});
}

std::vector<
std::tuple<ObligationKind, std::unique_ptr<VCExpr>, std::string>>
std::vector<std::tuple<ObligationKind, std::unique_ptr<VCExpr>,
std::string, SourceSpan>>
ExitAsserts;
for (const PassiveExitAssert &Exit : P.ExitAsserts)
ExitAsserts.emplace_back(obligationKind(Exit.ProofKind),
fromVExpr(Exit.Cond.get()), Exit.Note);
fromVExpr(Exit.Cond.get()), Exit.Note,
Exit.Anchor);

// A theorem only where it speaks of the entry state alone: the body's
// later states are not part of it.
Expand Down Expand Up @@ -2594,22 +2599,27 @@ class ObligationBuilder {
auto appendObligation = [&](ObligationKind Kind, const VCExpr *Condition,
uint64_t TraceEventCount,
const std::string &Note = "",
SourceSpan Focus = {}, SourceSpan Clause = {}) {
SourceSpan Focus = {}, SourceSpan Clause = {},
SourceSpan Anchor = {}) {
Obligation Item;
Item.Kind = Kind;
Item.Note = Note;
Item.Focus = Focus;
Item.Clause = Clause;
Item.Loc = Condition->Loc;
Item.BeginLoc = Condition->BeginLoc;
Item.EndLoc = Condition->EndLoc;
if (Anchor.isValid())
Item.Focus = {Condition->Loc, Condition->BeginLoc, Condition->EndLoc};
else
Anchor = {Condition->Loc, Condition->BeginLoc, Condition->EndLoc};
Item.Loc = Anchor.Loc;
Item.BeginLoc = Anchor.BeginLoc;
Item.EndLoc = Anchor.EndLoc;
Item.TraceEventCount = TraceEventCount;
Item.Id = Identity + "::obligation:" + std::to_string(++ObligationIndex);
Item.Goal = makeGoal(Condition);
Item.CounterexampleQuery = vcNot(cloneLogicExpr(Item.Goal.get()));
Item.CounterexampleQuery->Loc = Condition->Loc;
Item.CounterexampleQuery->BeginLoc = Condition->BeginLoc;
Item.CounterexampleQuery->EndLoc = Condition->EndLoc;
Item.CounterexampleQuery->Loc = Anchor.Loc;
Item.CounterexampleQuery->BeginLoc = Anchor.BeginLoc;
Item.CounterexampleQuery->EndLoc = Anchor.EndLoc;
M.Obligations.push_back(std::move(Item));
};

Expand All @@ -2625,8 +2635,9 @@ class ObligationBuilder {
if (oversize())
return ObligationSizeError::obligations(size(), MaxNodes);
}
for (const auto &[Kind, Exit, Note] : ExitAsserts) {
appendObligation(Kind, Exit.get(), M.TraceEvents.size(), Note);
for (const auto &[Kind, Exit, Note, Anchor] : ExitAsserts) {
appendObligation(Kind, Exit.get(), M.TraceEvents.size(), Note, {}, {},
Anchor);
if (oversize())
return ObligationSizeError::obligations(size(), MaxNodes);
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Verify/Backend/Obligation.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ enum class ObligationKind {
Deallocation,
MissingReturn,
Unreachable,
BuiltinArgument,
Unsupported
};

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Verify/Backend/ObligationSerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,8 @@ uint8_t obligationTag(ObligationKind Kind) {
return 21;
case ObligationKind::Unreachable:
return 23;
case ObligationKind::BuiltinArgument:
return 24;
case ObligationKind::Unsupported:
return 22;
}
Expand Down Expand Up @@ -386,6 +388,10 @@ std::optional<ObligationKind> obligationFromTag(uint8_t Tag, uint32_t Version) {
return ObligationKind::Unsupported;
case 23:
return ObligationKind::Unreachable;
case 24:
if (Version < 6)
return std::nullopt;
return ObligationKind::BuiltinArgument;
default:
return std::nullopt;
}
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Verify/Driver/Counterexample.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ bool isDefinedness(ObligationKind Kind) {
case ObligationKind::Initialization:
case ObligationKind::PointerDifference:
case ObligationKind::PointerValidity:
case ObligationKind::BuiltinArgument:
return true;
default:
return false;
Expand Down Expand Up @@ -289,8 +290,7 @@ FailurePlace verify::failingPlace(const ObligationModule &Module,
return nullptr;
}
if (Generated && E->K == LogicExpr::Or && E->Children.size() == 2 &&
E->Children[0]->K == LogicExpr::Not && isFalse(E->Children[0].get()) &&
isFalse(E->Children[1].get()))
isFalse(E->Children[0].get()) && isFalse(E->Children[1].get()))
if (const LogicExpr *Part = falsePart(E->Children[1].get()))
return Part;
return placed(E) ? E : nullptr;
Expand Down
Loading
Loading