================
@@ -0,0 +1,943 @@
+# Introduction
+
+This is a high level design documentation for Contracts implementation in
Clang.
+This summarized experiences from several implementors in seperate branches.
+
+This is also tend to be some developer policy/guide of contracts for how
should clang
+developers continue working on this.
+
+## High Level Developing Style
+
+We plan to implement contracts in different small PRs. We don't want to
implement
+contracts in a single PR. Smaller PR helps reviewers to understand the patch
and give
+higher quality reviewers. Samller PR is also easier for testers to test and
find the
+problems.
+
+The implementation contracts is marked as experimental before we announce it
is
+implemented completely.
+
+### ABI
+
+We'd like to keep ABI unstable during the experimental process. We all
understand
+ABI's importance. But getting consensus for ABI needs a long time. We prefer to
+move faster instead of waiting the consensus for ABI consensus.
+
+For GCC/libstdc++ interoperability, we need to identify the ABI revision
+which a toolchain targets. We shouldn't assume that one experimental GCC ABI
+will remain unchanged.
+
+### Driver flags
+
+Before we think the support of contracts are minimal usable, users are unable
+to get access to contracts unless users pass `-Xclang` explicitly.
+
+## AST structure
+
+### Contract Stmt
+
+```C++
+class Stmt {
+
+class ContractStmtBitfields {
+ unsigned Invalid : 1;
+};
+};
+
+class ContractStmtBase : public Stmt {
+ Expr *Predicate = nullptr;
+ SourceLocation KeywordLoc, LParenLoc, RParenLoc;
+ // llvm::ArrayRef<const Attr *> Attributes; // The design allows contracts
to have attributes but none
+ // contracts attribute are
presenting right now.
+};
+
+class ContractAssertStmt : public ContractStmtBase {};
+
+class ContractPreStmt : public ContractStmtBase {};
+
+class ContractPostStmt : public ContractStmtBase {
+ ResultNameDecl *ResultName = nullptr;
+};
+```
+
+Discussion:
+
+We though to use a simple struct to represent `pre` and `post` contracts. But
we decided to put all of
+them into Stmt system. The major reason is that now the users can reuse
components like
+`TraverseStmt(...)` more easily.
+
+For attributes, as we don't have any attributes for contracts yet, we can save
this on first implemenation.
+
+We split them into 3 Stmt kinds so we can save the bits for ResultName for
other contracts types.
+
+For `ResultNameDecl`, see following sections.
+
+### FunctionDecl
+
+```C++
+class FunctionContractSequence final
+ : private llvm::TrailingObjects<FunctionContractSequence, ContractStmtBase
*> {
+ friend llvm::TrailingObjects<FunctionContractSequence, ContractStmtBase *>;
+ unsigned NumContracts;
+
+ explicit FunctionContractSequence(unsigned N) : NumContracts(N) {}
+
+public:
+ static FunctionContractSequence *Create(
+ ASTContext &Ctx, llvm::ArrayRef<ContractStmtBase *> Contracts);
+
+ llvm::ArrayRef<ContractStmtBase *> contracts() const {
+ return {getTrailingObjects<ContractStmtBase *>(), NumContracts};
+ }
+};
+
+class FunctionDecl : /*...*/ {
+ /*...*/
+private:
+ // The actual contracts of the function.
+ // Even if it is not explicitly written.
+ FunctionContractSequence *Contracts = nullptr;
+
+public:
+ bool hasContracts() const {
+ return Contracts;
+ }
+
+ void setContracts(FunctionContractSequence *ContractsSeq) {
+ Contracts = ContractsSeq;
+ }
+
+ const FunctionContractSequence *getContracts() const {
+ return Contracts;
+ }
+};
+```
+
+Discussion:
+
+We need to use a single sequence for the contracts of a function. e.g.,
+
+```C++
+int f(int x)
+ pre(x > 0)
+ post(r: r > 0);
+
+int f(int x)
+ post(r: r > 0)
+ pre(x > 0); // Illegal
+```
+
+So that we can't use a sequence for `pre` and another sequence for `post`.
+
+Another problem is that we allow redeclarations without contracts, or
+with semantically equal contracts. So
+
+```C++
+int func(int lhs, int rhs) pre(lhs != rhs); // FD1
+int func(int lhs1, int rhs1) { return lhs1 == rhs1; } // FD2
+```
+
+we should think the function declaration `int func(int lhs1, int rhs1)`
+(FD2) have the same contracts as the first one.
+Then it is a problem that how should we store/access the contracts for
+`FD2`.
+
+Usually, as this is related to redeclarations, we might think we can
+get it by iterating redecls. e.g.,
+
+```C++
+for (auto *RD : this->redecls())
+ if (auto *ContractSeq = RD->getContracts())
+ return ContractSeq;
+```
+
+But if we follow this, the contracts we get for `FD2` will be the contracts
+of `FD1`, `pre(lhs != rhs)`. While this is semantically correct,
+`FD2` doesn't have `lhs` or `rhs`. It is not only they have
+different name but they are actually different AST nodes.
+So it will be problematic for code generation or static analysis
+if we simply store the contracts to the declaration
+which the contracts are written.
+
+The simplest solution is that Sema to put the corresponding
+contracts on every function redeclaration.
+
+This means, **Sema is responsible to construct a new contract
+sequence when it saw a redeclaration if the first function has
+contracts, no matter if the redeclaration has contracts written
+or not**.
+
+#### Possible Later Changes
+
+If needed, we can add `IsWritten` information to `FunctionContractSequence`
+to indicate if this sequence is written in source or not.
+This may be helpful for toolings.
+
+But in the first version, we don't have to do that.
+
+### ResultNameDecl
+
+A post contract can introduce a name to represent the return value of the
+function. e.g.,
+
+```C++
+int f(int x) post(result: result > x);
+```
+
+We introduce a new `ValueDecl` for this name.
+
+```C++
+class ResultNameDecl final : public ValueDecl {
+ ResultNameDecl(DeclContext *DC, SourceLocation Loc,
+ IdentifierInfo *Name, QualType T);
+
+public:
+ static ResultNameDecl *Create(ASTContext &Ctx, FunctionDecl *FD,
+ SourceLocation Loc, IdentifierInfo *Name,
+ QualType T);
+};
+```
+
+We don't use `VarDecl` here because the result name doesn't introduce a new
+variable. It doesn't have its own storage, initializer or lifetime. During
+code generation, references to the `ResultNameDecl` are bound to the return
+value of the function. Sema rejects a result name on a function returning
+`void`.
----------------
EricWF wrote:
I agree with using `ValueDecl`, not `BindingDecl`.
[expr.prim.id.unqual]p6 makes the result name an lvalue of type `const U` (or
`const T` for a `T&` return). In CodeGen it refers to the callee-side return
object before ABI conversion, which [expr.call]p10 allows to be a temporary
distinct from the caller's result object. That's what makes register returns
work.
The result name has to be capturable by lambdas in the predicate. We also need
to diagnose conflicts with parameter names and enclosing lambda scopes
([basic.scope.contract]p2), and it should participate in `-Wunused` and
`-Wshadow`.
For deduced return types (`auto f() post(r: ...)`), the return type isn't known
when the postcondition is parsed. I'd late-parse the postcondition after the
body, like class members, rather than rebuild it after deduction. Whatever you
do needs to cover non-templated functions and lambdas with deduced return types
too, not just templates.
https://github.com/llvm/llvm-project/pull/229978
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits