}
};
+/***********************************************************
+ * Tuple of result identifier (possibly null) and statement.
+ * This is used to store out contracts: out(id){ ensure }
+ */
+Ensure::Ensure()
+{
+ this->id = NULL;
+ this->ensure = NULL;
+}
+
+Ensure::Ensure(Identifier *id, Statement *ensure)
+{
+ this->id = id;
+ this->ensure = ensure;
+}
+
+Ensure Ensure::syntaxCopy()
+{
+ return Ensure(id, ensure->syntaxCopy());
+}
+
+/*****************************************
+ * Do syntax copy of an array of Ensure's.
+ */
+Ensures *Ensure::arraySyntaxCopy(Ensures *a)
+{
+ Ensures *b = NULL;
+ if (a)
+ {
+ b = a->copy();
+ for (size_t i = 0; i < a->length; i++)
+ (*b)[i] = (*a)[i].syntaxCopy();
+ }
+ return b;
+}
+
/********************************* FuncDeclaration ****************************/
FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type)
fdrequire = NULL;
fdensure = NULL;
mangleString = NULL;
- outId = NULL;
vresult = NULL;
returnLabel = NULL;
fensure = NULL;
+ frequires = NULL;
+ fensures = NULL;
fbody = NULL;
localsymtab = NULL;
vthis = NULL;
FuncDeclaration *f =
s ? (FuncDeclaration *)s
: new FuncDeclaration(loc, endloc, ident, storage_class, type->syntaxCopy());
- f->outId = outId;
- f->frequire = frequire ? frequire->syntaxCopy() : NULL;
- f->fensure = fensure ? fensure->syntaxCopy() : NULL;
- f->fbody = fbody ? fbody->syntaxCopy() : NULL;
+ f->frequires = frequires ? Statement::arraySyntaxCopy(frequires) : NULL;
+ f->fensures = fensures ? Ensure::arraySyntaxCopy(fensures) : NULL;
+ f->fbody = fbody ? fbody->syntaxCopy() : NULL;
assert(!fthrows); // deprecated
return f;
}
fd->flags |= FUNCFLAGinferScope;
}
+// Returns true if a contract can appear without a function body.
+static bool allowsContractWithoutBody(FuncDeclaration *funcdecl)
+{
+ assert(!funcdecl->fbody);
+
+ /* Contracts can only appear without a body when they are virtual
+ * interface functions or abstract.
+ */
+ Dsymbol *parent = funcdecl->toParent();
+ InterfaceDeclaration *id = parent->isInterfaceDeclaration();
+
+ if (!funcdecl->isAbstract() &&
+ (funcdecl->fensures || funcdecl->frequires) &&
+ !(id && funcdecl->isVirtual()))
+ {
+ ClassDeclaration *cd = parent->isClassDeclaration();
+ if (!(cd && cd->isAbstract()))
+ return false;
+ }
+ return true;
+}
+
// Do the semantic analysis on the external interface to the function.
void FuncDeclaration::semantic(Scope *sc)
error("destructors, postblits and invariants are not allowed in union %s", ud->toChars());
}
- /* Contracts can only appear without a body when they are virtual interface functions
- */
- if (!fbody && (fensure || frequire) && !(id && isVirtual()))
- error("in and out contracts require function body");
-
if (parent->isStructDeclaration())
{
if (isCtorDeclaration())
// Reflect this->type to f because it could be changed by findVtblIndex
f = type->toTypeFunction();
+Ldone:
+ /* Contracts can only appear without a body when they are virtual interface functions
+ */
+ if (!fbody && !allowsContractWithoutBody(this))
+ error("in and out contracts can only appear without a body when they are virtual interface functions or abstract");
+
/* Do not allow template instances to add virtual functions
* to a class.
*/
if (isMain())
checkDmain(); // Check main() parameters and return type
-Ldone:
/* Purity and safety can be inferred for some functions by examining
* the function body.
*/
*/
static bool needsFensure(FuncDeclaration *fd)
{
- if (fd->fensure)
+ if (fd->fensures)
return true;
for (size_t i = 0; i < fd->foverrides.length; i++)
}
/****************************************************
- * Rewrite contracts as nested functions, then call them. Doing it as nested
- * functions means that overriding functions can call them.
+ * Check whether result variable can be built.
+ * Returns:
+ * `true` if the function has a return type that
+ * is different from `void`.
+ */
+static bool canBuildResultVar(FuncDeclaration *fd)
+{
+ TypeFunction *f = (TypeFunction *)fd->type;
+ return f && f->nextOf() && f->nextOf()->toBasetype()->ty != Tvoid;
+}
+
+/****************************************************
+ * Rewrite contracts as statements.
* Params:
- * fd = the function to rewrite contracts for
+ * fdx = the function to rewrite contracts for
*/
static void buildEnsureRequire(FuncDeclaration *fdx)
{
+ if (fdx->frequires)
+ {
+ /* in { statements1... }
+ * in { statements2... }
+ * ...
+ * becomes:
+ * in { { statements1... } { statements2... } ... }
+ */
+ assert(fdx->frequires->length);
+ Loc loc = (*fdx->frequires)[0]->loc;
+ Statements *s = new Statements;
+ for (size_t i = 0; i < fdx->frequires->length; i++)
+ {
+ Statement *r = (*fdx->frequires)[i];
+ s->push(new ScopeStatement(r->loc, r, r->loc));
+ }
+ fdx->frequire = new CompoundStatement(loc, s);
+ }
+
+ if (fdx->fensures)
+ {
+ /* out(id1) { statements1... }
+ * out(id2) { statements2... }
+ * ...
+ * becomes:
+ * out(__result) { { ref id1 = __result; { statements1... } }
+ * { ref id2 = __result; { statements2... } } ... }
+ */
+ assert(fdx->fensures->length);
+ Loc loc = (*fdx->fensures)[0].ensure->loc;
+ Statements *s = new Statements;
+ for (size_t i = 0; i < fdx->fensures->length; i++)
+ {
+ Ensure r = (*fdx->fensures)[i];
+ if (r.id && canBuildResultVar(fdx))
+ {
+ Loc rloc = r.ensure->loc;
+ IdentifierExp *resultId = new IdentifierExp(rloc, Id::result);
+ ExpInitializer *init = new ExpInitializer(rloc, resultId);
+ StorageClass stc = STCref | STCtemp | STCresult;
+ VarDeclaration *decl = new VarDeclaration(rloc, NULL, r.id, init);
+ decl->storage_class = stc;
+ ExpStatement *sdecl = new ExpStatement(rloc, decl);
+ s->push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc));
+ }
+ else
+ {
+ s->push(r.ensure);
+ }
+ }
+ fdx->fensure = new CompoundStatement(loc, s);
+ }
+
if (!fdx->isVirtual())
return;
+ /* Rewrite contracts as nested functions, then call them. Doing it as nested
+ * functions means that overriding functions can call them.
+ */
TypeFunction *f = (TypeFunction *)fdx->type;
if (fdx->frequire)
fdx->fdrequire = fd;
}
- if (!fdx->outId && f->nextOf() && f->nextOf()->toBasetype()->ty != Tvoid)
- fdx->outId = Id::result; // provide a default
-
if (fdx->fensure)
{
/* out (result) { ... }
Loc loc = fdx->fensure->loc;
Parameters *fparams = new Parameters();
Parameter *p = NULL;
- if (fdx->outId)
+ if (canBuildResultVar(fdx))
{
- p = new Parameter(STCref | STCconst, f->nextOf(), fdx->outId, NULL, NULL);
+ p = new Parameter(STCref | STCconst, f->nextOf(), Id::result, NULL, NULL);
fparams->push(p);
}
TypeFunction *tf = new TypeFunction(ParameterList(fparams), Type::tvoid, LINKd);
fd->fbody = fdx->fensure;
Statement *s1 = new ExpStatement(loc, fd);
Expression *eresult = NULL;
- if (fdx->outId)
- eresult = new IdentifierExp(loc, fdx->outId);
+ if (canBuildResultVar(fdx))
+ eresult = new IdentifierExp(loc, Id::result);
Expression *e = new CallExp(loc, new VarExp(loc, fd, false), eresult);
Statement *s2 = new ExpStatement(loc, e);
fdx->fensure = new CompoundStatement(loc, s1, s2);
unsigned oldErrors = global.errors;
- if (frequire)
+ if (frequires)
{
for (size_t i = 0; i < foverrides.length; i++)
{
FuncDeclaration *fdv = foverrides[i];
- if (fdv->fbody && !fdv->frequire)
+ if (fdv->fbody && !fdv->frequires)
{
error("cannot have an in contract when overriden function %s does not have an in contract", fdv->toPrettyChars());
break;
}
// Remember whether we need to generate an 'out' contract.
- bool needEnsure = needsFensure(this);
+ const bool needEnsure = needsFensure(this);
- if (fbody || frequire || needEnsure)
+ if (fbody || frequires || needEnsure)
{
/* Symbol table into which we place parameters and nested functions,
* solely to diagnose name collisions.
}
frequire = mergeFrequire(frequire);
- fensure = mergeFensure(fensure, outId);
+ fensure = mergeFensure(fensure, Id::result);
Statement *freq = frequire;
Statement *fens = fensure;
{
/* fensure is composed of the [out] contracts
*/
- if (f->next->ty == Tvoid && outId)
- error("void functions have no result");
+ if (f->next->ty == Tvoid && fensures)
+ {
+ for (size_t i = 0; i < fensures->length; i++)
+ {
+ Ensure e = (*fensures)[i];
+ if (e.id)
+ {
+ error(e.ensure->loc, "`void` functions have no result");
+ //fens = NULL;
+ }
+ }
+ }
sc2 = scout; //push
sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPEensure;
}
// If declaration has no body, don't set sbody to prevent incorrect codegen.
- InterfaceDeclaration *id = parent->isInterfaceDeclaration();
- if (fbody || (id && (fdensure || fdrequire) && isVirtual()))
+ if (fbody || allowsContractWithoutBody(this))
fbody = sbody;
}
}
}
- if (naked && (fensure || frequire))
+ if (naked && (fensures || frequires))
error("naked assembly functions with contracts are not supported");
sc2->callSuper = 0;
* So, in here it may be a temporary type for vresult, and after
* fbody->semantic() running, vresult->type might be modified.
*/
- vresult = new VarDeclaration(loc, tret, outId ? outId : Id::result, NULL);
- vresult->storage_class |= STCnodtor;
-
- if (outId == Id::result)
- vresult->storage_class |= STCtemp;
+ vresult = new VarDeclaration(loc, tret, Id::result, NULL);
+ vresult->storage_class |= STCnodtor | STCtemp;
if (!isVirtual())
vresult->storage_class |= STCconst;
vresult->storage_class |= STCresult;
* be completed before code generation occurs.
* https://issues.dlang.org/show_bug.cgi?id=3602
*/
- if (fdv->frequire && fdv->semanticRun != PASSsemantic3done)
+ if (fdv->frequires && fdv->semanticRun != PASSsemantic3done)
{
assert(fdv->_scope);
Scope *sc = fdv->_scope->push();
//printf("fdv->fensure: %s\n", fdv->fensure->toChars());
// Make the call: __ensure(result)
Expression *eresult = NULL;
- if (outId)
+ if (canBuildResultVar(this))
{
eresult = new IdentifierExp(loc, oid);
case TOKinvariant:
{
Token *t = peek(&token);
- if ((t->value == TOKlparen && peek(t)->value == TOKrparen) ||
- t->value == TOKlcurly)
+ if (t->value == TOKlparen || t->value == TOKlcurly)
{
- // invariant {}
- // invariant() {}
+ // invariant { statements... }
+ // invariant() { statements... }
+ // invariant (expression);
s = parseInvariant(pAttrs);
}
else
/*****************************************
* Parse an invariant definition:
- * invariant() { body }
+ * invariant { statements... }
+ * invariant() { statements... }
+ * invariant (expression);
* Current token is 'invariant'.
*/
StorageClass stc = pAttrs ? pAttrs->storageClass : STCundefined;
nextToken();
- if (token.value == TOKlparen) // optional ()
+ if (token.value == TOKlparen) // optional () or invariant (expression);
{
nextToken();
- check(TOKrparen);
+ if (token.value != TOKrparen) // invariant (expression);
+ {
+ Expression *e = parseAssignExp();
+ Expression *msg = NULL;
+ if (token.value == TOKcomma)
+ {
+ nextToken();
+ if (token.value != TOKrparen)
+ {
+ msg = parseAssignExp();
+ if (token.value == TOKcomma)
+ nextToken();
+ }
+ }
+ check(TOKrparen);
+ check(TOKsemicolon);
+ e = new AssertExp(loc, e, msg);
+ ExpStatement *fbody = new ExpStatement(loc, e);
+ InvariantDeclaration *f = new InvariantDeclaration(loc, token.loc, stc);
+ f->fbody = fbody;
+ return f;
+ }
+ else
+ {
+ nextToken();
+ }
}
InvariantDeclaration *f = new InvariantDeclaration(loc, Loc(), stc);
// The following is irrelevant, as it is overridden by sc->linkage in
// TypeFunction::semantic
linkage = LINKd; // nested functions have D linkage
+ bool requireDo = false;
L1:
switch (token.value)
{
case TOKlcurly:
- if (f->frequire || f->fensure)
+ if (requireDo)
error("missing body { ... } after in or out");
f->fbody = parseStatement(PSsemi);
f->endloc = endloc;
break;
case TOKin:
+ {
+ // in { statements... }
+ // in (expression)
+ Loc loc = token.loc;
nextToken();
- if (f->frequire)
- error("redundant 'in' statement");
- f->frequire = parseStatement(PScurly | PSscope);
+ if (!f->frequires)
+ {
+ f->frequires = new Statements();
+ }
+ if (token.value == TOKlparen)
+ {
+ nextToken();
+ Expression *e = parseAssignExp();
+ Expression *msg = NULL;
+ if (token.value == TOKcomma)
+ {
+ nextToken();
+ if (token.value != TOKrparen)
+ {
+ msg = parseAssignExp();
+ if (token.value == TOKcomma)
+ nextToken();
+ }
+ }
+ check(TOKrparen);
+ e = new AssertExp(loc, e, msg);
+ f->frequires->push(new ExpStatement(loc, e));
+ requireDo = false;
+ }
+ else
+ {
+ f->frequires->push(parseStatement(PScurly | PSscope));
+ requireDo = true;
+ }
goto L1;
+ }
case TOKout:
- // parse: out (identifier) { statement }
+ {
+ // out { statements... }
+ // out (; expression)
+ // out (identifier) { statements... }
+ // out (identifier; expression)
+ Loc loc = token.loc;
nextToken();
+ if (!f->fensures)
+ {
+ f->fensures = new Ensures();
+ }
+ Identifier *id = NULL;
if (token.value != TOKlcurly)
{
check(TOKlparen);
- if (token.value != TOKidentifier)
- error("(identifier) following 'out' expected, not %s", token.toChars());
- f->outId = token.ident;
- nextToken();
+ if (token.value != TOKidentifier && token.value != TOKsemicolon)
+ error("`(identifier) { ... }` or `(identifier; expression)` following `out` expected, not `%s`", token.toChars());
+ if (token.value != TOKsemicolon)
+ {
+ id = token.ident;
+ nextToken();
+ }
+ if (token.value == TOKsemicolon)
+ {
+ nextToken();
+ Expression *e = parseAssignExp();
+ Expression *msg = NULL;
+ if (token.value == TOKcomma)
+ {
+ nextToken();
+ if (token.value != TOKrparen)
+ {
+ msg = parseAssignExp();
+ if (token.value == TOKcomma)
+ nextToken();
+ }
+ }
+ check(TOKrparen);
+ e = new AssertExp(loc, e, msg);
+ f->fensures->push(Ensure(id, new ExpStatement(loc, e)));
+ requireDo = false;
+ goto L1;
+ }
check(TOKrparen);
}
- if (f->fensure)
- error("redundant 'out' statement");
- f->fensure = parseStatement(PScurly | PSscope);
+ f->fensures->push(Ensure(id, parseStatement(PScurly | PSscope)));
+ requireDo = true;
goto L1;
+ }
case TOKsemicolon:
if (!literal)
{
// Bugzilla 15799: Semicolon becomes a part of function declaration
- // only when neither of contracts exists.
- if (!f->frequire && !f->fensure)
+ // only when 'do' is not required
+ if (!requireDo)
nextToken();
break;
}
Ldefault:
if (literal)
{
- const char *sbody = (f->frequire || f->fensure) ? "body " : "";
+ const char *sbody = requireDo ? "do " : "";
error("missing %s{ ... } for function literal", sbody);
}
- else if (!f->frequire && !f->fensure) // allow these even with no body
+ else if (!requireDo) // allow these even with no body
{
error("semicolon expected following function declaration");
}