Repository navigation
B2-1: function templates verified in each specialization - #80
Merged
Merged
Conversation
…s own object as nothing, and hold class invariants at reference and pointer parameters.
… for class invariants.
…eturns to its own object.
…ns to its own object, as an argument is.
…urns to its own object.
…assed by reference or pointer.
…s at reference and pointer parameters.
…ependence of their type or operands.
…nd the checks it shares with the parser.
…y in Sema, for the parser and for instantiation.
…tracts with their loops, and trigger marks.
…and keep ghost variables ghost.
…e, named with its arguments.
…a template's type compared with a constant.
…wn, so each element of a pack expansion binds its own variable.
… and the pattern's checks already give.
…d for those of every redeclaration.
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.
A function template's contracts were ignored with a warning, so none of its specializations was verified, and verified code could not call one. Now each specialization a translation unit instantiates is verified as a function of its own, with its contract instantiated as its body is:
x + xis anint, which ashortresult holds only up to 32767. On the base (#79), no specialization is verified and both callers are errors:(each error with its source line and caret).
Stacked on #79 (B2-1 of plan item B2: templates verified per instantiation; class templates follow).
Design
What a template means depends on the types and overloads its arguments bring, which C++ fixes only where it is instantiated, so a template is verified in each specialization the translation unit instantiates, by a use or by an explicit instantiation, the position of Kani and CBMC (Creusot and Prusti verify generic Rust once because trait bounds fix its meaning). A contracted template that nothing instantiates is reported, not verified.
Sema::InstantiateFunctionContractinstantiates it where a specialization is odr-used (MarkFunctionReferenced, which a trusted template without a definition, or anextern template, needs) and where its definition is instantiated, once per specialization: the specialization'sFunctionContractInfois recorded first, so a use in its own contract stops there. It runs in the specialization's context (ContextRAII, which gives a member itsthis) and an evaluation context of its own: the use that asks for it may be in the middle of a caller's full expression, whose temporaries' cleanups the contract would otherwise take (template_contracts.cppchecks the caller keeps itsExprWithCleanups). ItsLocalInstantiationScopemaps the pattern's parameters, and so those of every redeclaration (Clang identifies them by position), so the clauses may be written on a declaration before the definition or on a definition after a declaration that names the parameters otherwise. An unused specialization's contract is never instantiated, so arguments a contract does not fit are an error only where used, with Clang'sin instantiation ofnote.SubstFunctionContractrunsTreeTransform::TransformFunctionContract: each condition is converted tobool(TransformContractCondition), each measure must be an integer (TransformContractMeasure, throughSema::ActOnContractMeasure, which the parser now uses too), and a range is rebuilt byActOnContractRange(TransformContractFootprint). Quantifiers and choices are rebuilt bySema::BuildContractBinderExpr(bounds integers, body a condition), shared with the parser;cppverify::resulttakes the instantiated return type. In the body, an assertion's condition is checked again, a loop's contract is transformed with the loop and attached to the rebuilt loop (which is rebuilt whenever a clause changed, even if its condition and body did not), and trigger marks are kept (TransformExpr).(cv::forall(k, 0, ns, p[k] < 10) && ...), each element is the pattern transformed again, and the binder was mapped once for all of them; a right fold transforms its last element first, so the others read its variable. Each quantifier and choice now binds in aLocalInstantiationScopeof its own (TemplateInstantiator), as a lambda's locals do, and the block ofcheck(forall(k, ...)) by { ... }is given the instantiatedk. The parameters a pack expands to share its name; they are named as C++26 indexes a pack,ns...[1](nameParameters), counterexamples included.ResultExpr,OldExpr, and the quantifiers had no dependence, so in a templatecv::post(cv::result > 0)andcv::old(x) > 0did not compile (invalid operands to binary expression ('T' and 'int')). They now take the dependence of their type (result), operand (old), or operands (quantifiers and choices, as values).collectFunctionCandidatesadds after each function template the specializations the translation unit instantiates (from its canonical declaration, since its redeclarations share them), with their definition or, without one, their contract. A specialization is named with its arguments (functionName:maxv<int>,Meter::add<short>), and one the verifier cannot represent (maxv<double>) fails alone. An uncontractedconstexprtemplate in a contract is a spec in each specialization (bigger_of<int>).note: in instantiation of function template specialization 'twice<short>' requested here(JSONinstantiated_at).Tests
suite/function_templates.cpp(Z3, with a check that no proof is vacuous): per-specialization verdicts (maxv<int>,maxv<long>by explicit instantiation,twice<int>andtwice<signed char>,total<int>andtotal<short>with a loop contract,bits<short>andbits<long long>withsizeof(T)), a recursive template, ghost code of the template's type, a spec template, a contract on a declaration (half<int>, verified once) and one on a definition after a declaration (third<int>), a member function template, variadic templates (a fold over a quantifier,ns...[0]in a counterexample), a trusted template without a definition,cppverify::resultcompared with a constant, a loop whose condition and body do not change, a trigger (Layer 3), a constexpr template spec,maxv<double>failing alone, the notes (text and JSON), and the warning for a template nothing instantiates.template_contract_errors.cpp(frontend,-verify): a postcondition, precondition, measure, quantifier bound, range, assertion, and loop invariant that do not fit the arguments are errors with the instantiation note; arguments that fit, and an unevaluated use, are not.template_contracts.cpp: function template contracts kept;cv::result > 0andcv::old(v) > 0compile; a caller whose argument is a temporary keeps its cleanups.unused_template.cpp,z3_calls_fail_closed.cpp: the warning for a contracted template nothing instantiates replaces the error and the warning.All five fail on the base: the frontend tests get the ignored-contract warnings and none of the errors they expect (
template_contracts.cppgetsinvalid operands to binary expression ('T' and 'int')forcv::result > 0andcv::old(v) > 0), andfunction_templates.cppdoes not compile there (invalid operands to binary expression ('int' and 'T')for0 <= cv::result).Mutants, each built on an assertions build and run on the five tests: 23 of 23 killed. Three more found code that changed nothing, which is removed: mapping the parameters of the declaration the contract is written on rather than the pattern's (Clang keys each parameter by the canonical declaration's), a
thisscope (the specialization's context gives it), and copying ghost marks (ghost locals are found by their ghost block, and Sema checks every use in the pattern).function_templates,template_contract_errors,z3_calls_fail_closedfunction_templates(next_of<int>, trusted, without a definition)function_templates(maxv<long>, an explicit instantiation)template_contract_errors(needs<Pair>)cv::resultkeeps the pattern's typefunction_templatescv::resultnot dependentfunction_templates,template_contracts(cv::result > 0)function_templates(a fold overforall: "pack expansion does not contain any unexpanded parameter packs")function_templates,template_contract_errorsfunction_templates(size_of,while (false))function_templates(Layer 3trigger)function_templatesfunction_templates(half<int>once)function_templates(text and JSON)template_contract_errors(decltype)template_contracts(the caller'sExprWithCleanups)template_contract_errors(steps<int *>)template_contract_errors(fills<double>)template_contract_errors(all_below<double>)unused_template,z3_calls_fail_closed,function_templatesunused_template,function_templatesfunction_templates(first_small<int, int>)template_contractsfunction_templates(product_of<int, int>)Open
-Wcontract-unsupported; B2-2: class templates verified in each specialization #81 verifies them.std::minandstd::max(A18) stay skipped with the rest of namespacestd.cppverify::resultof a function returning a reference is a prvalue of reference type in Sema, so every use is rejected; a small fix of its own.Docs
twice), the rules, and what changed.Checked
docs_examples.testanddocs_coverage.testpass; the site builds with warnings as errors.