From acae7b21bc026150c2c01465e4ab0eacb20bd44d Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Thu, 7 Jan 2021 18:30:30 +0100 Subject: [PATCH] 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. --- gcc/d/dmd/MERGE | 2 +- gcc/d/dmd/arraytypes.h | 2 + gcc/d/dmd/declaration.h | 19 +- gcc/d/dmd/func.c | 206 ++++++++++++++---- gcc/d/dmd/hdrgen.c | 81 +++++-- gcc/d/dmd/parse.c | 139 ++++++++++-- gcc/d/dmd/statement.c | 27 ++- gcc/d/dmd/statement.h | 1 + .../gdc.test/compilable/extra-files/header1.d | 31 ++- .../gdc.test/fail_compilation/fail17502.d | 8 +- .../gdc.test/runnable/testcontracts.d | 133 +++++++++++ 11 files changed, 551 insertions(+), 98 deletions(-) diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 25b2b3ac965..f6c8f6f02cf 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 ea57214f734..627464a635f 100644 --- a/gcc/d/dmd/arraytypes.h +++ b/gcc/d/dmd/arraytypes.h @@ -61,3 +61,5 @@ typedef Array ReturnStatements; typedef Array GotoStatements; typedef Array TemplateInstances; + +typedef Array Ensures; diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index a4e776697fc..19e4d1a5aa8 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 53621adcc41..f8f43dc09bb 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 a04a8c523a3..a351930ce45 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 e414a4d69c0..80aaac089ec 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 9d2e7e22869..76dfe1d3f06 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 4ceec87566c..08eb5fd0fa5 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(); diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d index d8221eb882a..5eb6afd1787 100644 --- a/gcc/testsuite/gdc.test/compilable/extra-files/header1.d +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d @@ -10,6 +10,30 @@ static assert(true, "message"); alias double mydbl; +alias fl1 = function () + in {} + in (true) + out (; true) + out (r; true) + out + { + } + out (r) + { + } + do + { + return 2; + }; + +alias fl2 = function () + in (true) + out(; true) + out(r; true) + { + return 2; + }; + int testmain() in { @@ -133,11 +157,11 @@ template Foo(T, int V) } try - bar(1, 2); + bar(1, 2); catch(Object o) - x++; + x++; finally - x--; + x--; Object o; synchronized (o) @@ -244,6 +268,7 @@ class Test pure nothrow @safe @nogc unittest {} pure nothrow @safe @nogc invariant {} + pure nothrow @safe @nogc invariant (true); pure nothrow @safe @nogc new (size_t sz) { return null; } pure nothrow @safe @nogc delete (void* p) { } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail17502.d b/gcc/testsuite/gdc.test/fail_compilation/fail17502.d index 8202b1e9380..b1366d136b9 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail17502.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail17502.d @@ -1,10 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/fail17502.d(12): Error: function fail17502.Foo.foo void functions have no result -fail_compilation/fail17502.d(13): Error: cannot have parameter of type const(void) -fail_compilation/fail17502.d(16): Error: function fail17502.Foo.bar void functions have no result -fail_compilation/fail17502.d(17): Error: cannot have parameter of type const(void) +fail_compilation/fail17502.d(13): Error: function `fail17502.Foo.foo` `void` functions have no result +fail_compilation/fail17502.d(13): Error: undefined identifier `res` +fail_compilation/fail17502.d(17): Error: function `fail17502.Foo.bar` `void` functions have no result +fail_compilation/fail17502.d(17): Error: undefined identifier `res` --- */ class Foo diff --git a/gcc/testsuite/gdc.test/runnable/testcontracts.d b/gcc/testsuite/gdc.test/runnable/testcontracts.d index 9a2639d1afb..f99a10afff2 100644 --- a/gcc/testsuite/gdc.test/runnable/testcontracts.d +++ b/gcc/testsuite/gdc.test/runnable/testcontracts.d @@ -421,6 +421,56 @@ void test6417() (new Bug6417).bar(); } +/*******************************************/ +// 6549 + +class C6549 +{ + static int ocount = 0; + static int icount = 0; + + abstract int foo(int) + in { ++icount; } + out { ++ocount; } +} + +class CD6549 : C6549 +{ + override int foo(int) + in { assert(false); } + do { return 10; } +} + +abstract class D6549 +{ + static int icount = 0; + static int ocount = 0; + + int foo(int) + in { ++icount; } + out { ++ocount; } +} + +class DD6549 : D6549 +{ + override int foo(int) + in { assert(false); } + do { return 10; } +} + +void test6549() +{ + auto c = new CD6549; + c.foo(10); + assert(C6549.icount == 1); + assert(C6549.ocount == 1); + + auto d = new DD6549; + d.foo(10); + assert(D6549.icount == 1); + assert(D6549.ocount == 1); +} + /*******************************************/ // 7218 @@ -1030,6 +1080,81 @@ void test14779() /*******************************************/ +//******************************************/ +// DIP 1009 + +int dip1009_1(int x) + in (x > 0, "x must be positive!") + out (r; r < 0, "r must be negative!") + in (true, "cover trailing comma case",) + out (; true, "cover trailing comma case",) +{ + return -x; +} + +int dip1009_2(int x) + in (x > 0) + out (r; r < 0) +{ + return -x; +} + +int dip1009_3(int x) +in (x > 0,) +out (r; r < 0,) +do +{ + return -x; +} + +void dip1009_4(int x) + in (x > 0) + out (; x > 1) +{ + x += 1; +} + +interface DIP1009_5 +{ + void dip1009_5(int x) + in (x > 0) + out (; x > 1); +} + +int dip1009_6(int x, int y) + in (x > 0) + out (r; r > 1) + out (; x > 0) + in (y > 0) + in (x + y > 1) + out (r; r > 1) +{ + return x+y; +} + +int dip1009_7(int x) + in (x > 0) + in { assert(x > 1); } + out { assert(x > 2); } + out (; x > 3) + out (r; r > 3) +{ + x += 2; + return x; +} + +class DIP1009_8 +{ + private int x = 4; + invariant (x > 0, "x must stay positive"); + invariant (x > 1, "x must be greater than one",); + invariant (x > 2); + invariant (x > 3,); + void foo(){ x = 5; } +} + +/*******************************************/ + int main() { test1(); @@ -1043,6 +1168,7 @@ int main() test9(); test4785(); test6417(); + test6549(); test7218(); test7517(); test8073(); @@ -1051,6 +1177,13 @@ int main() test15524(); test15524a(); test14779(); + dip1009_1(1); + dip1009_2(1); + dip1009_3(1); + dip1009_4(1); + dip1009_6(1, 1); + dip1009_7(3); + new DIP1009_8().foo(); printf("Success\n"); return 0; -- 2.30.2