From patchwork Tue Nov 21 13:32:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Buclaw X-Patchwork-Id: 80473 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 62784385803D for ; Tue, 21 Nov 2023 13:33:15 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mout-p-102.mailbox.org (mout-p-102.mailbox.org [80.241.56.152]) by sourceware.org (Postfix) with ESMTPS id 245703858D37 for ; Tue, 21 Nov 2023 13:32:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 245703858D37 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=gdcproject.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gdcproject.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 245703858D37 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=80.241.56.152 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700573557; cv=none; b=hm2u5AB+pZ6gcnazj15huNKFUH/OBz1Sh9+HpT0ABFla1WX9e8UP1IKq8cbqgy+Yd67ukbsUwqnhFKutUIiffaVrGFiwqqR+3y+AVa7NZdaH+bE0NpbVdZRAjFJSqaKnG6/DMfNoA0xMolKDRxjC0Mb5qXh7zFFOXIQiNmdCBCQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700573557; c=relaxed/simple; bh=ZKBoQShnenKXXZo9+sXlLgAUryFYFZ3bw4csGHRqMFE=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=WZPY6PKp2svblvg1ZC/zdRVT5zrJqd2ybVlx+zDTa71BqisLJDWFbbodYLZy+fkPIgEBh9n3A2IQDUg5+LqkP2fzwkVCp3suyiZYNwdMok/8CARvTG3AklPO0RZFN78ncTeUpaKkVWKoGacyttAem7ObnQ7DI6mmKLYNvnN5E7o= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from smtp2.mailbox.org (smtp2.mailbox.org [IPv6:2001:67c:2050:b231:465::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-102.mailbox.org (Postfix) with ESMTPS id 4SZQKR6ZXmz9slP; Tue, 21 Nov 2023 14:32:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gdcproject.org; s=MBO0001; t=1700573547; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=Nqyeagyt+CNIPg1ErpT4LkH+uBQH31+kqGyFR6cOikg=; b=FoAsx0v+ZsZIPFp+XHMkqwy7wPkqeBL2WotzNkgymhhteJfenlOJMyzCJbUUcjDb28KNjs 4tJVXqstIii4pUJMnaK1zGw3VX7vHTPG+AmUSHnj16BgddbwFz2n2aorzZdDWKRnU4VidD Tq0KFiDeGvEdN7BjEmWbHqVf9YPPGB3X5rJV52Vmioz8BQiZwjhhMVkbMCpnmcpbGv6yIe 7KOnQkIlrqrbNZQvapQU0Q/8mgBDgssb6oS20gPEQrmsGM8jCXubb48LeNtou2ETi2Mg5P WagYypa2HZqwC05tYNeaLN9eM2SG3agYzbctJpPRfeRlnMEqO6XWwNMobNDBSw== From: Iain Buclaw To: gcc-patches@gcc.gnu.org Cc: Iain Buclaw Subject: [committed] d: Merge upstream dmd 65a3da148c, phobos fc06c514a. Date: Tue, 21 Nov 2023 14:32:24 +0100 Message-Id: <20231121133224.105698-1-ibuclaw@gdcproject.org> MIME-Version: 1.0 X-Rspamd-Queue-Id: 4SZQKR6ZXmz9slP X-Spam-Status: No, score=-13.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Hi, This patch merges the D front-end with upstream dmd 65a3da148c, and the standard library with phobos fc06c514a. Synchronizing with the upstream development branch as of 2023-11-12. D front-end changes: - Import latest bug fixes from dmd v2.106.0-beta.1. Phobos changes: - Import latest bug fixes from phobos v2.106.0-beta.1. - `std.range.primitives.isForwardRange' now takes an optional element type. Bootstrapped and regression tested on x86_64-linux-gnu/-m32, committed to mainline. Regards, Iain. --- gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd 65a3da148c. libphobos/ChangeLog: * src/MERGE: Merge upstream phobos fc06c514a. --- gcc/d/dmd/MERGE | 2 +- gcc/d/dmd/arrayop.d | 2 +- gcc/d/dmd/canthrow.d | 3 +- gcc/d/dmd/compiler.d | 1 + gcc/d/dmd/cond.d | 1 + gcc/d/dmd/cparse.d | 24 +- gcc/d/dmd/ctfeexpr.d | 227 +-- gcc/d/dmd/dcast.d | 1 + gcc/d/dmd/dmangle.d | 1 + gcc/d/dmd/dmodule.d | 2 +- gcc/d/dmd/dsymbolsem.d | 13 +- gcc/d/dmd/dtemplate.d | 1 + gcc/d/dmd/expression.d | 1424 ++--------------- gcc/d/dmd/expression.h | 49 +- gcc/d/dmd/expressionsem.d | 1313 ++++++++++++++- gcc/d/dmd/globals.d | 6 + gcc/d/dmd/hdrgen.d | 1 + gcc/d/dmd/iasmgcc.d | 4 +- gcc/d/dmd/initsem.d | 1 + gcc/d/dmd/lexer.d | 18 + gcc/d/dmd/opover.d | 1 + gcc/d/dmd/optimize.d | 16 +- gcc/d/dmd/semantic3.d | 3 +- gcc/d/dmd/statementsem.d | 3 +- gcc/d/dmd/staticcond.d | 1 + gcc/d/dmd/traits.d | 17 +- gcc/d/dmd/typesem.d | 7 +- gcc/testsuite/gdc.test/compilable/previewin.d | 6 - .../gdc.test/fail_compilation/fail212.d | 6 +- gcc/testsuite/gdc.test/runnable/previewin.d | 20 +- libphobos/src/MERGE | 2 +- libphobos/src/std/container/array.d | 11 + libphobos/src/std/logger/package.d | 1 + libphobos/src/std/range/primitives.d | 15 + 34 files changed, 1644 insertions(+), 1559 deletions(-) diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 235db4b2ef1..0cf9b5fd4a8 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -643b1261bba0757d97efa3ff1f63e461271eb000 +65a3da148c0c700a6c928f0e13799b2a7d34fcbe 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/arrayop.d b/gcc/d/dmd/arrayop.d index 25bbb3f32ad..c3b8526a9a4 100644 --- a/gcc/d/dmd/arrayop.d +++ b/gcc/d/dmd/arrayop.d @@ -172,7 +172,7 @@ Expression arrayOp(BinAssignExp e, Scope* sc) } if (e.e1.op == EXP.arrayLiteral) { - return e.e1.modifiableLvalue(sc, e.e1); + return e.e1.modifiableLvalue(sc); } return arrayOp(e.isBinExp(), sc); diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d index 8aece3bdd71..67305922df6 100644 --- a/gcc/d/dmd/canthrow.d +++ b/gcc/d/dmd/canthrow.d @@ -22,6 +22,7 @@ import dmd.declaration; import dmd.dsymbol; import dmd.errorsink; import dmd.expression; +import dmd.expressionsem; import dmd.func; import dmd.globals; import dmd.init; @@ -80,7 +81,7 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink) if (!f.isDtorDeclaration()) errorSupplementalInferredAttr(f, 10, false, STC.nothrow_); - e.checkOverriddenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow"); + f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isnothrow, "not nothrow"); } else if (func) { diff --git a/gcc/d/dmd/compiler.d b/gcc/d/dmd/compiler.d index e85cc202281..8b8a453d45e 100644 --- a/gcc/d/dmd/compiler.d +++ b/gcc/d/dmd/compiler.d @@ -12,6 +12,7 @@ module dmd.compiler; import dmd.arraytypes; +import dmd.ctfeexpr; import dmd.dmodule; import dmd.expression; import dmd.mtype; diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d index a8d099433a2..568b639e0b6 100644 --- a/gcc/d/dmd/cond.d +++ b/gcc/d/dmd/cond.d @@ -29,6 +29,7 @@ import dmd.globals; import dmd.identifier; import dmd.location; import dmd.mtype; +import dmd.optimize; import dmd.typesem; import dmd.common.outbuffer; import dmd.rootobject; diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d index b8e80527ec7..f0c834972d6 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -2023,6 +2023,9 @@ final class CParser(AST) : Parser!AST } symbols.push(s); } + if (level == LVL.global && !id) + error("expected identifier for declaration"); + first = false; switch (token.value) @@ -2736,7 +2739,7 @@ final class CParser(AST) : Parser!AST private AST.Type cparseDeclarator(DTR declarator, AST.Type tbase, out Identifier pident, ref Specifier specifier) { - //printf("cparseDeclarator(%d, %p)\n", declarator, t); + //printf("cparseDeclarator(%d, %s)\n", declarator, tbase.toChars()); AST.Types constTypes; // all the Types that will need `const` applied to them /* Insert tx -> t into @@ -2755,6 +2758,7 @@ final class CParser(AST) : Parser!AST AST.Type parseDecl(AST.Type t) { + //printf("parseDecl() t: %s\n", t.toChars()); AST.Type ts; while (1) { @@ -2770,9 +2774,18 @@ final class CParser(AST) : Parser!AST break; case TOK.leftParenthesis: // ( declarator ) + //printf("leftParen\n"); /* like: T (*fp)(); * T ((*fp))(); */ + auto tk = &token; + if (!isCDeclarator(tk, declarator)) + { + /* Not ( declarator ), might be parameter-list + */ + ts = t; + break; + } nextToken(); if (token.value == TOK.__stdcall) // T (__stdcall*fp)(); @@ -2786,6 +2799,7 @@ final class CParser(AST) : Parser!AST break; case TOK.mul: // pointer + //printf("star\n"); t = new AST.TypePointer(t); nextToken(); // add post fixes const/volatile/restrict/_Atomic @@ -2797,6 +2811,7 @@ final class CParser(AST) : Parser!AST continue; default: + //printf("default %s\n", token.toChars()); if (declarator == DTR.xdirect) { if (!t || t.isTypeIdentifier()) @@ -2914,7 +2929,7 @@ final class CParser(AST) : Parser!AST if (specifier._pure) stc |= STC.pure_; AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc); - // tf = tf.addSTC(storageClass); // TODO + //tf = tf.addSTC(storageClass); // TODO insertTx(ts, tf, t); // ts -> ... -> tf -> t if (ts != tf) @@ -2927,6 +2942,8 @@ final class CParser(AST) : Parser!AST } break; } + if (declarator == DTR.xdirect && !pident) + error("expected identifier for declarator"); return ts; } @@ -4556,6 +4573,7 @@ final class CParser(AST) : Parser!AST */ private bool isCDeclarator(ref Token* pt, DTR declarator) { + //printf("isCDeclarator()\n"); auto t = pt; while (1) { @@ -4578,6 +4596,8 @@ final class CParser(AST) : Parser!AST else if (t.value == TOK.leftParenthesis) { t = peek(t); + if (t.value == TOK.__stdcall) + t = peek(t); if (!isCDeclarator(t, declarator)) return false; if (t.value != TOK.rightParenthesis) diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d index ddfb57d22d7..c93269fb321 100644 --- a/gcc/d/dmd/ctfeexpr.d +++ b/gcc/d/dmd/ctfeexpr.d @@ -35,75 +35,98 @@ import dmd.root.rmem; import dmd.tokens; import dmd.visitor; - -/*********************************************************** - * A reference to a class, or an interface. We need this when we - * point to a base class (we must record what the type is). +/****************************************************************/ +/* A type meant as a union of all the Expression types, + * to serve essentially as a Variant that will sit on the stack + * during CTFE to reduce memory consumption. */ -extern (C++) final class ClassReferenceExp : Expression +extern (D) struct UnionExp { - StructLiteralExp value; - - extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) @safe + // yes, default constructor does nothing + extern (D) this(Expression e) { - super(loc, EXP.classReference); - assert(lit && lit.sd && lit.sd.isClassDeclaration()); - this.value = lit; - this.type = type; + memcpy(&this, cast(void*)e, e.size); } - ClassDeclaration originalClass() + /* Extract pointer to Expression + */ + extern (D) Expression exp() return { - return value.sd.isClassDeclaration(); + return cast(Expression)&u; } - // Return index of the field, or -1 if not found - private int getFieldIndex(Type fieldtype, uint fieldoffset) + /* Convert to an allocated Expression + */ + extern (D) Expression copy() { - ClassDeclaration cd = originalClass(); - uint fieldsSoFar = 0; - for (size_t j = 0; j < value.elements.length; j++) + Expression e = exp(); + //if (e.size > sizeof(u)) printf("%s\n", EXPtoString(e.op).ptr); + assert(e.size <= u.sizeof); + switch (e.op) { - while (j - fieldsSoFar >= cd.fields.length) - { - fieldsSoFar += cd.fields.length; - cd = cd.baseClass; - } - VarDeclaration v2 = cd.fields[j - fieldsSoFar]; - if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size()) - { - return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar)); - } + case EXP.cantExpression: return CTFEExp.cantexp; + case EXP.voidExpression: return CTFEExp.voidexp; + case EXP.break_: return CTFEExp.breakexp; + case EXP.continue_: return CTFEExp.continueexp; + case EXP.goto_: return CTFEExp.gotoexp; + default: return e.copy(); } - return -1; } - // Return index of the field, or -1 if not found - // Same as getFieldIndex, but checks for a direct match with the VarDeclaration - int findFieldIndexByName(VarDeclaration v) - { - ClassDeclaration cd = originalClass(); - size_t fieldsSoFar = 0; - for (size_t j = 0; j < value.elements.length; j++) - { - while (j - fieldsSoFar >= cd.fields.length) - { - fieldsSoFar += cd.fields.length; - cd = cd.baseClass; - } - VarDeclaration v2 = cd.fields[j - fieldsSoFar]; - if (v == v2) - { - return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar)); - } - } - return -1; - } +private: + // Ensure that the union is suitably aligned. + align(8) union _AnonStruct_u + { + char[__traits(classInstanceSize, Expression)] exp; + char[__traits(classInstanceSize, IntegerExp)] integerexp; + char[__traits(classInstanceSize, ErrorExp)] errorexp; + char[__traits(classInstanceSize, RealExp)] realexp; + char[__traits(classInstanceSize, ComplexExp)] complexexp; + char[__traits(classInstanceSize, SymOffExp)] symoffexp; + char[__traits(classInstanceSize, StringExp)] stringexp; + char[__traits(classInstanceSize, ArrayLiteralExp)] arrayliteralexp; + char[__traits(classInstanceSize, AssocArrayLiteralExp)] assocarrayliteralexp; + char[__traits(classInstanceSize, StructLiteralExp)] structliteralexp; + char[__traits(classInstanceSize, CompoundLiteralExp)] compoundliteralexp; + char[__traits(classInstanceSize, NullExp)] nullexp; + char[__traits(classInstanceSize, DotVarExp)] dotvarexp; + char[__traits(classInstanceSize, AddrExp)] addrexp; + char[__traits(classInstanceSize, IndexExp)] indexexp; + char[__traits(classInstanceSize, SliceExp)] sliceexp; + char[__traits(classInstanceSize, VectorExp)] vectorexp; + } + + _AnonStruct_u u; +} - override void accept(Visitor v) - { - v.visit(this); - } +void emplaceExp(T : Expression, Args...)(void* p, Args args) +{ + static if (__VERSION__ < 2099) + const init = typeid(T).initializer; + else + const init = __traits(initSymbol, T); + p[0 .. __traits(classInstanceSize, T)] = init[]; + (cast(T)p).__ctor(args); +} + +void emplaceExp(T : UnionExp)(T* p, Expression e) +{ + memcpy(p, cast(void*)e, e.size); +} + +// Generate an error message when this exception is not caught +void generateUncaughtError(ThrownExceptionExp tee) +{ + UnionExp ue = void; + Expression e = resolveSlice((*tee.thrown.value.elements)[0], &ue); + StringExp se = e.toStringExp(); + error(tee.thrown.loc, "uncaught CTFE exception `%s(%s)`", tee.thrown.type.toChars(), se ? se.toChars() : e.toChars()); + /* Also give the line where the throw statement was. We won't have it + * in the case where the ThrowStatement is generated internally + * (eg, in ScopeStatement) + */ + if (tee.loc.isValid() && !tee.loc.equals(tee.thrown.loc)) + .errorSupplemental(tee.loc, "thrown from here"); } /************************* @@ -121,100 +144,6 @@ int findFieldIndexByName(const StructDeclaration sd, const VarDeclaration v) pur return -1; } -/*********************************************************** - * Fake class which holds the thrown exception. - * Used for implementing exception handling. - */ -extern (C++) final class ThrownExceptionExp : Expression -{ - ClassReferenceExp thrown; // the thing being tossed - - extern (D) this(const ref Loc loc, ClassReferenceExp victim) @safe - { - super(loc, EXP.thrownException); - this.thrown = victim; - this.type = victim.type; - } - - override const(char)* toChars() const - { - return "CTFE ThrownException"; - } - - // Generate an error message when this exception is not caught - extern (D) void generateUncaughtError() - { - UnionExp ue = void; - Expression e = resolveSlice((*thrown.value.elements)[0], &ue); - StringExp se = e.toStringExp(); - error(thrown.loc, "uncaught CTFE exception `%s(%s)`", thrown.type.toChars(), se ? se.toChars() : e.toChars()); - /* Also give the line where the throw statement was. We won't have it - * in the case where the ThrowStatement is generated internally - * (eg, in ScopeStatement) - */ - if (loc.isValid() && !loc.equals(thrown.loc)) - .errorSupplemental(loc, "thrown from here"); - } - - override void accept(Visitor v) - { - v.visit(this); - } -} - -/*********************************************************** - * This type is only used by the interpreter. - */ -extern (C++) final class CTFEExp : Expression -{ - extern (D) this(EXP tok) - { - super(Loc.initial, tok); - type = Type.tvoid; - } - - override const(char)* toChars() const - { - switch (op) - { - case EXP.cantExpression: - return ""; - case EXP.voidExpression: - return "cast(void)0"; - case EXP.showCtfeContext: - return ""; - case EXP.break_: - return ""; - case EXP.continue_: - return ""; - case EXP.goto_: - return ""; - default: - assert(0); - } - } - - extern (D) __gshared CTFEExp cantexp; - extern (D) __gshared CTFEExp voidexp; - extern (D) __gshared CTFEExp breakexp; - extern (D) __gshared CTFEExp continueexp; - extern (D) __gshared CTFEExp gotoexp; - /* Used when additional information is needed regarding - * a ctfe error. - */ - extern (D) __gshared CTFEExp showcontext; - - extern (D) static bool isCantExp(const Expression e) @safe - { - return e && e.op == EXP.cantExpression; - } - - extern (D) static bool isGotoExp(const Expression e) @safe - { - return e && e.op == EXP.goto_; - } -} - // True if 'e' is CTFEExp::cantexp, or an exception bool exceptionOrCantInterpret(const Expression e) @safe { diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index f769473f591..eb3890bb58f 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -38,6 +38,7 @@ import dmd.init; import dmd.intrange; import dmd.mtype; import dmd.opover; +import dmd.optimize; import dmd.root.ctfloat; import dmd.common.outbuffer; import dmd.root.rmem; diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/dmangle.d index c58b5857482..2bedccb71c1 100644 --- a/gcc/d/dmd/dmangle.d +++ b/gcc/d/dmd/dmangle.d @@ -152,6 +152,7 @@ import dmd.identifier; import dmd.mtype; import dmd.root.ctfloat; import dmd.common.outbuffer; +import dmd.optimize; import dmd.root.aav; import dmd.root.string; import dmd.root.stringtable; diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 548928a0124..e6dde181fab 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -793,7 +793,7 @@ extern (C++) final class Module : Package } else { - const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + const bool doUnittests = global.params.parsingUnittestsRequired(); scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.v.vin; p.nextToken(); diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index 397c5e53d7f..637edd7bf08 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -57,6 +57,7 @@ import dmd.nogc; import dmd.nspace; import dmd.objc; import dmd.opover; +import dmd.optimize; import dmd.parse; import dmd.root.array; import dmd.root.filename; @@ -1951,7 +1952,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + const bool doUnittests = global.params.parsingUnittestsRequired(); auto loc = adjustLocForMixin(str, cd.loc, global.params.mixinOut); scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.v.vin; @@ -3384,9 +3385,13 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (!tf.isNaked() && !(funcdecl.isThis() || funcdecl.isNested())) { - OutBuffer buf; - MODtoBuffer(buf, tf.mod); - .error(funcdecl.loc, "%s `%s` without `this` cannot be `%s`", funcdecl.kind, funcdecl.toPrettyChars, buf.peekChars()); + import core.bitop; + auto mods = MODtoChars(tf.mod); + .error(funcdecl.loc, "%s `%s` without `this` cannot be `%s`", funcdecl.kind, funcdecl.toPrettyChars, mods); + if (tf.next && tf.next.ty != Tvoid && popcnt(tf.mod) == 1) + .errorSupplemental(funcdecl.loc, + "did you mean to use `%s(%s)` as the return type?", mods, tf.next.toChars()); + tf.mod = 0; // remove qualifiers } diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index 883f4ac94cf..4cf1bae7537 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -69,6 +69,7 @@ import dmd.initsem; import dmd.location; import dmd.mtype; import dmd.opover; +import dmd.optimize; import dmd.root.array; import dmd.common.outbuffer; import dmd.rootobject; diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index 87611f4690f..47902213504 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -18,25 +18,19 @@ import core.stdc.stdio; import core.stdc.string; import dmd.aggregate; -import dmd.aliasthis; -import dmd.arrayop; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; import dmd.gluelayer; -import dmd.ctfeexpr; -import dmd.ctorflow; import dmd.dclass; import dmd.declaration; import dmd.dimport; import dmd.dmodule; -import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; import dmd.dtemplate; import dmd.errors; import dmd.errorsink; -import dmd.expressionsem; import dmd.func; import dmd.globals; import dmd.hdrgen; @@ -45,39 +39,20 @@ import dmd.identifier; import dmd.init; import dmd.location; import dmd.mtype; -import dmd.opover; -import dmd.optimize; import dmd.root.complex; import dmd.root.ctfloat; -import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.optional; import dmd.root.rmem; import dmd.rootobject; import dmd.root.string; import dmd.root.utf; -import dmd.safe; import dmd.target; import dmd.tokens; import dmd.visitor; enum LOGSEMANTIC = false; -void emplaceExp(T : Expression, Args...)(void* p, Args args) -{ - static if (__VERSION__ < 2099) - const init = typeid(T).initializer; - else - const init = __traits(initSymbol, T); - p[0 .. __traits(classInstanceSize, T)] = init[]; - (cast(T)p).__ctor(args); -} - -void emplaceExp(T : UnionExp)(T* p, Expression e) -{ - memcpy(p, cast(void*)e, e.size); -} - /// Return value for `checkModifiable` enum Modifiable { @@ -118,45 +93,6 @@ inout(Expression) lastComma(inout Expression e) } -/*********************************** - * Determine if a `this` is needed to access `d`. - * Params: - * sc = context - * d = declaration to check - * Returns: - * true means a `this` is needed - */ -bool isNeedThisScope(Scope* sc, Declaration d) -{ - if (sc.intypeof == 1) - return false; - - AggregateDeclaration ad = d.isThis(); - if (!ad) - return false; - //printf("d = %s, ad = %s\n", d.toChars(), ad.toChars()); - - for (Dsymbol s = sc.parent; s; s = s.toParentLocal()) - { - //printf("\ts = %s %s, toParent2() = %p\n", s.kind(), s.toChars(), s.toParent2()); - if (AggregateDeclaration ad2 = s.isAggregateDeclaration()) - { - if (ad2 == ad) - return false; - else if (ad2.isNested()) - continue; - else - return true; - } - if (FuncDeclaration f = s.isFuncDeclaration()) - { - if (f.isMemberLocal()) - break; - } - } - return true; -} - /**************************************** * Expand tuples in-place. * @@ -315,70 +251,6 @@ TemplateDeclaration getFuncTemplateDecl(Dsymbol s) @safe return null; } -/****************************************************************/ -/* A type meant as a union of all the Expression types, - * to serve essentially as a Variant that will sit on the stack - * during CTFE to reduce memory consumption. - */ -extern (D) struct UnionExp -{ - // yes, default constructor does nothing - extern (D) this(Expression e) - { - memcpy(&this, cast(void*)e, e.size); - } - - /* Extract pointer to Expression - */ - extern (D) Expression exp() return - { - return cast(Expression)&u; - } - - /* Convert to an allocated Expression - */ - extern (D) Expression copy() - { - Expression e = exp(); - //if (e.size > sizeof(u)) printf("%s\n", EXPtoString(e.op).ptr); - assert(e.size <= u.sizeof); - switch (e.op) - { - case EXP.cantExpression: return CTFEExp.cantexp; - case EXP.voidExpression: return CTFEExp.voidexp; - case EXP.break_: return CTFEExp.breakexp; - case EXP.continue_: return CTFEExp.continueexp; - case EXP.goto_: return CTFEExp.gotoexp; - default: return e.copy(); - } - } - -private: - // Ensure that the union is suitably aligned. - align(8) union _AnonStruct_u - { - char[__traits(classInstanceSize, Expression)] exp; - char[__traits(classInstanceSize, IntegerExp)] integerexp; - char[__traits(classInstanceSize, ErrorExp)] errorexp; - char[__traits(classInstanceSize, RealExp)] realexp; - char[__traits(classInstanceSize, ComplexExp)] complexexp; - char[__traits(classInstanceSize, SymOffExp)] symoffexp; - char[__traits(classInstanceSize, StringExp)] stringexp; - char[__traits(classInstanceSize, ArrayLiteralExp)] arrayliteralexp; - char[__traits(classInstanceSize, AssocArrayLiteralExp)] assocarrayliteralexp; - char[__traits(classInstanceSize, StructLiteralExp)] structliteralexp; - char[__traits(classInstanceSize, CompoundLiteralExp)] compoundliteralexp; - char[__traits(classInstanceSize, NullExp)] nullexp; - char[__traits(classInstanceSize, DotVarExp)] dotvarexp; - char[__traits(classInstanceSize, AddrExp)] addrexp; - char[__traits(classInstanceSize, IndexExp)] indexexp; - char[__traits(classInstanceSize, SliceExp)] sliceexp; - char[__traits(classInstanceSize, VectorExp)] vectorexp; - } - - _AnonStruct_u u; -} - /************************ TypeDotIdExp ************************************/ /* Things like: * int.size @@ -678,71 +550,6 @@ extern (C++) abstract class Expression : ASTNode return false; } - /******************************* - * Give error if we're not an lvalue. - * If we can, convert expression to be an lvalue. - */ - Expression toLvalue(Scope* sc, Expression e) - { - if (!e) - e = this; - else if (!loc.isValid()) - loc = e.loc; - - if (e.op == EXP.type) - error(loc, "`%s` is a `%s` definition and cannot be modified", e.type.toChars(), e.type.kind()); - else - error(loc, "`%s` is not an lvalue and cannot be modified", e.toChars()); - - return ErrorExp.get(); - } - - Expression modifiableLvalue(Scope* sc, Expression e) - { - //printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type.toChars()); - // See if this expression is a modifiable lvalue (i.e. not const) - if (checkModifiable(this, sc) == Modifiable.yes) - { - assert(type); - if (!type.isMutable()) - { - if (auto dve = this.isDotVarExp()) - { - if (isNeedThisScope(sc, dve.var)) - for (Dsymbol s = sc.func; s; s = s.toParentLocal()) - { - FuncDeclaration ff = s.isFuncDeclaration(); - if (!ff) - break; - if (!ff.type.isMutable) - { - error(loc, "cannot modify `%s` in `%s` function", toChars(), MODtoChars(type.mod)); - return ErrorExp.get(); - } - } - } - error(loc, "cannot modify `%s` expression `%s`", MODtoChars(type.mod), toChars()); - return ErrorExp.get(); - } - else if (!type.isAssignable()) - { - error(loc, "cannot modify struct instance `%s` of type `%s` because it contains `const` or `immutable` members", - toChars(), type.toChars()); - return ErrorExp.get(); - } - } - return toLvalue(sc, e); - } - - /**************************************** - * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc. - */ - Expression resolveLoc(const ref Loc loc, Scope* sc) - { - this.loc = loc; - return this; - } - /**************************************** * Check that the expression has a valid type. * If not, generates an error "... has no type". @@ -835,417 +642,6 @@ extern (C++) abstract class Expression : ASTNode return checkValue(); } - extern (D) final bool checkDeprecated(Scope* sc, Dsymbol s) - { - return s.checkDeprecated(loc, sc); - } - - extern (D) final bool checkDisabled(Scope* sc, Dsymbol s) - { - if (auto d = s.isDeclaration()) - { - return d.checkDisabled(loc, sc); - } - - return false; - } - - /********************************************* - * Calling function f. - * Check the purity, i.e. if we're in a pure function - * we can only call other pure functions. - * Returns true if error occurs. - */ - extern (D) final bool checkPurity(Scope* sc, FuncDeclaration f) - { - if (!sc.func) - return false; - if (sc.func == f) - return false; - if (sc.intypeof == 1) - return false; - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) - return false; - - // If the call has a pure parent, then the called func must be pure. - if (!f.isPure() && checkImpure(sc, loc, null, f)) - { - error(loc, "`pure` %s `%s` cannot call impure %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), f.kind(), - f.toPrettyChars()); - - if (!f.isDtorDeclaration()) - errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_); - - checkOverriddenDtor(sc, f, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure"); - return true; - } - return false; - } - - /** - * Checks whether `f` is a generated `DtorDeclaration` that hides a user-defined one - * which passes `check` while `f` doesn't (e.g. when the user defined dtor is pure but - * the generated dtor is not). - * In that case the method will identify and print all members causing the attribute - * missmatch. - * - * Params: - * sc = scope - * f = potential `DtorDeclaration` - * check = current check (e.g. whether it's pure) - * checkName = the kind of check (e.g. `"pure"`) - */ - extern (D) final void checkOverriddenDtor(Scope* sc, FuncDeclaration f, - scope bool function(DtorDeclaration) check, const string checkName - ) { - auto dd = f.isDtorDeclaration(); - if (!dd || !dd.isGenerated()) - return; - - // DtorDeclaration without parents should fail at an earlier stage - auto ad = cast(AggregateDeclaration) f.toParent2(); - assert(ad); - - if (ad.userDtors.length) - { - if (!check(ad.userDtors[0])) // doesn't match check (e.g. is impure as well) - return; - - // Sanity check - assert(!check(ad.fieldDtor)); - } - - dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:", - dd.isGenerated() ? "generated " : "".ptr, - ad.toChars, - cast(int) checkName.length, checkName.ptr); - - // Search for the offending fields - foreach (field; ad.fields) - { - // Only structs may define automatically called destructors - auto ts = field.type.isTypeStruct(); - if (!ts) - { - // But they might be part of a static array - auto ta = field.type.isTypeSArray(); - if (!ta) - continue; - - ts = ta.baseElemOf().isTypeStruct(); - if (!ts) - continue; - } - - auto fieldSym = ts.toDsymbol(sc); - assert(fieldSym); // Resolving ts must succeed because missing defs. should error before - - auto fieldSd = fieldSym.isStructDeclaration(); - assert(fieldSd); // ts is a TypeStruct, this would imply a malformed ASR - - if (fieldSd.dtor && !check(fieldSd.dtor)) - { - field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars()); - - if (fieldSd.dtor.isGenerated()) - checkOverriddenDtor(sc, fieldSd.dtor, check, checkName); - else - fieldSd.dtor.loc.errorSupplemental(" %.*s `%s.~this` is declared here", - cast(int) checkName.length, checkName.ptr, fieldSd.toChars()); - } - } - } - - /******************************************* - * Accessing variable v. - * Check for purity and safety violations. - * Returns true if error occurs. - */ - extern (D) final bool checkPurity(Scope* sc, VarDeclaration v) - { - //printf("v = %s %s\n", v.type.toChars(), v.toChars()); - /* Look for purity and safety violations when accessing variable v - * from current function. - */ - if (!sc.func) - return false; - if (sc.intypeof == 1) - return false; // allow violations inside typeof(expression) - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) - return false; // allow violations inside compile-time evaluated expressions and debug conditionals - if (v.ident == Id.ctfe) - return false; // magic variable never violates pure and safe - if (v.isImmutable()) - return false; // always safe and pure to access immutables... - if (v.isConst() && !v.isReference() && (v.isDataseg() || v.isParameter()) && v.type.implicitConvTo(v.type.immutableOf())) - return false; // or const global/parameter values which have no mutable indirections - if (v.storage_class & STC.manifest) - return false; // ...or manifest constants - - // accessing empty structs is pure - // https://issues.dlang.org/show_bug.cgi?id=18694 - // https://issues.dlang.org/show_bug.cgi?id=21464 - // https://issues.dlang.org/show_bug.cgi?id=23589 - if (v.type.ty == Tstruct) - { - StructDeclaration sd = (cast(TypeStruct)v.type).sym; - if (sd.members) // not opaque - { - if (sd.semanticRun >= PASS.semanticdone) - sd.determineSize(v.loc); - if (sd.hasNoFields) - return false; - } - } - - bool err = false; - if (v.isDataseg()) - { - // https://issues.dlang.org/show_bug.cgi?id=7533 - // Accessing implicit generated __gate is pure. - if (v.ident == Id.gate) - return false; - - if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v)) - { - error(loc, "`pure` %s `%s` cannot access mutable static data `%s`", - sc.func.kind(), sc.func.toPrettyChars(), v.toChars()); - err = true; - } - } - else - { - /* Given: - * void f() { - * int fx; - * pure void g() { - * int gx; - * /+pure+/ void h() { - * int hx; - * /+pure+/ void i() { } - * } - * } - * } - * i() can modify hx and gx but not fx - */ - - Dsymbol vparent = v.toParent2(); - for (Dsymbol s = sc.func; !err && s; s = s.toParentP(vparent)) - { - if (s == vparent) - break; - - if (AggregateDeclaration ad = s.isAggregateDeclaration()) - { - if (ad.isNested()) - continue; - break; - } - FuncDeclaration ff = s.isFuncDeclaration(); - if (!ff) - break; - if (ff.isNested() || ff.isThis()) - { - if (ff.type.isImmutable() || - ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod)) - { - OutBuffer ffbuf; - OutBuffer vbuf; - MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod); - MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod); - error(loc, "%s%s `%s` cannot access %sdata `%s`", - ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars()); - err = true; - break; - } - continue; - } - break; - } - } - - /* Do not allow safe functions to access __gshared data - */ - if (v.storage_class & STC.gshared) - { - if (sc.setUnsafe(false, this.loc, - "`@safe` function `%s` cannot access `__gshared` data `%s`", sc.func, v)) - { - err = true; - } - } - - return err; - } - - /* - Check if sc.func is impure or can be made impure. - Returns true on error, i.e. if sc.func is pure and cannot be made impure. - */ - private static bool checkImpure(Scope* sc, Loc loc, const(char)* fmt, RootObject arg0) - { - return sc.func && (isRootTraitsCompilesScope(sc) - ? sc.func.isPureBypassingInference() >= PURE.weak - : sc.func.setImpure(loc, fmt, arg0)); - } - - /********************************************* - * Calling function f. - * Check the safety, i.e. if we're in a @safe function - * we can only call @safe or @trusted functions. - * Returns true if error occurs. - */ - extern (D) final bool checkSafety(Scope* sc, FuncDeclaration f) - { - if (sc.func == f) - return false; - if (sc.intypeof == 1) - return false; - if (sc.flags & SCOPE.debug_) - return false; - if ((sc.flags & SCOPE.ctfe) && sc.func) - return false; - - if (!sc.func) - { - if (sc.varDecl && !f.safetyInprocess && !f.isSafe() && !f.isTrusted()) - { - if (sc.varDecl.storage_class & STC.safe) - { - error(loc, "`@safe` variable `%s` cannot be initialized by calling `@system` function `%s`", - sc.varDecl.toChars(), f.toChars()); - return true; - } - else - { - sc.varDecl.storage_class |= STC.system; - sc.varDecl.systemInferred = true; - } - } - return false; - } - - if (!f.isSafe() && !f.isTrusted()) - { - if (isRootTraitsCompilesScope(sc) ? sc.func.isSafeBypassingInference() : sc.func.setUnsafeCall(f)) - { - if (!loc.isValid()) // e.g. implicitly generated dtor - loc = sc.func.loc; - - const prettyChars = f.toPrettyChars(); - error(loc, "`@safe` %s `%s` cannot call `@system` %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), f.kind(), - prettyChars); - if (!f.isDtorDeclaration) - errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe); - .errorSupplemental(f.loc, "`%s` is declared here", prettyChars); - - checkOverriddenDtor(sc, f, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system"); - - return true; - } - } - else if (f.isSafe() && f.safetyViolation) - { - // for dip1000 by default transition, print deprecations for calling functions that will become `@system` - if (sc.func.isSafeBypassingInference()) - { - .deprecation(this.loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars()); - errorSupplementalInferredAttr(f, 10, true, STC.safe); - } - else if (!sc.func.safetyViolation) - { - import dmd.func : AttributeViolation; - sc.func.safetyViolation = new AttributeViolation(this.loc, null, f, null, null); - } - } - return false; - } - - /********************************************* - * Calling function f. - * Check the @nogc-ness, i.e. if we're in a @nogc function - * we can only call other @nogc functions. - * Returns true if error occurs. - */ - extern (D) final bool checkNogc(Scope* sc, FuncDeclaration f) - { - if (!sc.func) - return false; - if (sc.func == f) - return false; - if (sc.intypeof == 1) - return false; - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) - return false; - /* The original expressions (`new S(...)` or `new S[...]``) will be - * verified instead. This is to keep errors related to the original code - * and not the lowering. - */ - if (f.ident == Id._d_newitemT || f.ident == Id._d_newarrayT) - return false; - - if (!f.isNogc()) - { - if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f)) - { - if (loc.linnum == 0) // e.g. implicitly generated dtor - loc = sc.func.loc; - - // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)), - // so don't print anything to avoid double error messages. - if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT - || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX - || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) - { - error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); - - if (!f.isDtorDeclaration) - f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); - } - - checkOverriddenDtor(sc, f, dd => dd.type.toTypeFunction().isnogc, "non-@nogc"); - - return true; - } - } - return false; - } - - /******************************************** - * Check that the postblit is callable if t is an array of structs. - * Returns true if error happens. - */ - extern (D) final bool checkPostblit(Scope* sc, Type t) - { - if (auto ts = t.baseElemOf().isTypeStruct()) - { - if (global.params.useTypeInfo && Type.dtypeinfo) - { - // https://issues.dlang.org/show_bug.cgi?id=11395 - // Require TypeInfo generation for array concatenation - semanticTypeInfo(sc, t); - } - - StructDeclaration sd = ts.sym; - if (sd.postblit) - { - if (sd.postblit.checkDisabled(loc, sc)) - return true; - - //checkDeprecated(sc, sd.postblit); // necessary? - checkPurity(sc, sd.postblit); - checkSafety(sc, sd.postblit); - checkNogc(sc, sd.postblit); - //checkAccess(sd, loc, sc, sd.postblit); // necessary? - return false; - } - } - return false; - } - /******************************* * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not. * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics) @@ -1308,11 +704,6 @@ extern (C++) abstract class Expression : ASTNode return this; } - final Expression optimize(int result, bool keepLvalue = false) - { - return Expression_optimize(this, result, keepLvalue); - } - final int isConst() { //printf("Expression::isConst(): %s\n", e.toChars()); @@ -1587,16 +978,6 @@ extern (C++) final class IntegerExp : Expression return typeof(return)(r); } - override Expression toLvalue(Scope* sc, Expression e) - { - if (!e) - e = this; - else if (!loc.isValid()) - loc = e.loc; - error(e.loc, "cannot modify constant `%s`", e.toChars()); - return ErrorExp.get(); - } - override void accept(Visitor v) { v.visit(this); @@ -1722,7 +1103,7 @@ extern (C++) final class IntegerExp : Expression */ extern (C++) final class ErrorExp : Expression { - private extern (D) this() + extern (D) this() { super(Loc.initial, EXP.error); type = Type.terror; @@ -1745,11 +1126,6 @@ extern (C++) final class ErrorExp : Expression return errorexp; } - override Expression toLvalue(Scope* sc, Expression e) - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1987,11 +1363,6 @@ extern (C++) class IdentifierExp : Expression return true; } - override final Expression toLvalue(Scope* sc, Expression e) - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2036,11 +1407,6 @@ extern (C++) final class DsymbolExp : Expression return true; } - override Expression toLvalue(Scope* sc, Expression e) - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2087,16 +1453,6 @@ extern (C++) class ThisExp : Expression return type.toBasetype().ty != Tclass; } - override final Expression toLvalue(Scope* sc, Expression e) - { - if (type.toBasetype().ty == Tclass) - { - // Class `this` is an rvalue; struct `this` is an lvalue. - return Expression.toLvalue(sc, e); - } - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -2464,18 +1820,6 @@ extern (C++) final class StringExp : Expression return (type && type.toBasetype().ty == Tsarray); } - override Expression toLvalue(Scope* sc, Expression e) - { - //printf("StringExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL); - return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e); - } - - override Expression modifiableLvalue(Scope* sc, Expression e) - { - error(loc, "cannot modify string literal `%s`", toChars()); - return ErrorExp.get(); - } - /******************************** * Convert string contents to a 0 terminated string, * allocated by mem.xmalloc(). @@ -3050,14 +2394,6 @@ extern (C++) final class StructLiteralExp : Expression return -1; } - override Expression toLvalue(Scope* sc, Expression e) - { - if (sc.flags & SCOPE.Cfile) - return this; // C struct literals are lvalues - else - return Expression.toLvalue(sc, e); - } - override void accept(Visitor v) { v.visit(this); @@ -3203,15 +2539,6 @@ extern (C++) final class TemplateExp : Expression return fd !is null; } - override Expression toLvalue(Scope* sc, Expression e) - { - if (!fd) - return Expression.toLvalue(sc, e); - - assert(sc); - return symbolToExp(fd, loc, sc, true); - } - override bool checkType() { error(loc, "%s `%s` has no type", td.kind(), toChars()); @@ -3409,43 +2736,6 @@ extern (C++) final class VarExp : SymbolExp return true; } - override Expression toLvalue(Scope* sc, Expression e) - { - if (var.storage_class & STC.manifest) - { - error(loc, "manifest constant `%s` cannot be modified", var.toChars()); - return ErrorExp.get(); - } - if (var.storage_class & STC.lazy_ && !delegateWasExtracted) - { - error(loc, "lazy variable `%s` cannot be modified", var.toChars()); - return ErrorExp.get(); - } - if (var.ident == Id.ctfe) - { - error(loc, "cannot modify compiler-generated variable `__ctfe`"); - return ErrorExp.get(); - } - if (var.ident == Id.dollar) // https://issues.dlang.org/show_bug.cgi?id=13574 - { - error(loc, "cannot modify operator `$`"); - return ErrorExp.get(); - } - return this; - } - - override Expression modifiableLvalue(Scope* sc, Expression e) - { - //printf("VarExp::modifiableLvalue('%s')\n", var.toChars()); - if (var.storage_class & STC.manifest) - { - error(loc, "cannot modify manifest constant `%s`", toChars()); - return ErrorExp.get(); - } - // See if this expression is a modifiable lvalue (i.e. not const) - return Expression.modifiableLvalue(sc, e); - } - override void accept(Visitor v) { v.visit(this); @@ -3472,11 +2762,6 @@ extern (C++) final class OverExp : Expression return true; } - override Expression toLvalue(Scope* sc, Expression e) - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3522,52 +2807,6 @@ extern (C++) final class FuncExp : Expression return false; } - extern (D) void genIdent(Scope* sc) - { - if (fd.ident == Id.empty) - { - const(char)[] s; - if (fd.fes) - s = "__foreachbody"; - else if (fd.tok == TOK.reserved) - s = "__lambda"; - else if (fd.tok == TOK.delegate_) - s = "__dgliteral"; - else - s = "__funcliteral"; - - DsymbolTable symtab; - if (FuncDeclaration func = sc.parent.isFuncDeclaration()) - { - if (func.localsymtab is null) - { - // Inside template constraint, symtab is not set yet. - // Initialize it lazily. - func.localsymtab = new DsymbolTable(); - } - symtab = func.localsymtab; - } - else - { - ScopeDsymbol sds = sc.parent.isScopeDsymbol(); - if (!sds.symtab) - { - // Inside template constraint, symtab may not be set yet. - // Initialize it lazily. - assert(sds.isTemplateInstance()); - sds.symtab = new DsymbolTable(); - } - symtab = sds.symtab; - } - assert(symtab); - Identifier id = Identifier.generateId(s, symtab.length() + 1); - fd.ident = id; - if (td) - td.ident = id; - symtab.insert(td ? cast(Dsymbol)td : cast(Dsymbol)fd); - } - } - override FuncExp syntaxCopy() { if (td) @@ -3815,12 +3054,6 @@ extern (C++) abstract class UnaExp : Expression } - override final Expression resolveLoc(const ref Loc loc, Scope* sc) - { - e1 = e1.resolveLoc(loc, sc); - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3936,18 +3169,6 @@ extern (C++) class BinAssignExp : BinExp return true; } - override final Expression toLvalue(Scope* sc, Expression ex) - { - // Lvalue-ness will be handled in glue layer. - return this; - } - - override final Expression modifiableLvalue(Scope* sc, Expression e) - { - // should check e1.checkModifiable() ? - return toLvalue(sc, this); - } - override void accept(Visitor v) { v.visit(this); @@ -4134,78 +3355,26 @@ extern (C++) final class DotTemplateExp : UnaExp */ extern (C++) final class DotVarExp : UnaExp { - Declaration var; - bool hasOverloads; - - extern (D) this(const ref Loc loc, Expression e, Declaration var, bool hasOverloads = true) @safe - { - if (var.isVarDeclaration()) - hasOverloads = false; - - super(loc, EXP.dotVariable, e); - //printf("DotVarExp()\n"); - this.var = var; - this.hasOverloads = hasOverloads; - } - - override bool isLvalue() - { - if (e1.op != EXP.structLiteral) - return true; - auto vd = var.isVarDeclaration(); - return !(vd && vd.isField()); - } - - override Expression toLvalue(Scope* sc, Expression e) - { - //printf("DotVarExp::toLvalue(%s)\n", toChars()); - if (sc && sc.flags & SCOPE.Cfile) - { - /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator - * is an lvalue if the first expression is an lvalue. - */ - if (!e1.isLvalue()) - return Expression.toLvalue(sc, e); - } - if (!isLvalue()) - return Expression.toLvalue(sc, e); - if (e1.op == EXP.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor)) - { - if (VarDeclaration vd = var.isVarDeclaration()) - { - auto ad = vd.isMember2(); - if (ad && ad.fields.length == sc.ctorflow.fieldinit.length) - { - foreach (i, f; ad.fields) - { - if (f == vd) - { - if (!(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor)) - { - /* If the address of vd is taken, assume it is thereby initialized - * https://issues.dlang.org/show_bug.cgi?id=15869 - */ - modifyFieldVar(loc, sc, vd, e1); - } - break; - } - } - } - } - } - return this; - } + Declaration var; + bool hasOverloads; - override Expression modifiableLvalue(Scope* sc, Expression e) + extern (D) this(const ref Loc loc, Expression e, Declaration var, bool hasOverloads = true) @safe { - version (none) - { - printf("DotVarExp::modifiableLvalue(%s)\n", toChars()); - printf("e1.type = %s\n", e1.type.toChars()); - printf("var.type = %s\n", var.type.toChars()); - } + if (var.isVarDeclaration()) + hasOverloads = false; + + super(loc, EXP.dotVariable, e); + //printf("DotVarExp()\n"); + this.var = var; + this.hasOverloads = hasOverloads; + } - return Expression.modifiableLvalue(sc, e); + override bool isLvalue() + { + if (e1.op != EXP.structLiteral) + return true; + auto vd = var.isVarDeclaration(); + return !(vd && vd.isField()); } override void accept(Visitor v) @@ -4239,49 +3408,6 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp return new DotTemplateInstanceExp(loc, e1.syntaxCopy(), ti.name, TemplateInstance.arraySyntaxCopy(ti.tiargs)); } - extern (D) bool findTempDecl(Scope* sc) - { - static if (LOGSEMANTIC) - { - printf("DotTemplateInstanceExp::findTempDecl('%s')\n", toChars()); - } - if (ti.tempdecl) - return true; - - Expression e = new DotIdExp(loc, e1, ti.name); - e = e.expressionSemantic(sc); - if (e.op == EXP.dot) - e = (cast(DotExp)e).e2; - - Dsymbol s = null; - switch (e.op) - { - case EXP.overloadSet: - s = (cast(OverExp)e).vars; - break; - - case EXP.dotTemplateDeclaration: - s = (cast(DotTemplateExp)e).td; - break; - - case EXP.scope_: - s = (cast(ScopeExp)e).sds; - break; - - case EXP.dotVariable: - s = (cast(DotVarExp)e).var; - break; - - case EXP.variable: - s = (cast(VarExp)e).var; - break; - - default: - return false; - } - return ti.updateTempDecl(sc, s); - } - override bool checkType() { // Same logic as ScopeExp.checkType() @@ -4486,13 +3612,6 @@ extern (C++) final class CallExp : UnaExp return false; } - override Expression toLvalue(Scope* sc, Expression e) - { - if (isLvalue()) - return this; - return Expression.toLvalue(sc, e); - } - override void accept(Visitor v) { v.visit(this); @@ -4600,30 +3719,6 @@ extern (C++) final class PtrExp : UnaExp return true; } - override Expression toLvalue(Scope* sc, Expression e) - { - return this; - } - - override Expression modifiableLvalue(Scope* sc, Expression e) - { - //printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type.toChars()); - Declaration var; - if (auto se = e1.isSymOffExp()) - var = se.var; - else if (auto ve = e1.isVarExp()) - var = ve.var; - if (var && var.type.isFunction_Delegate_PtrToFunction()) - { - if (var.type.isTypeFunction()) - error(loc, "function `%s` is not an lvalue and cannot be modified", var.toChars()); - else - error(loc, "function pointed to by `%s` is not an lvalue and cannot be modified", var.toChars()); - return ErrorExp.get(); - } - return Expression.modifiableLvalue(sc, e); - } - override void accept(Visitor v) { v.visit(this); @@ -4755,19 +3850,6 @@ extern (C++) final class CastExp : UnaExp e1.type.mutableOf().unSharedOf().equals(to.mutableOf().unSharedOf()); } - override Expression toLvalue(Scope* sc, Expression e) - { - if (sc && sc.flags & SCOPE.Cfile) - { - /* C11 6.5.4-5: A cast does not yield an lvalue. - */ - return Expression.toLvalue(sc, e); - } - if (isLvalue()) - return this; - return Expression.toLvalue(sc, e); - } - override void accept(Visitor v) { v.visit(this); @@ -4822,12 +3904,6 @@ extern (C++) final class VectorArrayExp : UnaExp return e1.isLvalue(); } - override Expression toLvalue(Scope* sc, Expression e) - { - e1 = e1.toLvalue(sc, e); - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -4885,18 +3961,6 @@ extern (C++) final class SliceExp : UnaExp return (type && type.toBasetype().ty == Tsarray); } - override Expression toLvalue(Scope* sc, Expression e) - { - //printf("SliceExp::toLvalue(%s) type = %s\n", toChars(), type ? type.toChars() : NULL); - return (type && type.toBasetype().ty == Tsarray) ? this : Expression.toLvalue(sc, e); - } - - override Expression modifiableLvalue(Scope* sc, Expression e) - { - error(loc, "slice expression `%s` is not a modifiable lvalue", toChars()); - return this; - } - override Optional!bool toBool() { return e1.toBool(); @@ -4964,13 +4028,6 @@ extern (C++) final class ArrayExp : UnaExp return true; } - override Expression toLvalue(Scope* sc, Expression e) - { - if (type && type.toBasetype().ty == Tvoid) - error(loc, "`void`s have no value"); - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -5018,18 +4075,6 @@ extern (C++) final class CommaExp : BinExp return e2.isLvalue(); } - override Expression toLvalue(Scope* sc, Expression e) - { - e2 = e2.toLvalue(sc, null); - return this; - } - - override Expression modifiableLvalue(Scope* sc, Expression e) - { - e2 = e2.modifiableLvalue(sc, e); - return this; - } - override Optional!bool toBool() { return e2.toBool(); @@ -5105,21 +4150,6 @@ extern (C++) final class DelegatePtrExp : UnaExp return e1.isLvalue(); } - override Expression toLvalue(Scope* sc, Expression e) - { - e1 = e1.toLvalue(sc, e); - return this; - } - - override Expression modifiableLvalue(Scope* sc, Expression e) - { - if (sc.setUnsafe(false, this.loc, "cannot modify delegate pointer in `@safe` code `%s`", this)) - { - return ErrorExp.get(); - } - return Expression.modifiableLvalue(sc, e); - } - override void accept(Visitor v) { v.visit(this); @@ -5143,21 +4173,6 @@ extern (C++) final class DelegateFuncptrExp : UnaExp return e1.isLvalue(); } - override Expression toLvalue(Scope* sc, Expression e) - { - e1 = e1.toLvalue(sc, e); - return this; - } - - override Expression modifiableLvalue(Scope* sc, Expression e) - { - if (sc.setUnsafe(false, this.loc, "cannot modify delegate function pointer in `@safe` code `%s`", this)) - { - return ErrorExp.get(); - } - return Expression.modifiableLvalue(sc, e); - } - override void accept(Visitor v) { v.visit(this); @@ -5205,23 +4220,6 @@ extern (C++) final class IndexExp : BinExp return true; } - override Expression toLvalue(Scope* sc, Expression e) - { - if (isLvalue()) - return this; - return Expression.toLvalue(sc, e); - } - - override Expression modifiableLvalue(Scope* sc, Expression e) - { - //printf("IndexExp::modifiableLvalue(%s)\n", toChars()); - Expression ex = markSettingAAElem(); - if (ex.op == EXP.error) - return ex; - - return Expression.modifiableLvalue(sc, e); - } - extern (D) Expression markSettingAAElem() { if (e1.type.toBasetype().ty == Taarray) @@ -5324,20 +4322,6 @@ extern (C++) class AssignExp : BinExp return true; } - override final Expression toLvalue(Scope* sc, Expression ex) - { - if (e1.op == EXP.slice || e1.op == EXP.arrayLength) - { - return Expression.toLvalue(sc, ex); - } - - /* In front-end level, AssignExp should make an lvalue of e1. - * Taking the address of e1 will be handled in low level layer, - * so this function does nothing. - */ - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -5733,13 +4717,6 @@ extern (C++) final class CatExp : BinExp super(loc, EXP.concatenate, e1, e2); } - override Expression resolveLoc(const ref Loc loc, Scope* sc) - { - e1 = e1.resolveLoc(loc, sc); - e2 = e2.resolveLoc(loc, sc); - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -6074,28 +5051,6 @@ extern (C++) final class CondExp : BinExp return e1.isLvalue() && e2.isLvalue(); } - override Expression toLvalue(Scope* sc, Expression ex) - { - // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2) - CondExp e = cast(CondExp)copy(); - e.e1 = e1.toLvalue(sc, null).addressOf(); - e.e2 = e2.toLvalue(sc, null).addressOf(); - e.type = type.pointerTo(); - return new PtrExp(loc, e, type); - } - - override Expression modifiableLvalue(Scope* sc, Expression e) - { - if (!e1.isLvalue() && !e2.isLvalue()) - { - error(loc, "conditional expression `%s` is not a modifiable lvalue", toChars()); - return ErrorExp.get(); - } - e1 = e1.modifiableLvalue(sc, e1); - e2 = e2.modifiableLvalue(sc, e2); - return toLvalue(sc, this); - } - override void accept(Visitor v) { v.visit(this); @@ -6145,19 +5100,6 @@ extern (C++) final class FileInitExp : DefaultInitExp super(loc, tok); } - override Expression resolveLoc(const ref Loc loc, Scope* sc) - { - //printf("FileInitExp::resolve() %s\n", toChars()); - const(char)* s; - if (op == EXP.fileFullPath) - s = FileName.toAbsolute(loc.isValid() ? loc.filename : sc._module.srcfile.toChars()); - else - s = loc.isValid() ? loc.filename : sc._module.ident.toChars(); - - Expression e = new StringExp(loc, s.toDString()); - return e.expressionSemantic(sc); - } - override void accept(Visitor v) { v.visit(this); @@ -6174,12 +5116,6 @@ extern (C++) final class LineInitExp : DefaultInitExp super(loc, EXP.line); } - override Expression resolveLoc(const ref Loc loc, Scope* sc) - { - Expression e = new IntegerExp(loc, loc.linnum, Type.tint32); - return e.expressionSemantic(sc); - } - override void accept(Visitor v) { v.visit(this); @@ -6196,13 +5132,6 @@ extern (C++) final class ModuleInitExp : DefaultInitExp super(loc, EXP.moduleString); } - override Expression resolveLoc(const ref Loc loc, Scope* sc) - { - const auto s = (sc.callsc ? sc.callsc : sc)._module.toPrettyChars().toDString(); - Expression e = new StringExp(loc, s); - return e.expressionSemantic(sc); - } - override void accept(Visitor v) { v.visit(this); @@ -6219,19 +5148,6 @@ extern (C++) final class FuncInitExp : DefaultInitExp super(loc, EXP.functionString); } - override Expression resolveLoc(const ref Loc loc, Scope* sc) - { - const(char)* s; - if (sc.callsc && sc.callsc.func) - s = sc.callsc.func.Dsymbol.toPrettyChars(); - else if (sc.func) - s = sc.func.Dsymbol.toPrettyChars(); - else - s = ""; - Expression e = new StringExp(loc, s.toDString()); - return e.expressionSemantic(sc); - } - override void accept(Visitor v) { v.visit(this); @@ -6248,29 +5164,153 @@ extern (C++) final class PrettyFuncInitExp : DefaultInitExp super(loc, EXP.prettyFunction); } - override Expression resolveLoc(const ref Loc loc, Scope* sc) + override void accept(Visitor v) + { + v.visit(this); + } +} + +/*********************************************************** + * A reference to a class, or an interface. We need this when we + * point to a base class (we must record what the type is). + */ +extern (C++) final class ClassReferenceExp : Expression +{ + StructLiteralExp value; + + extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) @safe + { + super(loc, EXP.classReference); + assert(lit && lit.sd && lit.sd.isClassDeclaration()); + this.value = lit; + this.type = type; + } + + ClassDeclaration originalClass() { - FuncDeclaration fd = (sc.callsc && sc.callsc.func) - ? sc.callsc.func - : sc.func; + return value.sd.isClassDeclaration(); + } - const(char)* s; - if (fd) + // Return index of the field, or -1 if not found + int getFieldIndex(Type fieldtype, uint fieldoffset) + { + ClassDeclaration cd = originalClass(); + uint fieldsSoFar = 0; + for (size_t j = 0; j < value.elements.length; j++) { - const funcStr = fd.Dsymbol.toPrettyChars(); - OutBuffer buf; - functionToBufferWithIdent(fd.type.isTypeFunction(), buf, funcStr, fd.isStatic); - s = buf.extractChars(); + while (j - fieldsSoFar >= cd.fields.length) + { + fieldsSoFar += cd.fields.length; + cd = cd.baseClass; + } + VarDeclaration v2 = cd.fields[j - fieldsSoFar]; + if (fieldoffset == v2.offset && fieldtype.size() == v2.type.size()) + { + return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar)); + } } - else + return -1; + } + + // Return index of the field, or -1 if not found + // Same as getFieldIndex, but checks for a direct match with the VarDeclaration + int findFieldIndexByName(VarDeclaration v) + { + ClassDeclaration cd = originalClass(); + size_t fieldsSoFar = 0; + for (size_t j = 0; j < value.elements.length; j++) { - s = ""; + while (j - fieldsSoFar >= cd.fields.length) + { + fieldsSoFar += cd.fields.length; + cd = cd.baseClass; + } + VarDeclaration v2 = cd.fields[j - fieldsSoFar]; + if (v == v2) + { + return cast(int)(value.elements.length - fieldsSoFar - cd.fields.length + (j - fieldsSoFar)); + } } + return -1; + } - Expression e = new StringExp(loc, s.toDString()); - e = e.expressionSemantic(sc); - e.type = Type.tstring; - return e; + override void accept(Visitor v) + { + v.visit(this); + } +} + +/*********************************************************** + * This type is only used by the interpreter. + */ +extern (C++) final class CTFEExp : Expression +{ + extern (D) this(EXP tok) + { + super(Loc.initial, tok); + type = Type.tvoid; + } + + override const(char)* toChars() const + { + switch (op) + { + case EXP.cantExpression: + return ""; + case EXP.voidExpression: + return "cast(void)0"; + case EXP.showCtfeContext: + return ""; + case EXP.break_: + return ""; + case EXP.continue_: + return ""; + case EXP.goto_: + return ""; + default: + assert(0); + } + } + + extern (D) __gshared CTFEExp cantexp; + extern (D) __gshared CTFEExp voidexp; + extern (D) __gshared CTFEExp breakexp; + extern (D) __gshared CTFEExp continueexp; + extern (D) __gshared CTFEExp gotoexp; + /* Used when additional information is needed regarding + * a ctfe error. + */ + extern (D) __gshared CTFEExp showcontext; + + extern (D) static bool isCantExp(const Expression e) @safe + { + return e && e.op == EXP.cantExpression; + } + + extern (D) static bool isGotoExp(const Expression e) @safe + { + return e && e.op == EXP.goto_; + } +} + +/*********************************************************** + * Fake class which holds the thrown exception. + * Used for implementing exception handling. + */ +extern (C++) final class ThrownExceptionExp : Expression +{ + ClassReferenceExp thrown; // the thing being tossed + + extern (D) this(const ref Loc loc, ClassReferenceExp victim) @safe + { + super(loc, EXP.thrownException); + this.thrown = victim; + this.type = victim.type; + } + + override const(char)* toChars() const + { + return "CTFE ThrownException"; } override void accept(Visitor v) @@ -6330,154 +5370,6 @@ extern (C++) final class GenericExp : Expression } } -/*************************************** - * Parameters: - * sc: scope - * flag: 1: do not issue error message for invalid modification - 2: the exp is a DotVarExp and a subfield of the leftmost - variable is modified - * Returns: - * Whether the type is modifiable - */ -extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyFlags.none) -{ - switch(exp.op) - { - case EXP.variable: - auto varExp = cast(VarExp)exp; - - //printf("VarExp::checkModifiable %s", varExp.toChars()); - assert(varExp.type); - return varExp.var.checkModify(varExp.loc, sc, null, flag); - - case EXP.dotVariable: - auto dotVarExp = cast(DotVarExp)exp; - - //printf("DotVarExp::checkModifiable %s %s\n", dotVarExp.toChars(), dotVarExp.type.toChars()); - if (dotVarExp.e1.op == EXP.this_) - return dotVarExp.var.checkModify(dotVarExp.loc, sc, dotVarExp.e1, flag); - - /* https://issues.dlang.org/show_bug.cgi?id=12764 - * If inside a constructor and an expression of type `this.field.var` - * is encountered, where `field` is a struct declaration with - * default construction disabled, we must make sure that - * assigning to `var` does not imply that `field` was initialized - */ - if (sc.func && sc.func.isCtorDeclaration()) - { - // if inside a constructor scope and e1 of this DotVarExp - // is another DotVarExp, then check if the leftmost expression is a `this` identifier - if (auto dve = dotVarExp.e1.isDotVarExp()) - { - // Iterate the chain of DotVarExp to find `this` - // Keep track whether access to fields was limited to union members - // s.t. one can initialize an entire struct inside nested unions - // (but not its members) - bool onlyUnion = true; - while (true) - { - auto v = dve.var.isVarDeclaration(); - assert(v); - - // Accessing union member? - auto t = v.type.isTypeStruct(); - if (!t || !t.sym.isUnionDeclaration()) - onlyUnion = false; - - // Another DotVarExp left? - if (!dve.e1 || dve.e1.op != EXP.dotVariable) - break; - - dve = cast(DotVarExp) dve.e1; - } - - if (dve.e1.op == EXP.this_) - { - scope v = dve.var.isVarDeclaration(); - /* if v is a struct member field with no initializer, no default construction - * and v wasn't intialized before - */ - if (v && v.isField() && !v._init && !v.ctorinit) - { - if (auto ts = v.type.isTypeStruct()) - { - if (ts.sym.noDefaultCtor) - { - /* checkModify will consider that this is an initialization - * of v while it is actually an assignment of a field of v - */ - scope modifyLevel = v.checkModify(dotVarExp.loc, sc, dve.e1, !onlyUnion ? (flag | ModifyFlags.fieldAssign) : flag); - if (modifyLevel == Modifiable.initialization) - { - // https://issues.dlang.org/show_bug.cgi?id=22118 - // v is a union type field that was assigned - // a variable, therefore it counts as initialization - if (v.ctorinit) - return Modifiable.initialization; - - return Modifiable.yes; - } - return modifyLevel; - } - } - } - } - } - } - - //printf("\te1 = %s\n", e1.toChars()); - return dotVarExp.e1.checkModifiable(sc, flag); - - case EXP.star: - auto ptrExp = cast(PtrExp)exp; - if (auto se = ptrExp.e1.isSymOffExp()) - { - return se.var.checkModify(ptrExp.loc, sc, null, flag); - } - else if (auto ae = ptrExp.e1.isAddrExp()) - { - return ae.e1.checkModifiable(sc, flag); - } - return Modifiable.yes; - - case EXP.slice: - auto sliceExp = cast(SliceExp)exp; - - //printf("SliceExp::checkModifiable %s\n", sliceExp.toChars()); - auto e1 = sliceExp.e1; - if (e1.type.ty == Tsarray || (e1.op == EXP.index && e1.type.ty != Tarray) || e1.op == EXP.slice) - { - return e1.checkModifiable(sc, flag); - } - return Modifiable.yes; - - case EXP.comma: - return (cast(CommaExp)exp).e2.checkModifiable(sc, flag); - - case EXP.index: - auto indexExp = cast(IndexExp)exp; - auto e1 = indexExp.e1; - if (e1.type.ty == Tsarray || - e1.type.ty == Taarray || - (e1.op == EXP.index && e1.type.ty != Tarray) || - e1.op == EXP.slice) - { - return e1.checkModifiable(sc, flag); - } - return Modifiable.yes; - - case EXP.question: - auto condExp = cast(CondExp)exp; - if (condExp.e1.checkModifiable(sc, flag) != Modifiable.no - && condExp.e2.checkModifiable(sc, flag) != Modifiable.no) - return Modifiable.yes; - return Modifiable.no; - - default: - return exp.type ? Modifiable.yes : Modifiable.no; // default modifiable - } -} - /** * Verify if the given identifier is _d_array{,set}ctor. * diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index f7f6b0b63ff..12ca6b4566c 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -50,7 +50,10 @@ struct Symbol; // back end symbol Expression *ctfeInterpret(Expression *e); void expandTuples(Expressions *exps, Identifiers *names = nullptr); StringExp *toUTF8(StringExp *se, Scope *sc); +Expression *resolveLoc(Expression *exp, const Loc &loc, Scope *sc); MATCH implicitConvTo(Expression *e, Type *t); +Expression *toLvalue(Expression *_this, Scope *sc); +Expression *modifiableLvalue(Expression* exp, Scope *sc); typedef unsigned char OwnedBy; enum @@ -99,9 +102,6 @@ public: virtual complex_t toComplex(); virtual StringExp *toStringExp(); virtual bool isLvalue(); - virtual Expression *toLvalue(Scope *sc, Expression *e); - virtual Expression *modifiableLvalue(Scope *sc, Expression *e); - virtual Expression *resolveLoc(const Loc &loc, Scope *sc); virtual bool checkType(); virtual bool checkValue(); Expression *addressOf(); @@ -244,7 +244,6 @@ public: real_t toImaginary() override; complex_t toComplex() override; Optional toBool() override; - Expression *toLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } dinteger_t getInteger() { return value; } template @@ -254,7 +253,6 @@ public: class ErrorExp final : public Expression { public: - Expression *toLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } static ErrorExp *errorexp; // handy shared value @@ -302,7 +300,6 @@ public: static IdentifierExp *create(const Loc &loc, Identifier *ident); bool isLvalue() override final; - Expression *toLvalue(Scope *sc, Expression *e) override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -320,7 +317,6 @@ public: DsymbolExp *syntaxCopy() override; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -331,8 +327,7 @@ public: ThisExp *syntaxCopy() override; Optional toBool() override; - bool isLvalue() override final; - Expression *toLvalue(Scope *sc, Expression *e) override final; + bool isLvalue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -370,8 +365,6 @@ public: StringExp *toStringExp() override; Optional toBool() override; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; - Expression *modifiableLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } size_t numberOfCodeUnits(int tynto = 0) const; void writeTo(void* dest, bool zero, int tyto = 0) const; @@ -469,7 +462,6 @@ public: static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL); bool equals(const RootObject * const o) const override; StructLiteralExp *syntaxCopy() override; - Expression *toLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -501,7 +493,6 @@ public: FuncDeclaration *fd; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; bool checkType() override; bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } @@ -575,8 +566,6 @@ public: static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true); bool equals(const RootObject * const o) const override; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; - Expression *modifiableLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -589,7 +578,6 @@ public: OverloadSet *vars; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -678,7 +666,6 @@ public: Expression *e1; UnaExp *syntaxCopy() override; - Expression *resolveLoc(const Loc &loc, Scope *sc) override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -701,8 +688,6 @@ class BinAssignExp : public BinExp { public: bool isLvalue() override final; - Expression *toLvalue(Scope *sc, Expression *ex) override final; - Expression *modifiableLvalue(Scope *sc, Expression *e) override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -767,8 +752,6 @@ public: d_bool hasOverloads; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; - Expression *modifiableLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -821,7 +804,6 @@ public: CallExp *syntaxCopy() override; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -836,8 +818,6 @@ class PtrExp final : public UnaExp { public: bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; - Expression *modifiableLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -882,7 +862,6 @@ public: CastExp *syntaxCopy() override; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -903,7 +882,6 @@ class VectorArrayExp final : public UnaExp { public: bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -926,8 +904,6 @@ private: public: SliceExp *syntaxCopy() override; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; - Expression *modifiableLvalue(Scope *sc, Expression *e) override; Optional toBool() override; void accept(Visitor *v) override { v->visit(this); } @@ -953,8 +929,6 @@ class DelegatePtrExp final : public UnaExp { public: bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; - Expression *modifiableLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -962,8 +936,6 @@ class DelegateFuncptrExp final : public UnaExp { public: bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; - Expression *modifiableLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -978,7 +950,6 @@ public: ArrayExp *syntaxCopy() override; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -997,8 +968,6 @@ public: d_bool isGenerated; d_bool allowCommaExp; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; - Expression *modifiableLvalue(Scope *sc, Expression *e) override; Optional toBool() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -1012,8 +981,6 @@ public: IndexExp *syntaxCopy() override; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; - Expression *modifiableLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -1047,7 +1014,6 @@ public: MemorySet memset; bool isLvalue() override final; - Expression *toLvalue(Scope *sc, Expression *ex) override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -1292,8 +1258,6 @@ public: CondExp *syntaxCopy() override; bool isLvalue() override; - Expression *toLvalue(Scope *sc, Expression *e) override; - Expression *modifiableLvalue(Scope *sc, Expression *e) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -1320,35 +1284,30 @@ public: class FileInitExp final : public DefaultInitExp { public: - Expression *resolveLoc(const Loc &loc, Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; class LineInitExp final : public DefaultInitExp { public: - Expression *resolveLoc(const Loc &loc, Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; class ModuleInitExp final : public DefaultInitExp { public: - Expression *resolveLoc(const Loc &loc, Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; class FuncInitExp final : public DefaultInitExp { public: - Expression *resolveLoc(const Loc &loc, Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; class PrettyFuncInitExp final : public DefaultInitExp { public: - Expression *resolveLoc(const Loc &loc, Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 1ddb2b1ea08..d55ab3bcb26 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -87,6 +87,46 @@ import dmd.visitor; enum LOGSEMANTIC = false; +/*********************************** + * Determine if a `this` is needed to access `d`. + * Params: + * sc = context + * d = declaration to check + * Returns: + * true means a `this` is needed + */ +private bool isNeedThisScope(Scope* sc, Declaration d) +{ + if (sc.intypeof == 1) + return false; + + AggregateDeclaration ad = d.isThis(); + if (!ad) + return false; + //printf("d = %s, ad = %s\n", d.toChars(), ad.toChars()); + + for (Dsymbol s = sc.parent; s; s = s.toParentLocal()) + { + //printf("\ts = %s %s, toParent2() = %p\n", s.kind(), s.toChars(), s.toParent2()); + if (AggregateDeclaration ad2 = s.isAggregateDeclaration()) + { + if (ad2 == ad) + return false; + else if (ad2.isNested()) + continue; + else + return true; + } + if (FuncDeclaration f = s.isFuncDeclaration()) + { + if (f.isMemberLocal()) + break; + } + } + return true; +} + + /******************************************************** * Perform semantic analysis and CTFE on expressions to produce * a string. @@ -196,6 +236,51 @@ FuncDeclaration hasThis(Scope* sc) } +extern (D) bool findTempDecl(DotTemplateInstanceExp exp, Scope* sc) +{ + auto ti = exp.ti; + auto e1 = exp.e1; + static if (LOGSEMANTIC) + { + printf("DotTemplateInstanceExp::findTempDecl('%s')\n", exp.toChars()); + } + if (ti.tempdecl) + return true; + + Expression e = new DotIdExp(exp.loc, e1, ti.name); + e = e.expressionSemantic(sc); + if (e.op == EXP.dot) + e = (cast(DotExp)e).e2; + + Dsymbol s = null; + switch (e.op) + { + case EXP.overloadSet: + s = (cast(OverExp)e).vars; + break; + + case EXP.dotTemplateDeclaration: + s = (cast(DotTemplateExp)e).td; + break; + + case EXP.scope_: + s = (cast(ScopeExp)e).sds; + break; + + case EXP.dotVariable: + s = (cast(DotVarExp)e).var; + break; + + case EXP.variable: + s = (cast(VarExp)e).var; + break; + + default: + return false; + } + return ti.updateTempDecl(sc, s); +} + /*********************************************************** * Resolve `exp` as a compile-time known string. * Params: @@ -1731,6 +1816,403 @@ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc) return false; } +/********************************************* + * Calling function f. + * Check the purity, i.e. if we're in a pure function + * we can only call other pure functions. + * Returns true if error occurs. + */ +private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) +{ + if (!sc.func) + return false; + if (sc.func == f) + return false; + if (sc.intypeof == 1) + return false; + if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + return false; + + // If the call has a pure parent, then the called func must be pure. + if (!f.isPure() && checkImpure(sc, loc, null, f)) + { + error(loc, "`pure` %s `%s` cannot call impure %s `%s`", + sc.func.kind(), sc.func.toPrettyChars(), f.kind(), + f.toPrettyChars()); + + if (!f.isDtorDeclaration()) + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_); + + f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure"); + return true; + } + return false; +} + +/** + * Checks whether `f` is a generated `DtorDeclaration` that hides a user-defined one + * which passes `check` while `f` doesn't (e.g. when the user defined dtor is pure but + * the generated dtor is not). + * In that case the method will identify and print all members causing the attribute + * missmatch. + * + * Params: + * f = potential `DtorDeclaration` + * sc = scope + * loc = location + * check = current check (e.g. whether it's pure) + * checkName = the kind of check (e.g. `"pure"`) + */ +void checkOverriddenDtor(FuncDeclaration f, Scope* sc, const ref Loc loc, + scope bool function(DtorDeclaration) check, const string checkName) +{ + auto dd = f.isDtorDeclaration(); + if (!dd || !dd.isGenerated()) + return; + + // DtorDeclaration without parents should fail at an earlier stage + auto ad = cast(AggregateDeclaration) f.toParent2(); + assert(ad); + + if (ad.userDtors.length) + { + if (!check(ad.userDtors[0])) // doesn't match check (e.g. is impure as well) + return; + + // Sanity check + assert(!check(ad.fieldDtor)); + } + + dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:", + dd.isGenerated() ? "generated " : "".ptr, + ad.toChars, + cast(int) checkName.length, checkName.ptr); + + // Search for the offending fields + foreach (field; ad.fields) + { + // Only structs may define automatically called destructors + auto ts = field.type.isTypeStruct(); + if (!ts) + { + // But they might be part of a static array + auto ta = field.type.isTypeSArray(); + if (!ta) + continue; + + ts = ta.baseElemOf().isTypeStruct(); + if (!ts) + continue; + } + + auto fieldSym = ts.toDsymbol(sc); + assert(fieldSym); // Resolving ts must succeed because missing defs. should error before + + auto fieldSd = fieldSym.isStructDeclaration(); + assert(fieldSd); // ts is a TypeStruct, this would imply a malformed ASR + + if (fieldSd.dtor && !check(fieldSd.dtor)) + { + field.loc.errorSupplemental(" - %s %s", field.type.toChars(), field.toChars()); + + if (fieldSd.dtor.isGenerated()) + fieldSd.dtor.checkOverriddenDtor(sc, loc, check, checkName); + else + fieldSd.dtor.loc.errorSupplemental(" %.*s `%s.~this` is declared here", + cast(int) checkName.length, checkName.ptr, fieldSd.toChars()); + } + } +} + +/******************************************* + * Accessing variable v. + * Check for purity and safety violations. + * Returns true if error occurs. + */ +private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) +{ + //printf("v = %s %s\n", v.type.toChars(), v.toChars()); + /* Look for purity and safety violations when accessing variable v + * from current function. + */ + if (!sc.func) + return false; + if (sc.intypeof == 1) + return false; // allow violations inside typeof(expression) + if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + return false; // allow violations inside compile-time evaluated expressions and debug conditionals + if (v.ident == Id.ctfe) + return false; // magic variable never violates pure and safe + if (v.isImmutable()) + return false; // always safe and pure to access immutables... + if (v.isConst() && !v.isReference() && (v.isDataseg() || v.isParameter()) && v.type.implicitConvTo(v.type.immutableOf())) + return false; // or const global/parameter values which have no mutable indirections + if (v.storage_class & STC.manifest) + return false; // ...or manifest constants + + // accessing empty structs is pure + // https://issues.dlang.org/show_bug.cgi?id=18694 + // https://issues.dlang.org/show_bug.cgi?id=21464 + // https://issues.dlang.org/show_bug.cgi?id=23589 + if (v.type.ty == Tstruct) + { + StructDeclaration sd = (cast(TypeStruct)v.type).sym; + if (sd.members) // not opaque + { + if (sd.semanticRun >= PASS.semanticdone) + sd.determineSize(v.loc); + if (sd.hasNoFields) + return false; + } + } + + bool err = false; + if (v.isDataseg()) + { + // https://issues.dlang.org/show_bug.cgi?id=7533 + // Accessing implicit generated __gate is pure. + if (v.ident == Id.gate) + return false; + + if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v)) + { + error(loc, "`pure` %s `%s` cannot access mutable static data `%s`", + sc.func.kind(), sc.func.toPrettyChars(), v.toChars()); + err = true; + } + } + else + { + /* Given: + * void f() { + * int fx; + * pure void g() { + * int gx; + * /+pure+/ void h() { + * int hx; + * /+pure+/ void i() { } + * } + * } + * } + * i() can modify hx and gx but not fx + */ + + Dsymbol vparent = v.toParent2(); + for (Dsymbol s = sc.func; !err && s; s = s.toParentP(vparent)) + { + if (s == vparent) + break; + + if (AggregateDeclaration ad = s.isAggregateDeclaration()) + { + if (ad.isNested()) + continue; + break; + } + FuncDeclaration ff = s.isFuncDeclaration(); + if (!ff) + break; + if (ff.isNested() || ff.isThis()) + { + if (ff.type.isImmutable() || + ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod)) + { + OutBuffer ffbuf; + OutBuffer vbuf; + MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod); + MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod); + error(loc, "%s%s `%s` cannot access %sdata `%s`", + ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars()); + err = true; + break; + } + continue; + } + break; + } + } + + /* Do not allow safe functions to access __gshared data + */ + if (v.storage_class & STC.gshared) + { + if (sc.setUnsafe(false, loc, + "`@safe` function `%s` cannot access `__gshared` data `%s`", sc.func, v)) + { + err = true; + } + } + + return err; +} + +/* +Check if sc.func is impure or can be made impure. +Returns true on error, i.e. if sc.func is pure and cannot be made impure. +*/ +private bool checkImpure(Scope* sc, Loc loc, const(char)* fmt, RootObject arg0) +{ + return sc.func && (isRootTraitsCompilesScope(sc) + ? sc.func.isPureBypassingInference() >= PURE.weak + : sc.func.setImpure(loc, fmt, arg0)); +} + +/********************************************* + * Calling function f. + * Check the safety, i.e. if we're in a @safe function + * we can only call @safe or @trusted functions. + * Returns true if error occurs. + */ +private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc) +{ + if (sc.func == f) + return false; + if (sc.intypeof == 1) + return false; + if (sc.flags & SCOPE.debug_) + return false; + if ((sc.flags & SCOPE.ctfe) && sc.func) + return false; + + if (!sc.func) + { + if (sc.varDecl && !f.safetyInprocess && !f.isSafe() && !f.isTrusted()) + { + if (sc.varDecl.storage_class & STC.safe) + { + error(loc, "`@safe` variable `%s` cannot be initialized by calling `@system` function `%s`", + sc.varDecl.toChars(), f.toChars()); + return true; + } + else + { + sc.varDecl.storage_class |= STC.system; + sc.varDecl.systemInferred = true; + } + } + return false; + } + + if (!f.isSafe() && !f.isTrusted()) + { + if (isRootTraitsCompilesScope(sc) ? sc.func.isSafeBypassingInference() : sc.func.setUnsafeCall(f)) + { + if (!loc.isValid()) // e.g. implicitly generated dtor + loc = sc.func.loc; + + const prettyChars = f.toPrettyChars(); + error(loc, "`@safe` %s `%s` cannot call `@system` %s `%s`", + sc.func.kind(), sc.func.toPrettyChars(), f.kind(), + prettyChars); + if (!f.isDtorDeclaration) + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe); + .errorSupplemental(f.loc, "`%s` is declared here", prettyChars); + + f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system"); + + return true; + } + } + else if (f.isSafe() && f.safetyViolation) + { + // for dip1000 by default transition, print deprecations for calling functions that will become `@system` + if (sc.func.isSafeBypassingInference()) + { + .deprecation(loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars()); + errorSupplementalInferredAttr(f, 10, true, STC.safe); + } + else if (!sc.func.safetyViolation) + { + import dmd.func : AttributeViolation; + sc.func.safetyViolation = new AttributeViolation(loc, null, f, null, null); + } + } + return false; +} + +/********************************************* + * Calling function f. + * Check the @nogc-ness, i.e. if we're in a @nogc function + * we can only call other @nogc functions. + * Returns true if error occurs. + */ +private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) +{ + if (!sc.func) + return false; + if (sc.func == f) + return false; + if (sc.intypeof == 1) + return false; + if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + return false; + /* The original expressions (`new S(...)` or `new S[...]``) will be + * verified instead. This is to keep errors related to the original code + * and not the lowering. + */ + if (f.ident == Id._d_newitemT || f.ident == Id._d_newarrayT) + return false; + + if (!f.isNogc()) + { + if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f)) + { + if (loc.linnum == 0) // e.g. implicitly generated dtor + loc = sc.func.loc; + + // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)), + // so don't print anything to avoid double error messages. + if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT + || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX + || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) + { + error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", + sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); + + if (!f.isDtorDeclaration) + f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); + } + + f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isnogc, "non-@nogc"); + + return true; + } + } + return false; +} + +/******************************************** + * Check that the postblit is callable if t is an array of structs. + * Returns true if error happens. + */ +private bool checkPostblit(Type t, ref Loc loc, Scope* sc) +{ + if (auto ts = t.baseElemOf().isTypeStruct()) + { + if (global.params.useTypeInfo && Type.dtypeinfo) + { + // https://issues.dlang.org/show_bug.cgi?id=11395 + // Require TypeInfo generation for array concatenation + semanticTypeInfo(sc, t); + } + + StructDeclaration sd = ts.sym; + if (sd.postblit) + { + if (sd.postblit.checkDisabled(loc, sc)) + return true; + + //checkDeprecated(sc, sd.postblit); // necessary? + sd.postblit.checkPurity(loc, sc); + sd.postblit.checkSafety(loc, sc); + sd.postblit.checkNogc(loc, sc); + //checkAccess(sd, loc, sc, sd.postblit); // necessary? + return false; + } + } + return false; +} + /*************************************** * Pull out any properties. */ @@ -1942,7 +2424,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = { if (auto v = ve.var.isVarDeclaration()) { - if (ve.checkPurity(sc, v)) + if (v.checkPurity(ve.loc, sc)) return ErrorExp.get(); } } @@ -2647,7 +3129,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v)); arg = ev.expressionSemantic(sc); } - arg = arg.toLvalue(sc, arg); + arg = arg.toLvalue(sc); // Look for mutable misaligned pointer, etc., in @safe mode err |= checkUnsafeAccess(sc, arg, false, true); @@ -2665,7 +3147,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v)); arg = ev.expressionSemantic(sc); } - arg = arg.toLvalue(sc, arg); + arg = arg.toLvalue(sc); // Look for mutable misaligned pointer, etc., in @safe mode err |= checkUnsafeAccess(sc, arg, false, true); @@ -2684,7 +3166,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, err |= checkUnsafeAccess(sc, arg, false, true); err |= checkDefCtor(arg.loc, t); // t must be default constructible } - arg = arg.toLvalue(sc, arg); + arg = arg.toLvalue(sc); } else if (p.isLazy()) { @@ -4852,6 +5334,52 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; } + private void genIdent(FuncExp exp, Scope* sc) + { + if (exp.fd.ident == Id.empty) + { + const(char)[] s; + if (exp.fd.fes) + s = "__foreachbody"; + else if (exp.fd.tok == TOK.reserved) + s = "__lambda"; + else if (exp.fd.tok == TOK.delegate_) + s = "__dgliteral"; + else + s = "__funcliteral"; + + DsymbolTable symtab; + if (FuncDeclaration func = sc.parent.isFuncDeclaration()) + { + if (func.localsymtab is null) + { + // Inside template constraint, symtab is not set yet. + // Initialize it lazily. + func.localsymtab = new DsymbolTable(); + } + symtab = func.localsymtab; + } + else + { + ScopeDsymbol sds = sc.parent.isScopeDsymbol(); + if (!sds.symtab) + { + // Inside template constraint, symtab may not be set yet. + // Initialize it lazily. + assert(sds.isTemplateInstance()); + sds.symtab = new DsymbolTable(); + } + symtab = sds.symtab; + } + assert(symtab); + Identifier id = Identifier.generateId(s, symtab.length() + 1); + exp.fd.ident = id; + if (exp.td) + exp.td.ident = id; + symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd); + } + } + override void visit(FuncExp exp) { static if (LOGSEMANTIC) @@ -4882,7 +5410,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor //if (fd.treq) // fd.treq = fd.treq.dsymbolSemantic(loc, sc); - exp.genIdent(sc); + genIdent(exp, sc); // Set target of return type inference if (exp.fd.treq && !exp.fd.type.nextOf()) @@ -4995,7 +5523,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return checkarg; } - exp.genIdent(sc); + genIdent(exp, sc); assert(exp.td.parameters && exp.td.parameters.length); exp.td.dsymbolSemantic(sc); @@ -5257,7 +5785,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor ve.type = t.typeSemantic(exp.loc, sc); } VarDeclaration v = ve.var.isVarDeclaration(); - if (v && ve.checkPurity(sc, v)) + if (v && v.checkPurity(ve.loc, sc)) return setError(); } @@ -5885,9 +6413,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Purity and safety check should run after testing arguments matching if (exp.f) { - exp.checkPurity(sc, exp.f); - exp.checkSafety(sc, exp.f); - exp.checkNogc(sc, exp.f); + exp.f.checkPurity(exp.loc, sc); + exp.f.checkSafety(exp.loc, sc); + exp.f.checkNogc(exp.loc, sc); if (exp.f.checkNestedReference(sc, exp.loc)) return setError(); } @@ -6795,7 +7323,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } exp.e1 = exp.e1.expressionSemantic(sc); - exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1); + exp.e1 = exp.e1.modifiableLvalue(sc); exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true); exp.type = exp.e1.type; @@ -6873,7 +7401,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor uint errors = global.errors; const len = buf.length; const str = buf.extractChars()[0 .. len]; - const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + const bool doUnittests = global.params.parsingUnittestsRequired(); auto loc = adjustLocForMixin(str, exp.loc, global.params.mixinOut); scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.v.vin; @@ -7767,7 +8295,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else { // `toLvalue` call further below is upon exp.e1, omitting & from the error message - exp.toLvalue(sc, null); + exp.toLvalue(sc); return setError(); } } @@ -7857,7 +8385,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - exp.e1 = exp.e1.toLvalue(sc, null); + exp.e1 = exp.e1.toLvalue(sc); if (exp.e1.op == EXP.error) { result = exp.e1; @@ -7934,7 +8462,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!checkAddressVar(sc, exp.e1, v)) return setError(); - ve.checkPurity(sc, v); + v.checkPurity(ve.loc, sc); } FuncDeclaration f = ve.var.isFuncDeclaration(); if (f) @@ -8006,7 +8534,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (VarDeclaration v = expToVariable(exp.e1)) { - exp.e1.checkPurity(sc, v); + v.checkPurity(exp.e1.loc, sc); } } else if (wasCond) @@ -8275,7 +8803,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } exp.e1 = resolveProperties(sc, exp.e1); - exp.e1 = exp.e1.modifiableLvalue(sc, null); + exp.e1 = exp.e1.modifiableLvalue(sc); if (exp.e1.op == EXP.error) { result = exp.e1; @@ -8306,9 +8834,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (cd.dtor) { err |= !cd.dtor.functionSemantic(); - err |= exp.checkPurity(sc, cd.dtor); - err |= exp.checkSafety(sc, cd.dtor); - err |= exp.checkNogc(sc, cd.dtor); + err |= cd.dtor.checkPurity(exp.loc, sc); + err |= cd.dtor.checkSafety(exp.loc, sc); + err |= cd.dtor.checkNogc(exp.loc, sc); } if (err) return setError(); @@ -9491,7 +10019,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1); + exp.e1 = exp.e1.modifiableLvalue(sc); exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true); e = exp; @@ -10253,7 +10781,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression ex; ex = new IndexExp(exp.loc, ea, ek); ex = ex.expressionSemantic(sc); - ex = ex.modifiableLvalue(sc, ex); // allocate new slot + ex = ex.modifiableLvalue(sc); // allocate new slot ex = ex.optimize(WANTvalue); ey = new ConstructExp(exp.loc, ex, ey); @@ -10344,7 +10872,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (exp.op != EXP.blit && (e2x.op == EXP.slice && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op == EXP.cast_ && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op != EXP.slice && e2x.isLvalue())) { - if (e1x.checkPostblit(sc, t1)) + if (t1.checkPostblit(e1x.loc, sc)) return setError(); } @@ -10446,7 +10974,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { // e1 is not an lvalue, but we let code generator handle it - auto ale1x = ale.e1.modifiableLvalue(sc, exp.e1); + auto ale1x = ale.e1.modifiableLvalueImpl(sc, exp.e1); if (ale1x.op == EXP.error) return setResult(ale1x); ale.e1 = ale1x; @@ -10532,7 +11060,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor se = cast(SliceExp)se.e1; if (se.e1.op == EXP.question && se.e1.type.toBasetype().ty == Tsarray) { - se.e1 = se.e1.modifiableLvalue(sc, exp.e1); + se.e1 = se.e1.modifiableLvalueImpl(sc, exp.e1); if (se.e1.op == EXP.error) return setResult(se.e1); } @@ -10556,7 +11084,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Try to do a decent error message with the expression // before it gets constant folded if (exp.op == EXP.assign) - e1x = e1x.modifiableLvalue(sc, e1old); + e1x = e1x.modifiableLvalueImpl(sc, e1old); e1x = e1x.optimize(WANTvalue, /*keepLvalue*/ true); @@ -10587,7 +11115,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // '= null' is the only allowable block assignment (Bug 7493) exp.memset = MemorySet.blockAssign; // make it easy for back end to tell what this is e2x = e2x.implicitCastTo(sc, t1.nextOf()); - if (exp.op != EXP.blit && e2x.isLvalue() && exp.e1.checkPostblit(sc, t1.nextOf())) + if (exp.op != EXP.blit && e2x.isLvalue() && t1.nextOf.checkPostblit(exp.e1.loc, sc)) return setError(); } else if (exp.e1.op == EXP.slice && @@ -10625,7 +11153,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e2x.op == EXP.cast_ && (cast(UnaExp)e2x).e1.isLvalue() || e2x.op != EXP.slice && e2x.isLvalue())) { - if (exp.e1.checkPostblit(sc, t1.nextOf())) + if (t1.nextOf().checkPostblit(exp.e1.loc, sc)) return setError(); } @@ -11004,7 +11532,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1); + exp.e1 = exp.e1.modifiableLvalue(sc); } if ((exp.e1.type.isintegral() || exp.e1.type.isfloating()) && (exp.e2.type.isintegral() || exp.e2.type.isfloating())) @@ -11063,7 +11591,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - exp.e1 = exp.e1.modifiableLvalue(sc, exp.e1); + exp.e1 = exp.e1.modifiableLvalue(sc); if (exp.e1.op == EXP.error) { result = exp.e1; @@ -11095,7 +11623,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { // EXP.concatenateAssign assert(exp.op == EXP.concatenateAssign); - if (exp.e1.checkPostblit(sc, tb1next)) + if (tb1next.checkPostblit(exp.e1.loc, sc)) return setError(); exp.e2 = exp.e2.castTo(sc, exp.e1.type); @@ -11113,7 +11641,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (tb2.ty == Tclass && (cast(TypeClass)tb2).implicitConvToThroughAliasThis(tb1next)) goto Laliasthis; // Append element - if (exp.e2.checkPostblit(sc, tb2)) + if (tb2.checkPostblit(exp.e2.loc, sc)) return setError(); if (checkNewEscape(sc, exp.e2, false)) @@ -11746,7 +12274,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - if (exp.e2.checkPostblit(sc, tb2)) + if (tb2.checkPostblit(exp.e2.loc, sc)) return setError(); // Postblit call will be done in runtime helper function } @@ -11781,7 +12309,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - if (exp.e1.checkPostblit(sc, tb1)) + if (tb1.checkPostblit(exp.e1.loc, sc)) return setError(); } @@ -11836,7 +12364,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } if (Type tbn = tb.nextOf()) { - if (exp.checkPostblit(sc, tbn)) + if (tbn.checkPostblit(exp.loc, sc)) return setError(); } Type t1 = exp.e1.type.toBasetype(); @@ -13582,6 +14110,14 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) return exp; } +private bool checkDisabled(Dsymbol s, ref Loc loc, Scope* sc) +{ + if (auto d = s.isDeclaration()) + return d.checkDisabled(loc, sc); + + return false; +} + /****************************** * Resolve properties, i.e. `e1.ident`, without seeing UFCS. * Params: @@ -13675,8 +14211,8 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) // if 's' is a tuple variable, the tuple is returned. s = s.toAlias(); - exp.checkDeprecated(sc, s); - exp.checkDisabled(sc, s); + s.checkDeprecated(exp.loc, sc); + s.checkDisabled(exp.loc, sc); if (auto em = s.isEnumMember()) { @@ -14466,12 +15002,112 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) return check(e, returnRef); } -/************************************************ - * Destructors are attached to VarDeclarations. - * Hence, if expression returns a temp that needs a destructor, - * make sure and create a VarDeclaration for that temp. +/**************************************** + * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc. */ -Expression addDtorHook(Expression e, Scope* sc) +Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) +{ + Expression visit(Expression exp) + { + if (auto unaExp = exp.isUnaExp()) + { + unaExp.e1 = unaExp.e1.resolveLoc(loc, sc); + return unaExp; + } + exp.loc = loc; + return exp; + } + + Expression visitCat(CatExp exp) + { + exp.e1 = exp.e1.resolveLoc(loc, sc); + exp.e2 = exp.e2.resolveLoc(loc, sc); + return exp; + } + + Expression visitFileInit(FileInitExp exp) + { + //printf("FileInitExp::resolve() %s\n", exp.toChars()); + const(char)* s; + if (exp.op == EXP.fileFullPath) + s = FileName.toAbsolute(loc.isValid() ? loc.filename : sc._module.srcfile.toChars()); + else + s = loc.isValid() ? loc.filename : sc._module.ident.toChars(); + + Expression e = new StringExp(loc, s.toDString()); + return e.expressionSemantic(sc); + } + + Expression visitLineInit(LineInitExp _) + { + Expression e = new IntegerExp(loc, loc.linnum, Type.tint32); + return e.expressionSemantic(sc); + } + + Expression visitModuleInit(ModuleInitExp _) + { + const auto s = (sc.callsc ? sc.callsc : sc)._module.toPrettyChars().toDString(); + Expression e = new StringExp(loc, s); + return e.expressionSemantic(sc); + } + + Expression visitFuncInit(FuncInitExp _) + { + const(char)* s; + if (sc.callsc && sc.callsc.func) + s = sc.callsc.func.Dsymbol.toPrettyChars(); + else if (sc.func) + s = sc.func.Dsymbol.toPrettyChars(); + else + s = ""; + Expression e = new StringExp(loc, s.toDString()); + return e.expressionSemantic(sc); + } + + Expression visitPrettyFunc(PrettyFuncInitExp _) + { + FuncDeclaration fd = (sc.callsc && sc.callsc.func) + ? sc.callsc.func + : sc.func; + + const(char)* s; + if (fd) + { + const funcStr = fd.Dsymbol.toPrettyChars(); + OutBuffer buf; + functionToBufferWithIdent(fd.type.isTypeFunction(), buf, funcStr, fd.isStatic); + s = buf.extractChars(); + } + else + { + s = ""; + } + + Expression e = new StringExp(loc, s.toDString()); + e = e.expressionSemantic(sc); + e.type = Type.tstring; + return e; + } + + switch(exp.op) + { + default: return visit(exp); + case EXP.concatenate: return visitCat(exp.isCatExp()); + case EXP.file: + case EXP.fileFullPath: return visitFileInit(exp.isFileInitExp()); + case EXP.line: return visitLineInit(exp.isLineInitExp); + case EXP.moduleString: return visitModuleInit(exp.isModuleInitExp()); + case EXP.functionString: return visitFuncInit(exp.isFuncInitExp()); + case EXP.prettyFunction: return visitPrettyFunc(exp.isPrettyFuncInitExp()); + } +} + +/************************************************ + * Destructors are attached to VarDeclarations. + * Hence, if expression returns a temp that needs a destructor, + * make sure and create a VarDeclaration for that temp. + */ +Expression addDtorHook(Expression e, Scope* sc) { Expression visit(Expression exp) { @@ -14567,6 +15203,584 @@ Expression addDtorHook(Expression e, Scope* sc) } } +/******************************* + * Try to convert an expression to be an lvalue. + * + * Give error if we're not an lvalue. + * Params: + * _this = expression to convert + * sc = scope + * Returns: converted expression, or `ErrorExp` on error +*/ +extern(C++) Expression toLvalue(Expression _this, Scope* sc) +{ + return toLvalueImpl(_this, sc, _this); +} + +// e = original un-lowered expression for error messages, in case of recursive calls +private Expression toLvalueImpl(Expression _this, Scope* sc, Expression e) { + Expression visit(Expression _this) + { + // BinaryAssignExp does not have an EXP associated + // so it's treated on the default path. + // Lvalue-ness will be handled in glue :layer. + if (_this.isBinAssignExp()) + return _this; + if (!_this.loc.isValid()) + _this.loc = e.loc; + + if (e.op == EXP.type) + error(_this.loc, "`%s` is a `%s` definition and cannot be modified", e.type.toChars(), e.type.kind()); + else + error(_this.loc, "`%s` is not an lvalue and cannot be modified", e.toChars()); + + return ErrorExp.get(); + } + + Expression visitInteger(IntegerExp _this) + { + if (!_this.loc.isValid()) + _this.loc = e.loc; + error(e.loc, "cannot modify constant `%s`", e.toChars()); + return ErrorExp.get(); + } + + Expression visitThis(ThisExp _this) + { + if (_this.type.toBasetype().ty == Tclass) + { + // Class `this` is an rvalue; struct `this` is an lvalue. + return visit(_this); + } + + return _this; + } + + Expression visitString(StringExp _this) + { + //printf("StringExp::toLvalue(%s) type = %s\n", _this.toChars(), _this.type ? _this.type.toChars() : NULL); + return (_this.type && _this.type.toBasetype().ty == Tsarray) ? _this : visit(_this); + } + + Expression visitStructLiteral(StructLiteralExp _this) + { + if (sc.flags & SCOPE.Cfile) + return _this; // C struct literals are lvalues + else + return visit(_this); + } + + Expression visitTemplate(TemplateExp _this) + { + if (!_this.fd) + return visit(_this); + + assert(sc); + return symbolToExp(_this.fd, _this.loc, sc, true); + + } + + Expression visitVar(VarExp _this) + { + auto var = _this.var; + if (var.storage_class & STC.manifest) + { + error(_this.loc, "manifest constant `%s` cannot be modified", var.toChars()); + return ErrorExp.get(); + } + if (var.storage_class & STC.lazy_ && !_this.delegateWasExtracted) + { + error(_this.loc, "lazy variable `%s` cannot be modified", var.toChars()); + return ErrorExp.get(); + } + if (var.ident == Id.ctfe) + { + error(_this.loc, "cannot modify compiler-generated variable `__ctfe`"); + return ErrorExp.get(); + } + if (var.ident == Id.dollar) // https://issues.dlang.org/show_bug.cgi?id=13574 + { + error(_this.loc, "cannot modify operator `$`"); + return ErrorExp.get(); + } + return _this; + } + + Expression visitDotVar(DotVarExp _this) + { + auto e1 = _this.e1; + auto var = _this.var; + //printf("DotVarExp::toLvalue(%s)\n", toChars()); + if (sc && sc.flags & SCOPE.Cfile) + { + /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator + * is an lvalue if the first expression is an lvalue. + */ + if (!e1.isLvalue()) + return visit(_this); + } + if (!_this.isLvalue()) + return visit(_this); + if (e1.op == EXP.this_ && sc.ctorflow.fieldinit.length && !(sc.ctorflow.callSuper & CSX.any_ctor)) + { + if (VarDeclaration vd = var.isVarDeclaration()) + { + auto ad = vd.isMember2(); + if (ad && ad.fields.length == sc.ctorflow.fieldinit.length) + { + foreach (i, f; ad.fields) + { + if (f == vd) + { + if (!(sc.ctorflow.fieldinit[i].csx & CSX.this_ctor)) + { + /* If the address of vd is taken, assume it is thereby initialized + * https://issues.dlang.org/show_bug.cgi?id=15869 + */ + modifyFieldVar(_this.loc, sc, vd, e1); + } + break; + } + } + } + } + } + return _this; + } + + Expression visitCall(CallExp _this) + { + if (_this.isLvalue()) + return _this; + return visit(_this); + } + + Expression visitCast(CastExp _this) + { + if (sc && sc.flags & SCOPE.Cfile) + { + /* C11 6.5.4-5: A cast does not yield an lvalue. + */ + return visit(_this); + } + if (_this.isLvalue()) + return _this; + return visit(_this); + } + + Expression visitVectorArray(VectorArrayExp _this) + { + _this.e1 = _this.e1.toLvalueImpl(sc, e); + return _this; + } + + Expression visitSlice(SliceExp _this) + { + //printf("SliceExp::toLvalue(%s) _this.type = %s\n", _this.toChars(), _this.type ? _this.type.toChars() : NULL); + return (_this.type && _this.type.toBasetype().ty == Tsarray) ? _this : visit(_this); + } + + Expression visitArray(ArrayExp _this) + { + if (_this.type && _this.type.toBasetype().ty == Tvoid) + error(_this.loc, "`void`s have no value"); + return _this; + } + + Expression visitComma(CommaExp _this) + { + _this.e2 = _this.e2.toLvalue(sc); + return _this; + } + + Expression visitDelegatePointer(DelegatePtrExp _this) + { + _this.e1 = _this.e1.toLvalueImpl(sc, e); + return _this; + } + + Expression visitDelegateFuncptr(DelegateFuncptrExp _this) + { + _this.e1 = _this.e1.toLvalueImpl(sc, e); + return _this; + } + + Expression visitIndex(IndexExp _this) + { + if (_this.isLvalue()) + return _this; + return visit(_this); + } + + Expression visitAssign(AssignExp _this) + { + if (_this.e1.op == EXP.slice || _this.e1.op == EXP.arrayLength) + { + return visit(_this); + } + + /* In front-end level, AssignExp should make an lvalue of e1. + * Taking the address of e1 will be handled in low level layer, + * so this function does nothing. + */ + return _this; + } + + Expression visitCond(CondExp _this) + { + // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2) + CondExp e = cast(CondExp)(_this.copy()); + e.e1 = _this.e1.toLvalue(sc).addressOf(); + e.e2 = _this.e2.toLvalue(sc).addressOf(); + e.type = _this.type.pointerTo(); + return new PtrExp(_this.loc, e, _this.type); + + } + + switch(_this.op) + { + default: return visit(_this); + + case EXP.int64: return visitInteger(_this.isIntegerExp()); + case EXP.error: return _this; + case EXP.identifier: return _this; + case EXP.dSymbol: return _this; + case EXP.this_: return visitThis(_this.isThisExp()); + case EXP.super_: return visitThis(_this.isSuperExp()); + case EXP.string_: return visitString(_this.isStringExp()); + case EXP.structLiteral: return visitStructLiteral(_this.isStructLiteralExp()); + case EXP.template_: return visitTemplate(_this.isTemplateExp()); + case EXP.variable: return visitVar(_this.isVarExp()); + case EXP.overloadSet: return _this; + case EXP.dotVariable: return visitDotVar(_this.isDotVarExp()); + case EXP.call: return visitCall(_this.isCallExp()); + case EXP.star: return _this; + case EXP.cast_: return visitCast(_this.isCastExp()); + case EXP.vectorArray: return visitVectorArray(_this.isVectorArrayExp()); + case EXP.slice: return visitSlice(_this.isSliceExp()); + case EXP.array: return visitArray(_this.isArrayExp()); + case EXP.comma: return visitComma(_this.isCommaExp()); + case EXP.delegatePointer: return visitDelegatePointer(_this.isDelegatePtrExp()); + case EXP.delegateFunctionPointer: return visitDelegateFuncptr(_this.isDelegateFuncptrExp()); + case EXP.index: return visitIndex(_this.isIndexExp()); + case EXP.construct: return visitAssign(_this.isConstructExp()); + case EXP.loweredAssignExp: return visitAssign(_this.isLoweredAssignExp()); + case EXP.blit: return visitAssign(_this.isBlitExp()); + case EXP.assign: return visitAssign(_this.isAssignExp()); + case EXP.question: return visitCond(_this.isCondExp()); + } +} + +/*************************************** + * Parameters: + * sc: scope + * flag: 1: do not issue error message for invalid modification + 2: the exp is a DotVarExp and a subfield of the leftmost + variable is modified + * Returns: + * Whether the type is modifiable + */ +Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag = ModifyFlags.none) +{ + switch(exp.op) + { + case EXP.variable: + auto varExp = cast(VarExp)exp; + + //printf("VarExp::checkModifiable %s", varExp.toChars()); + assert(varExp.type); + return varExp.var.checkModify(varExp.loc, sc, null, flag); + + case EXP.dotVariable: + auto dotVarExp = cast(DotVarExp)exp; + + //printf("DotVarExp::checkModifiable %s %s\n", dotVarExp.toChars(), dotVarExp.type.toChars()); + if (dotVarExp.e1.op == EXP.this_) + return dotVarExp.var.checkModify(dotVarExp.loc, sc, dotVarExp.e1, flag); + + /* https://issues.dlang.org/show_bug.cgi?id=12764 + * If inside a constructor and an expression of type `this.field.var` + * is encountered, where `field` is a struct declaration with + * default construction disabled, we must make sure that + * assigning to `var` does not imply that `field` was initialized + */ + if (sc.func && sc.func.isCtorDeclaration()) + { + // if inside a constructor scope and e1 of this DotVarExp + // is another DotVarExp, then check if the leftmost expression is a `this` identifier + if (auto dve = dotVarExp.e1.isDotVarExp()) + { + // Iterate the chain of DotVarExp to find `this` + // Keep track whether access to fields was limited to union members + // s.t. one can initialize an entire struct inside nested unions + // (but not its members) + bool onlyUnion = true; + while (true) + { + auto v = dve.var.isVarDeclaration(); + assert(v); + + // Accessing union member? + auto t = v.type.isTypeStruct(); + if (!t || !t.sym.isUnionDeclaration()) + onlyUnion = false; + + // Another DotVarExp left? + if (!dve.e1 || dve.e1.op != EXP.dotVariable) + break; + + dve = cast(DotVarExp) dve.e1; + } + + if (dve.e1.op == EXP.this_) + { + scope v = dve.var.isVarDeclaration(); + /* if v is a struct member field with no initializer, no default construction + * and v wasn't intialized before + */ + if (v && v.isField() && !v._init && !v.ctorinit) + { + if (auto ts = v.type.isTypeStruct()) + { + if (ts.sym.noDefaultCtor) + { + /* checkModify will consider that this is an initialization + * of v while it is actually an assignment of a field of v + */ + scope modifyLevel = v.checkModify(dotVarExp.loc, sc, dve.e1, !onlyUnion ? (flag | ModifyFlags.fieldAssign) : flag); + if (modifyLevel == Modifiable.initialization) + { + // https://issues.dlang.org/show_bug.cgi?id=22118 + // v is a union type field that was assigned + // a variable, therefore it counts as initialization + if (v.ctorinit) + return Modifiable.initialization; + + return Modifiable.yes; + } + return modifyLevel; + } + } + } + } + } + } + + //printf("\te1 = %s\n", e1.toChars()); + return dotVarExp.e1.checkModifiable(sc, flag); + + case EXP.star: + auto ptrExp = cast(PtrExp)exp; + if (auto se = ptrExp.e1.isSymOffExp()) + { + return se.var.checkModify(ptrExp.loc, sc, null, flag); + } + else if (auto ae = ptrExp.e1.isAddrExp()) + { + return ae.e1.checkModifiable(sc, flag); + } + return Modifiable.yes; + + case EXP.slice: + auto sliceExp = cast(SliceExp)exp; + + //printf("SliceExp::checkModifiable %s\n", sliceExp.toChars()); + auto e1 = sliceExp.e1; + if (e1.type.ty == Tsarray || (e1.op == EXP.index && e1.type.ty != Tarray) || e1.op == EXP.slice) + { + return e1.checkModifiable(sc, flag); + } + return Modifiable.yes; + + case EXP.comma: + return (cast(CommaExp)exp).e2.checkModifiable(sc, flag); + + case EXP.index: + auto indexExp = cast(IndexExp)exp; + auto e1 = indexExp.e1; + if (e1.type.ty == Tsarray || + e1.type.ty == Taarray || + (e1.op == EXP.index && e1.type.ty != Tarray) || + e1.op == EXP.slice) + { + return e1.checkModifiable(sc, flag); + } + return Modifiable.yes; + + case EXP.question: + auto condExp = cast(CondExp)exp; + if (condExp.e1.checkModifiable(sc, flag) != Modifiable.no + && condExp.e2.checkModifiable(sc, flag) != Modifiable.no) + return Modifiable.yes; + return Modifiable.no; + + default: + return exp.type ? Modifiable.yes : Modifiable.no; // default modifiable + } +} + +/** + * Similar to `toLvalue`, but also enforce it is mutable or raise an error. + * Params: + * _this = Expression to convert + * sc = scope + * Returns: `_this` converted to an lvalue, or an `ErrorExp` + */ +extern(C++) Expression modifiableLvalue(Expression _this, Scope* sc) +{ + return modifiableLvalueImpl(_this, sc, _this); +} + +// e = original / un-lowered expression to print in error messages +private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression e) +{ + Expression visit(Expression exp) + { + //printf("Expression::modifiableLvalue() %s, type = %s\n", exp.toChars(), exp.type.toChars()); + // See if this expression is a modifiable lvalue (i.e. not const) + if (exp.isBinAssignExp()) + return exp.toLvalue(sc); + + auto type = exp.type; + if (checkModifiable(exp, sc) == Modifiable.yes) + { + assert(type); + if (!type.isMutable()) + { + if (auto dve = exp.isDotVarExp()) + { + if (isNeedThisScope(sc, dve.var)) + for (Dsymbol s = sc.func; s; s = s.toParentLocal()) + { + FuncDeclaration ff = s.isFuncDeclaration(); + if (!ff) + break; + if (!ff.type.isMutable) + { + error(exp.loc, "cannot modify `%s` in `%s` function", exp.toChars(), MODtoChars(type.mod)); + return ErrorExp.get(); + } + } + } + error(exp.loc, "cannot modify `%s` expression `%s`", MODtoChars(type.mod), exp.toChars()); + return ErrorExp.get(); + } + else if (!type.isAssignable()) + { + error(exp.loc, "cannot modify struct instance `%s` of type `%s` because it contains `const` or `immutable` members", + exp.toChars(), type.toChars()); + return ErrorExp.get(); + } + } + return exp.toLvalueImpl(sc, e); + } + + Expression visitString(StringExp exp) + { + error(exp.loc, "cannot modify string literal `%s`", exp.toChars()); + return ErrorExp.get(); + } + + Expression visitVar(VarExp exp) + { + //printf("VarExp::modifiableLvalue('%s')\n", exp.var.toChars()); + if (exp.var.storage_class & STC.manifest) + { + error(exp.loc, "cannot modify manifest constant `%s`", exp.toChars()); + return ErrorExp.get(); + } + // See if this expression is a modifiable lvalue (i.e. not const) + return visit(exp); + } + + Expression visitPtr(PtrExp exp) + { + //printf("PtrExp::modifiableLvalue() %s, type %s\n", exp.toChars(), exp.type.toChars()); + Declaration var; + auto e1 = exp.e1; + if (auto se = e1.isSymOffExp()) + var = se.var; + else if (auto ve = e1.isVarExp()) + var = ve.var; + if (var && var.type.isFunction_Delegate_PtrToFunction()) + { + if (var.type.isTypeFunction()) + error(exp.loc, "function `%s` is not an lvalue and cannot be modified", var.toChars()); + else + error(exp.loc, "function pointed to by `%s` is not an lvalue and cannot be modified", var.toChars()); + return ErrorExp.get(); + } + return visit(exp); + } + + Expression visitSlice(SliceExp exp) + { + error(exp.loc, "slice expression `%s` is not a modifiable lvalue", exp.toChars()); + return exp; + } + + Expression visitComma(CommaExp exp) + { + exp.e2 = exp.e2.modifiableLvalueImpl(sc, e); + return exp; + } + + Expression visitDelegatePtr(DelegatePtrExp exp) + { + if (sc.setUnsafe(false, exp.loc, "cannot modify delegate pointer in `@safe` code `%s`", exp)) + { + return ErrorExp.get(); + } + return visit(exp); + } + + Expression visitDelegateFuncptr(DelegateFuncptrExp exp) + { + if (sc.setUnsafe(false, exp.loc, "cannot modify delegate function pointer in `@safe` code `%s`", exp)) + { + return ErrorExp.get(); + } + return visit(exp); + } + + Expression visitIndex(IndexExp exp) + { + //printf("IndexExp::modifiableLvalue(%s)\n", exp.toChars()); + Expression ex = exp.markSettingAAElem(); + if (ex.op == EXP.error) + return ex; + + return visit(exp); + } + + Expression visitCond(CondExp exp) + { + if (!exp.e1.isLvalue() && !exp.e2.isLvalue()) + { + error(exp.loc, "conditional expression `%s` is not a modifiable lvalue", exp.toChars()); + return ErrorExp.get(); + } + exp.e1 = exp.e1.modifiableLvalue(sc); + exp.e2 = exp.e2.modifiableLvalue(sc); + return exp.toLvalue(sc); + } + + switch(_this.op) + { + default: return visit(_this); + case EXP.string_: return visitString(_this.isStringExp()); + case EXP.variable: return visitVar(_this.isVarExp()); + case EXP.star: return visitPtr(_this.isPtrExp()); + case EXP.slice: return visitSlice(_this.isSliceExp()); + case EXP.comma: return visitComma(_this.isCommaExp()); + case EXP.delegatePointer: return visitDelegatePtr(_this.isDelegatePtrExp()); + case EXP.delegateFunctionPointer: return visitDelegateFuncptr(_this.isDelegateFuncptrExp()); + case EXP.index: return visitIndex(_this.isIndexExp()); + case EXP.question: return visitCond(_this.isCondExp()); + } +} + + /**************************************************** * Determine if `exp`, which gets its address taken, can do so safely. * Params: @@ -14676,15 +15890,12 @@ bool checkAddressable(Expression e, Scope* sc) */ private bool checkFunctionAttributes(Expression exp, Scope* sc, FuncDeclaration f) { - with(exp) - { - bool error = checkDisabled(sc, f); - error |= checkDeprecated(sc, f); - error |= checkPurity(sc, f); - error |= checkSafety(sc, f); - error |= checkNogc(sc, f); - return error; - } + bool error = f.checkDisabled(exp.loc, sc); + error |= f.checkDeprecated(exp.loc, sc); + error |= f.checkPurity(exp.loc, sc); + error |= f.checkSafety(exp.loc, sc); + error |= f.checkNogc(exp.loc, sc); + return error; } /******************************* diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index 2f6fae3a1ab..8d88207a566 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -250,6 +250,12 @@ extern (C++) struct Param const(char)[] resfile; const(char)[] exefile; const(char)[] mapfile; + + /// + bool parsingUnittestsRequired() + { + return useUnitTests || ddoc.doOutput || dihdr.doOutput; + } } enum mars_ext = "d"; // for D source files diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index d935bd3480b..8325081dbd2 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -41,6 +41,7 @@ import dmd.identifier; import dmd.init; import dmd.mtype; import dmd.nspace; +import dmd.optimize; import dmd.parse; import dmd.root.complex; import dmd.root.ctfloat; diff --git a/gcc/d/dmd/iasmgcc.d b/gcc/d/dmd/iasmgcc.d index 5494fecd70c..92837b45842 100644 --- a/gcc/d/dmd/iasmgcc.d +++ b/gcc/d/dmd/iasmgcc.d @@ -302,7 +302,7 @@ Ldone: extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) { //printf("GccAsmStatement.semantic()\n"); - const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + const bool doUnittests = global.params.parsingUnittestsRequired(); scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests); // Make a safe copy of the token list before parsing. @@ -341,7 +341,7 @@ extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) e = e.expressionSemantic(sc); // Check argument is a valid lvalue/rvalue. if (i < s.outputargs) - e = e.modifiableLvalue(sc, null); + e = e.modifiableLvalue(sc); else if (e.checkValue()) e = ErrorExp.get(); (*s.args)[i] = e; diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index 632c0d0a682..139db0f59e9 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -38,6 +38,7 @@ import dmd.init; import dmd.location; import dmd.mtype; import dmd.opover; +import dmd.optimize; import dmd.statement; import dmd.target; import dmd.tokens; diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index a1214b2623e..b8faec76d60 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -3258,6 +3258,24 @@ class Lexer scanloc.linnum = scanloc.linnum + 1; line = p; } + + /**************************** + * Print the tokens from the current `token` to the end, + * while not advancing the parser forward. + * Useful for debugging. + */ + void printRestOfTokens() + { + auto tk = &token; + while (1) + { + printf("%s ", (*tk).toChars()); + if (tk.value == TOK.endOfFile) + break; + tk = peek(tk); + } + printf("\n"); + } } diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d index addcd0103d0..b445b7b707b 100644 --- a/gcc/d/dmd/opover.d +++ b/gcc/d/dmd/opover.d @@ -34,6 +34,7 @@ import dmd.id; import dmd.identifier; import dmd.location; import dmd.mtype; +import dmd.optimize; import dmd.statement; import dmd.tokens; import dmd.typesem; diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d index 0065b016f83..69028fac21d 100644 --- a/gcc/d/dmd/optimize.d +++ b/gcc/d/dmd/optimize.d @@ -272,9 +272,9 @@ package void setLengthVarIfKnown(VarDeclaration lengthVar, Type type) * Returns: * Constant folded version of `e` */ -Expression Expression_optimize(Expression e, int result, bool keepLvalue) +Expression optimize(Expression e, int result, bool keepLvalue = false) { - //printf("Expression_optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue); + //printf("optimize() e: %s result: %d keepLvalue %d\n", e.toChars(), result, keepLvalue); Expression ret = e; void errorReturn() @@ -288,7 +288,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) { if (!e) return false; - Expression ex = Expression_optimize(e, flags, keepLvalue); + Expression ex = optimize(e, flags, keepLvalue); if (ex.op == EXP.error) { ret = ex; // store error result @@ -591,7 +591,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) Expression add = new AddExp(ae.loc, ex, new IntegerExp(ae.e2.loc, offset, ae.e2.type)); add.type = e.type; - ret = Expression_optimize(add, result, keepLvalue); + ret = optimize(add, result, keepLvalue); return; } } @@ -1239,7 +1239,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) ret = new CastExp(e.loc, ret, Type.tvoid); ret.type = e.type; } - ret = Expression_optimize(ret, result, false); + ret = optimize(ret, result, false); return; } expOptimize(e.e2, WANTvalue); @@ -1294,7 +1294,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) // `["c"] ~ "a" ~ "b"` becoming `["c"] ~ "ab"` scope CatExp cex = new CatExp(e.loc, ce1.e2, e.e2); cex.type = e.type; - Expression ex = Expression_optimize(cex, result, false); + Expression ex = optimize(cex, result, false); if (ex != cex) { e.e1 = ce1.e1; @@ -1323,9 +1323,9 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) return; const opt = e.econd.toBool(); if (opt.hasValue(true)) - ret = Expression_optimize(e.e1, result, keepLvalue); + ret = optimize(e.e1, result, keepLvalue); else if (opt.hasValue(false)) - ret = Expression_optimize(e.e2, result, keepLvalue); + ret = optimize(e.e2, result, keepLvalue); else { expOptimize(e.e1, result, keepLvalue); diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index 0b0ca916db2..bf220f31d9b 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -54,6 +54,7 @@ import dmd.nspace; import dmd.ob; import dmd.objc; import dmd.opover; +import dmd.optimize; import dmd.parse; import dmd.root.filename; import dmd.common.outbuffer; @@ -917,7 +918,7 @@ private extern(C++) final class Semantic3Visitor : Visitor if (f.isref) { // Function returns a reference - exp = exp.toLvalue(sc2, exp); + exp = exp.toLvalue(sc2); checkReturnEscapeRef(sc2, exp, false); exp = exp.optimize(WANTvalue, /*keepLvalue*/ true); } diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index d43d915e2e3..f8b2c26df7f 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -55,6 +55,7 @@ import dmd.location; import dmd.mtype; import dmd.mustuse; import dmd.nogc; +import dmd.optimize; import dmd.opover; import dmd.parse; import dmd.common.outbuffer; @@ -4887,7 +4888,7 @@ private Statements* flatten(Statement statement, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + const bool doUnittests = global.params.parsingUnittestsRequired(); auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut); scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.v.vin; diff --git a/gcc/d/dmd/staticcond.d b/gcc/d/dmd/staticcond.d index 923f1a99e80..1d18de31829 100644 --- a/gcc/d/dmd/staticcond.d +++ b/gcc/d/dmd/staticcond.d @@ -22,6 +22,7 @@ import dmd.expressionsem; import dmd.globals; import dmd.identifier; import dmd.mtype; +import dmd.optimize; import dmd.root.array; import dmd.common.outbuffer; import dmd.tokens; diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index ca2af79dde4..79df7fde02b 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -42,6 +42,7 @@ import dmd.identifier; import dmd.location; import dmd.mtype; import dmd.nogc; +import dmd.optimize; import dmd.parse; import dmd.root.array; import dmd.root.speller; @@ -1875,16 +1876,24 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return dimError(1); auto o = (*e.args)[0]; - Type t = isType(o); - AggregateDeclaration ad = t ? isAggregate(t) : null; - // Interfaces don't have an init symbol and hence cause linker errors - if (!ad || ad.isInterfaceDeclaration()) + ErrorExp badArgument() { error(e.loc, "struct / class type expected as argument to __traits(initSymbol) instead of `%s`", o.toChars()); return ErrorExp.get(); } + Type t = isType(o); + + if (!t || t.isTypeEnum()) + return badArgument(); + + AggregateDeclaration ad = isAggregate(t); + + // Interfaces don't have an init symbol and hence cause linker errors + if (!ad || ad.isInterfaceDeclaration()) + return badArgument(); + Declaration d = new SymbolDeclaration(ad.loc, ad); d.type = Type.tvoid.arrayOf().constOf(); d.storage_class |= STC.rvalue; diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index bbe11f63d4b..4a4c5d4f7f0 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -53,6 +53,7 @@ import dmd.visitor; import dmd.mtype; import dmd.objc; import dmd.opover; +import dmd.optimize; import dmd.parse; import dmd.root.complex; import dmd.root.ctfloat; @@ -1098,7 +1099,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) if (isRefOrOut && !isAuto && !(global.params.previewIn && (fparam.storageClass & STC.in_)) && global.params.rvalueRefParam != FeatureState.enabled) - e = e.toLvalue(sc, e); + e = e.toLvalue(sc); fparam.defaultArg = e; return (e.op != EXP.error); @@ -3748,7 +3749,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag } // check before alias resolution; the alias itself might be deprecated! if (s.isAliasDeclaration) - e.checkDeprecated(sc, s); + s.checkDeprecated(e.loc, sc); s = s.toAlias(); if (auto em = s.isEnumMember()) @@ -5009,7 +5010,7 @@ RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + const bool doUnittests = global.params.parsingUnittestsRequired(); auto locm = adjustLocForMixin(str, loc, global.params.mixinOut); scope p = new Parser!ASTCodegen(locm, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); p.transitionIn = global.params.v.vin; diff --git a/gcc/testsuite/gdc.test/compilable/previewin.d b/gcc/testsuite/gdc.test/compilable/previewin.d index 8926fbd6aa7..558005c5280 100644 --- a/gcc/testsuite/gdc.test/compilable/previewin.d +++ b/gcc/testsuite/gdc.test/compilable/previewin.d @@ -79,14 +79,11 @@ version (Win64) { void checkReal(in real p) { - // ref for x87 real, value for double-precision real - static assert(__traits(isRef, p) == (real.sizeof > 8)); } struct RGB { ubyte r, g, b; } void checkNonPowerOf2(in RGB p) { - static assert(__traits(isRef, p)); } } else version (X86_64) // Posix x86_64 @@ -94,7 +91,6 @@ else version (X86_64) // Posix x86_64 struct Empty {} // 1 dummy byte passed on the stack void checkEmptyStruct(in Empty p) { - static assert(!__traits(isRef, p)); } static if (is(__vector(double[4]))) @@ -102,7 +98,6 @@ else version (X86_64) // Posix x86_64 struct AvxVectorWrapper { __vector(double[4]) a; } // 256 bits void checkAvxVector(in AvxVectorWrapper p) { - static assert(!__traits(isRef, p)); } } } @@ -111,6 +106,5 @@ else version (AArch64) alias HVA = __vector(float[4])[4]; // can be passed in 4 vector registers void checkHVA(in HVA p) { - static assert(!__traits(isRef, p)); } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail212.d b/gcc/testsuite/gdc.test/fail_compilation/fail212.d index 5f308638b9c..780cd8279e5 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail212.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail212.d @@ -1,10 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/fail212.d(14): Error: function `fail212.S.bar` without `this` cannot be `const` +fail_compilation/fail212.d(10): Error: function `fail212.baz` without `this` cannot be `const` +fail_compilation/fail212.d(10): did you mean to use `const(int)` as the return type? +fail_compilation/fail212.d(18): Error: function `fail212.S.bar` without `this` cannot be `const` --- */ +const int baz(); + struct S { void foo() const diff --git a/gcc/testsuite/gdc.test/runnable/previewin.d b/gcc/testsuite/gdc.test/runnable/previewin.d index 117070dfe5e..50f22ee22bf 100644 --- a/gcc/testsuite/gdc.test/runnable/previewin.d +++ b/gcc/testsuite/gdc.test/runnable/previewin.d @@ -154,27 +154,25 @@ struct WithDtor @safe pure nothrow @nogc: // By value -void testin1(in uint p) { static assert(!__traits(isRef, p)); } +void testin1(in uint p) { } // By ref because of size -void testin2(in ulong[64] p) { static assert(__traits(isRef, p)); } +void testin2(in ulong[64] p) { } // By value or ref depending on size (or structs always passed by reference) -void testin3(in ValueT p) { static assert(!__traits(isRef, p) || true); } -void testin3(in RefT p) { static assert(__traits(isRef, p)); } +void testin3(in ValueT p) { } +void testin3(in RefT p) { } // By ref because of size (or arrays always passed by reference) -void testin4(in ValueT[64] p) { static assert(__traits(isRef, p)); } -void testin4(in RefT[4] p) { static assert(__traits(isRef, p)); } +void testin4(in ValueT[64] p) { } +void testin4(in RefT[4] p) { } // By ref because of non-copyability -void testin5(in NonCopyable noncopy) { static assert(__traits(isRef, noncopy)); } -static assert(testin5.mangleof == "_D9previewin7testin5FNaNbNiNfIKSQBe11NonCopyableZv"); // incl. `ref` +void testin5(in NonCopyable noncopy) { } // By ref because of postblit -void testin6(in WithPostblit withpostblit) { static assert(__traits(isRef, withpostblit)); } +void testin6(in WithPostblit withpostblit) { } // By ref because of copy ctor -void testin7(in WithCopyCtor withcopy) { static assert(__traits(isRef, withcopy)); } +void testin7(in WithCopyCtor withcopy) { } // By ref because of dtor void testin8(in WithDtor withdtor, scope bool* isTestOver) { - static assert(__traits(isRef, withdtor)); if (isTestOver) *isTestOver = true; } diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index 8c536ce71a5..95b2778d141 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -1c98326e787e504d9045004e593273ec99b13121 +fc06c514a8c4492f60fc89b8c4f857e6932fbcbd The first line of this file holds the git revision number of the last merge done from the dlang/phobos repository. diff --git a/libphobos/src/std/container/array.d b/libphobos/src/std/container/array.d index 0d6be93a1c1..ad120c1c74f 100644 --- a/libphobos/src/std/container/array.d +++ b/libphobos/src/std/container/array.d @@ -594,6 +594,10 @@ if (!is(immutable T == immutable bool)) assert(capacity == values.length); // We check that reserve has been called before the loop. } + /// ditto + // needed when T is an array and only one argument is passed + this(T single) { __ctor!T(single); } + /** * Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) */ @@ -1282,6 +1286,13 @@ if (!is(immutable T == immutable bool)) } } +@system unittest +{ + import std.algorithm.comparison : equal; + auto a = Array!string("test"); + assert(a[].equal(["test"])); +} + @safe unittest { // https://issues.dlang.org/show_bug.cgi?id=13621 diff --git a/libphobos/src/std/logger/package.d b/libphobos/src/std/logger/package.d index 4f4183c4cec..330ef88aa06 100644 --- a/libphobos/src/std/logger/package.d +++ b/libphobos/src/std/logger/package.d @@ -54,6 +54,7 @@ $(UL $(LI `trace`) $(LI `info`) $(LI `warning`) + #(LI `error`) $(LI `critical`) $(LI `fatal`) ) diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d index 89cfa07cc1f..fec5c85cbef 100644 --- a/libphobos/src/std/range/primitives.d +++ b/libphobos/src/std/range/primitives.d @@ -1015,12 +1015,27 @@ See_Also: enum bool isForwardRange(R) = isInputRange!R && is(typeof((R r) { return r.save; } (R.init)) == R); +/// ditto +enum bool isForwardRange(R, E) = + .isForwardRange!R && isQualifierConvertible!(ElementType!R, E); + /// @safe unittest { static assert(!isForwardRange!(int)); static assert( isForwardRange!(int[])); static assert( isForwardRange!(inout(int)[])); + + static assert( isForwardRange!(int[], const int)); + static assert(!isForwardRange!(int[], immutable int)); + + static assert(!isForwardRange!(const(int)[], int)); + static assert( isForwardRange!(const(int)[], const int)); + static assert(!isForwardRange!(const(int)[], immutable int)); + + static assert(!isForwardRange!(immutable(int)[], int)); + static assert( isForwardRange!(immutable(int)[], const int)); + static assert( isForwardRange!(immutable(int)[], immutable int)); } @safe unittest