aboutsummaryrefslogtreecommitdiff
path: root/gcc/d
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2021-01-07 18:30:30 +0100
committerIain Buclaw <ibuclaw@gdcproject.org>2021-01-09 23:45:46 +0100
commitacae7b21bc026150c2c01465e4ab0eacb20bd44d (patch)
treeb0ed635955a5668753257db827e230358448ee0d /gcc/d
parentf2a5346244a18c89bebac078657d0f74dbb24622 (diff)
downloadgcc-acae7b21bc026150c2c01465e4ab0eacb20bd44d.zip
gcc-acae7b21bc026150c2c01465e4ab0eacb20bd44d.tar.gz
gcc-acae7b21bc026150c2c01465e4ab0eacb20bd44d.tar.bz2
d: Implement expression-based contract syntax
Expression-based contract syntax has been added. Contracts that consist of a single assertion can now be written more succinctly and multiple `in` or `out` contracts can be specified for the same function. Reviewed-on: https://github.com/dlang/dmd/pull/12106 gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd e598f69c0.
Diffstat (limited to 'gcc/d')
-rw-r--r--gcc/d/dmd/MERGE2
-rw-r--r--gcc/d/dmd/arraytypes.h2
-rw-r--r--gcc/d/dmd/declaration.h19
-rw-r--r--gcc/d/dmd/func.c206
-rw-r--r--gcc/d/dmd/hdrgen.c81
-rw-r--r--gcc/d/dmd/parse.c139
-rw-r--r--gcc/d/dmd/statement.c27
-rw-r--r--gcc/d/dmd/statement.h1
8 files changed, 386 insertions, 91 deletions
diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE
index 25b2b3a..f6c8f6f 100644
--- a/gcc/d/dmd/MERGE
+++ b/gcc/d/dmd/MERGE
@@ -1,4 +1,4 @@
-9038e64c5b67a10763d32893f53bb6c610df3595
+e598f69c0726ad1bf6b2e15e0b60d7cead737fad
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
diff --git a/gcc/d/dmd/arraytypes.h b/gcc/d/dmd/arraytypes.h
index ea57214..627464a 100644
--- a/gcc/d/dmd/arraytypes.h
+++ b/gcc/d/dmd/arraytypes.h
@@ -61,3 +61,5 @@ typedef Array<class ReturnStatement *> ReturnStatements;
typedef Array<class GotoStatement *> GotoStatements;
typedef Array<class TemplateInstance *> TemplateInstances;
+
+typedef Array<struct Ensure> Ensures;
diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h
index a4e7766..19e4d1a 100644
--- a/gcc/d/dmd/declaration.h
+++ b/gcc/d/dmd/declaration.h
@@ -20,6 +20,16 @@ class LabelDsymbol;
class Initializer;
class Module;
class ForeachStatement;
+struct Ensure
+{
+ Identifier *id;
+ Statement *ensure;
+
+ Ensure();
+ Ensure(Identifier *id, Statement *ensure);
+ Ensure syntaxCopy();
+ static Ensures *arraySyntaxCopy(Ensures *a);
+};
class FuncDeclaration;
class ExpInitializer;
class StructDeclaration;
@@ -516,8 +526,10 @@ class FuncDeclaration : public Declaration
{
public:
Types *fthrows; // Array of Type's of exceptions (not used)
- Statement *frequire;
- Statement *fensure;
+ Statements *frequires; // in contracts
+ Ensures *fensures; // out contracts
+ Statement *frequire; // lowered in contract
+ Statement *fensure; // lowered out contract
Statement *fbody;
FuncDeclarations foverrides; // functions this function overrides
@@ -526,8 +538,7 @@ public:
const char *mangleString; // mangled symbol created from mangleExact()
- Identifier *outId; // identifier for out statement
- VarDeclaration *vresult; // variable corresponding to outId
+ VarDeclaration *vresult; // result variable for out contracts
LabelDsymbol *returnLabel; // where the return goes
// used to prevent symbols in different
diff --git a/gcc/d/dmd/func.c b/gcc/d/dmd/func.c
index 53621adc..f8f43dc 100644
--- a/gcc/d/dmd/func.c
+++ b/gcc/d/dmd/func.c
@@ -292,6 +292,42 @@ public:
}
};
+/***********************************************************
+ * 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)
@@ -314,10 +350,11 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla
fdrequire = NULL;
fdensure = NULL;
mangleString = NULL;
- outId = NULL;
vresult = NULL;
returnLabel = NULL;
fensure = NULL;
+ frequires = NULL;
+ fensures = NULL;
fbody = NULL;
localsymtab = NULL;
vthis = NULL;
@@ -372,10 +409,9 @@ Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s)
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;
}
@@ -441,6 +477,28 @@ static void initInferAttributes(FuncDeclaration *fd)
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)
@@ -780,11 +838,6 @@ 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())
@@ -1157,6 +1210,12 @@ void FuncDeclaration::semantic(Scope *sc)
// 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.
*/
@@ -1186,7 +1245,6 @@ void FuncDeclaration::semantic(Scope *sc)
if (isMain())
checkDmain(); // Check main() parameters and return type
-Ldone:
/* Purity and safety can be inferred for some functions by examining
* the function body.
*/
@@ -1270,7 +1328,7 @@ void FuncDeclaration::semantic2(Scope *sc)
*/
static bool needsFensure(FuncDeclaration *fd)
{
- if (fd->fensure)
+ if (fd->fensures)
return true;
for (size_t i = 0; i < fd->foverrides.length; i++)
@@ -1287,16 +1345,83 @@ static bool needsFensure(FuncDeclaration *fd)
}
/****************************************************
- * 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)
@@ -1322,9 +1447,6 @@ static void buildEnsureRequire(FuncDeclaration *fdx)
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) { ... }
@@ -1335,9 +1457,9 @@ static void buildEnsureRequire(FuncDeclaration *fdx)
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);
@@ -1350,8 +1472,8 @@ static void buildEnsureRequire(FuncDeclaration *fdx)
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);
@@ -1435,13 +1557,13 @@ void FuncDeclaration::semantic3(Scope *sc)
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;
@@ -1450,9 +1572,9 @@ void FuncDeclaration::semantic3(Scope *sc)
}
// 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.
@@ -2039,7 +2161,7 @@ void FuncDeclaration::semantic3(Scope *sc)
}
frequire = mergeFrequire(frequire);
- fensure = mergeFensure(fensure, outId);
+ fensure = mergeFensure(fensure, Id::result);
Statement *freq = frequire;
Statement *fens = fensure;
@@ -2075,8 +2197,18 @@ void FuncDeclaration::semantic3(Scope *sc)
{
/* 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;
@@ -2263,8 +2395,7 @@ void FuncDeclaration::semantic3(Scope *sc)
}
// 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;
}
@@ -2277,7 +2408,7 @@ void FuncDeclaration::semantic3(Scope *sc)
}
}
- if (naked && (fensure || frequire))
+ if (naked && (fensures || frequires))
error("naked assembly functions with contracts are not supported");
sc2->callSuper = 0;
@@ -2610,11 +2741,8 @@ void FuncDeclaration::buildResultVar(Scope *sc, Type *tret)
* 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;
@@ -2685,7 +2813,7 @@ Statement *FuncDeclaration::mergeFrequire(Statement *sf)
* 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();
@@ -2758,7 +2886,7 @@ Statement *FuncDeclaration::mergeFensure(Statement *sf, Identifier *oid)
//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);
diff --git a/gcc/d/dmd/hdrgen.c b/gcc/d/dmd/hdrgen.c
index a04a8c5..a351930 100644
--- a/gcc/d/dmd/hdrgen.c
+++ b/gcc/d/dmd/hdrgen.c
@@ -1950,32 +1950,70 @@ public:
int saveauto = hgs->autoMember;
hgs->tpltMember = 0;
hgs->autoMember = 0;
-
buf->writenl();
-
+ bool requireDo = false;
// in{}
- if (f->frequire)
+ if (f->frequires)
{
- buf->writestring("in");
- buf->writenl();
- f->frequire->accept(this);
+ for (size_t i = 0; i < f->frequires->length; i++)
+ {
+ Statement *frequire = (*f->frequires)[i];
+ buf->writestring("in");
+ if (ExpStatement *es = frequire->isExpStatement())
+ {
+ assert(es->exp && es->exp->op == TOKassert);
+ buf->writestring(" (");
+ ((AssertExp *)es->exp)->e1->accept(this);
+ buf->writeByte(')');
+ buf->writenl();
+ requireDo = false;
+ }
+ else
+ {
+ buf->writenl();
+ frequire->accept(this);
+ requireDo = true;
+ }
+ }
}
// out{}
- if (f->fensure)
+ if (f->fensures)
{
- buf->writestring("out");
- if (f->outId)
+ for (size_t i = 0; i < f->fensures->length; i++)
{
- buf->writeByte('(');
- buf->writestring(f->outId->toChars());
- buf->writeByte(')');
+ Ensure fensure = (*f->fensures)[i];
+ buf->writestring("out");
+ if (ExpStatement *es = fensure.ensure->isExpStatement())
+ {
+ assert(es->exp && es->exp->op == TOKassert);
+ buf->writestring(" (");
+ if (fensure.id)
+ {
+ buf->writestring(fensure.id->toChars());
+ }
+ buf->writestring("; ");
+ ((AssertExp *)es->exp)->e1->accept(this);
+ buf->writeByte(')');
+ buf->writenl();
+ requireDo = false;
+ }
+ else
+ {
+ if (fensure.id)
+ {
+ buf->writeByte('(');
+ buf->writestring(fensure.id->toChars());
+ buf->writeByte(')');
+ }
+ buf->writenl();
+ fensure.ensure->accept(this);
+ requireDo = true;
+ }
}
- buf->writenl();
- f->fensure->accept(this);
}
- if (f->frequire || f->fensure)
+ if (requireDo)
{
buf->writestring("body");
buf->writenl();
@@ -2093,7 +2131,18 @@ public:
if (stcToBuffer(buf, d->storage_class))
buf->writeByte(' ');
buf->writestring("invariant");
- bodyToBuffer(d);
+ if (ExpStatement *es = d->fbody->isExpStatement())
+ {
+ assert(es->exp && es->exp->op == TOKassert);
+ buf->writestring(" (");
+ ((AssertExp *)es->exp)->e1->accept(this);
+ buf->writestring(");");
+ buf->writenl();
+ }
+ else
+ {
+ bodyToBuffer(d);
+ }
}
void visit(UnitTestDeclaration *d)
diff --git a/gcc/d/dmd/parse.c b/gcc/d/dmd/parse.c
index e414a4d..80aaac0 100644
--- a/gcc/d/dmd/parse.c
+++ b/gcc/d/dmd/parse.c
@@ -372,11 +372,11 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl, PrefixAttributes
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
@@ -1846,7 +1846,9 @@ Dsymbol *Parser::parseSharedStaticDtor(PrefixAttributes *pAttrs)
/*****************************************
* Parse an invariant definition:
- * invariant() { body }
+ * invariant { statements... }
+ * invariant() { statements... }
+ * invariant (expression);
* Current token is 'invariant'.
*/
@@ -1856,10 +1858,35 @@ Dsymbol *Parser::parseInvariant(PrefixAttributes *pAttrs)
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);
@@ -4426,11 +4453,12 @@ FuncDeclaration *Parser::parseContracts(FuncDeclaration *f)
// 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;
@@ -4448,35 +4476,100 @@ L1:
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;
}
@@ -4486,10 +4579,10 @@ L1:
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");
}
diff --git a/gcc/d/dmd/statement.c b/gcc/d/dmd/statement.c
index 9d2e7e2..76dfe1d 100644
--- a/gcc/d/dmd/statement.c
+++ b/gcc/d/dmd/statement.c
@@ -109,6 +109,24 @@ Statement *Statement::syntaxCopy()
return NULL;
}
+/*************************************
+ * Do syntax copy of an array of Statement's.
+ */
+Statements *Statement::arraySyntaxCopy(Statements *a)
+{
+ Statements *b = NULL;
+ if (a)
+ {
+ b = a->copy();
+ for (size_t i = 0; i < a->length; i++)
+ {
+ Statement *s = (*a)[i];
+ (*b)[i] = s ? s->syntaxCopy() : NULL;
+ }
+ }
+ return b;
+}
+
void Statement::print()
{
fprintf(stderr, "%s\n", toChars());
@@ -560,14 +578,7 @@ CompoundStatement *CompoundStatement::create(Loc loc, Statement *s1, Statement *
Statement *CompoundStatement::syntaxCopy()
{
- Statements *a = new Statements();
- a->setDim(statements->length);
- for (size_t i = 0; i < statements->length; i++)
- {
- Statement *s = (*statements)[i];
- (*a)[i] = s ? s->syntaxCopy() : NULL;
- }
- return new CompoundStatement(loc, a);
+ return new CompoundStatement(loc, Statement::arraySyntaxCopy(statements));
}
Statements *CompoundStatement::flatten(Scope *)
diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h
index 4ceec87..08eb5fd 100644
--- a/gcc/d/dmd/statement.h
+++ b/gcc/d/dmd/statement.h
@@ -74,6 +74,7 @@ public:
Statement(Loc loc);
virtual Statement *syntaxCopy();
+ static Statements *arraySyntaxCopy(Statements *a);
void print();
const char *toChars();