diff --git a/CHANGELOG.md b/CHANGELOG.md index b727772eeee..9356f42fed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # LDC master #### Big news +- Frontend, druntime and Phobos are at version [2.104.2](https://dlang.org/changelog/2.104.0.html). (#4440) #### Platform support diff --git a/CMakeLists.txt b/CMakeLists.txt index 83a4c0739ce..1c34a3e7710 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,10 +111,10 @@ include(GetLinuxDistribution) # # Version information -set(LDC_VERSION "1.33.0") # May be overridden by git hash tag +set(LDC_VERSION "1.34.0") # May be overridden by git hash tag set(DMDFE_MAJOR_VERSION 2) -set(DMDFE_MINOR_VERSION 103) -set(DMDFE_PATCH_VERSION 1) +set(DMDFE_MINOR_VERSION 104) +set(DMDFE_PATCH_VERSION 2) set(DMD_VERSION ${DMDFE_MAJOR_VERSION}.${DMDFE_MINOR_VERSION}.${DMDFE_PATCH_VERSION}) diff --git a/dmd/aggregate.h b/dmd/aggregate.h index ac086679ddb..6ee2b32c5aa 100644 --- a/dmd/aggregate.h +++ b/dmd/aggregate.h @@ -108,8 +108,8 @@ class AggregateDeclaration : public ScopeDsymbol Expression *getRTInfo; // pointer to GC info generated by object.RTInfo(this) Visibility visibility; - bool noDefaultCtor; // no default construction - bool disableNew; // disallow allocations using `new` + d_bool noDefaultCtor; // no default construction + d_bool disableNew; // disallow allocations using `new` Sizeok sizeok; // set when structsize contains valid data virtual Scope *newScope(Scope *sc); @@ -273,10 +273,10 @@ class ClassDeclaration : public AggregateDeclaration // their own vtbl[] TypeInfoClassDeclaration *vclassinfo; // the ClassInfo object for this ClassDeclaration - bool com; // true if this is a COM class (meaning it derives from IUnknown) - bool stack; // true if this is a scope class + d_bool com; // true if this is a COM class (meaning it derives from IUnknown) + d_bool stack; // true if this is a scope class int cppDtorVtblIndex; // slot reserved for the virtual destructor [extern(C++)] - bool inuse; // to prevent recursive attempts + d_bool inuse; // to prevent recursive attempts ThreeState isabstract; // if abstract class Baseok baseok; // set the progress of base classes resolving diff --git a/dmd/aliasthis.d b/dmd/aliasthis.d index ef839fae536..ce384593c59 100644 --- a/dmd/aliasthis.d +++ b/dmd/aliasthis.d @@ -78,7 +78,7 @@ extern (C++) final class AliasThis : Dsymbol * Params: * sc = context * e = expression forming the `this` - * gag = if true do not print errors, return null instead + * gag = do not print errors, return `null` instead * findOnly = don't do further processing like resolving properties, * i.e. just return plain dotExp() result. * Returns: @@ -93,7 +93,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find { Loc loc = e.loc; Type tthis = (e.op == EXP.type ? e.type : null); - const flags = DotExpFlag.noAliasThis | (gag ? DotExpFlag.gag : 0); + const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag)); uint olderrors = gag ? global.startGagging() : 0; e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags); if (!e || findOnly) @@ -200,15 +200,29 @@ bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc) /************************************** * Check and set 'att' if 't' is a recursive 'alias this' type + * + * The goal is to prevent endless loops when there is a cycle in the alias this chain. + * Since there is no multiple `alias this`, the chain either ends in a leaf, + * or it loops back on itself as some point. + * + * Example: S0 -> (S1 -> S2 -> S3 -> S1) + * + * `S0` is not a recursive alias this, so this returns `false`, and a rewrite to `S1` can be tried. + * `S1` is a recursive alias this type, but since `att` is initialized to `null`, + * this still returns `false`, but `att1` is set to `S1`. + * A rewrite to `S2` and `S3` can be tried, but when we want to try a rewrite to `S1` again, + * we notice `att == t`, so we're back at the start of the loop, and this returns `true`. + * * Params: - * att = type reference used to detect recursion - * t = 'alias this' type + * att = type reference used to detect recursion. Should be initialized to `null`. + * t = type of 'alias this' rewrite to attempt * * Returns: - * Whether the 'alias this' is recursive or not + * `false` if the rewrite is safe, `true` if it would loop back around */ bool isRecursiveAliasThis(ref Type att, Type t) { + //printf("+isRecursiveAliasThis(att = %s, t = %s)\n", att ? att.toChars() : "null", t.toChars()); auto tb = t.toBasetype(); if (att && tb.equivalent(att)) return true; diff --git a/dmd/apply.d b/dmd/apply.d index 59ba9f5ecd6..d18b81f044f 100644 --- a/dmd/apply.d +++ b/dmd/apply.d @@ -170,7 +170,7 @@ public: { if (e.stageflags & stageApply) return; - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageApply; doCond(e.elements.peekSlice()) || applyTo(e); e.stageflags = old; diff --git a/dmd/astenums.d b/dmd/astenums.d index 6e882082bed..77f36f304a5 100644 --- a/dmd/astenums.d +++ b/dmd/astenums.d @@ -214,8 +214,8 @@ enum TY : ubyte Tmixin, Tnoreturn, Ttag, - TMAX } +enum TMAX = TY.max + 1; alias Tarray = TY.Tarray; alias Tsarray = TY.Tsarray; @@ -265,7 +265,6 @@ alias Ttraits = TY.Ttraits; alias Tmixin = TY.Tmixin; alias Tnoreturn = TY.Tnoreturn; alias Ttag = TY.Ttag; -alias TMAX = TY.TMAX; enum TFlags { @@ -328,6 +327,7 @@ enum VarArg : ubyte variadic = 1, /// (T t, ...) can be C-style (core.stdc.stdarg) or D-style (core.vararg) typesafe = 2, /// (T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions /// or https://dlang.org/spec/function.html#typesafe_variadic_functions + KRvariadic = 3, /// K+R C style variadics (no function prototype) } /************************* @@ -339,7 +339,7 @@ enum STMT : ubyte Error, Peel, Exp, DtorExp, - Compile, + Mixin, Compound, CompoundDeclaration, CompoundAsm, UnrolledLoop, Scope, @@ -439,3 +439,22 @@ enum FileType : ubyte ddoc, /// Ddoc documentation file (.dd) c, /// C source file } + +extern (C++) struct structalign_t +{ + private: + ushort value = 0; // unknown + enum STRUCTALIGN_DEFAULT = 1234; // default = match whatever the corresponding C compiler does + bool pack; // use #pragma pack semantics + + public: + pure @safe @nogc nothrow: + bool isDefault() const { return value == STRUCTALIGN_DEFAULT; } + void setDefault() { value = STRUCTALIGN_DEFAULT; } + bool isUnknown() const { return value == 0; } // value is not set + void setUnknown() { value = 0; } + void set(uint value) { this.value = cast(ushort)value; } + uint get() const { return value; } + bool isPack() const { return pack; } + void setPack(bool pack) { this.pack = pack; } +} diff --git a/dmd/attrib.d b/dmd/attrib.d index a73119078ec..d5f1cb0bda3 100644 --- a/dmd/attrib.d +++ b/dmd/attrib.d @@ -62,6 +62,12 @@ extern (C++) abstract class AttribDeclaration : Dsymbol this.decl = decl; } + extern (D) this(const ref Loc loc, Dsymbols* decl) + { + super(loc, null); + this.decl = decl; + } + extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl) { super(loc, ident); @@ -228,6 +234,12 @@ extern (C++) class StorageClassDeclaration : AttribDeclaration this.stc = stc; } + extern (D) this(const ref Loc loc, StorageClass stc, Dsymbols* decl) + { + super(loc, decl); + this.stc = stc; + } + override StorageClassDeclaration syntaxCopy(Dsymbol s) { assert(!s); @@ -1300,7 +1312,8 @@ extern(C++) final class ForwardingAttribDeclaration : AttribDeclaration * mixin("int x"); * https://dlang.org/spec/module.html#mixin-declaration */ -extern (C++) final class CompileDeclaration : AttribDeclaration +// Note: was CompileDeclaration +extern (C++) final class MixinDeclaration : AttribDeclaration { Expressions* exps; ScopeDsymbol scopesym; @@ -1309,19 +1322,19 @@ extern (C++) final class CompileDeclaration : AttribDeclaration extern (D) this(const ref Loc loc, Expressions* exps) { super(loc, null, null); - //printf("CompileDeclaration(loc = %d)\n", loc.linnum); + //printf("MixinDeclaration(loc = %d)\n", loc.linnum); this.exps = exps; } - override CompileDeclaration syntaxCopy(Dsymbol s) + override MixinDeclaration syntaxCopy(Dsymbol s) { - //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars()); - return new CompileDeclaration(loc, Expression.arraySyntaxCopy(exps)); + //printf("MixinDeclaration::syntaxCopy('%s')\n", toChars()); + return new MixinDeclaration(loc, Expression.arraySyntaxCopy(exps)); } override void addMember(Scope* sc, ScopeDsymbol sds) { - //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum); + //printf("MixinDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum); this.scopesym = sds; } @@ -1335,7 +1348,7 @@ extern (C++) final class CompileDeclaration : AttribDeclaration return "mixin"; } - override inout(CompileDeclaration) isCompileDeclaration() inout + override inout(MixinDeclaration) isMixinDeclaration() inout { return this; } diff --git a/dmd/attrib.h b/dmd/attrib.h index 44ceb12e0d0..1e75598c72a 100644 --- a/dmd/attrib.h +++ b/dmd/attrib.h @@ -132,7 +132,7 @@ class AlignDeclaration final : public AttribDeclaration class AnonDeclaration final : public AttribDeclaration { public: - bool isunion; + d_bool isunion; int sem; // 1 if successful semantic() unsigned anonoffset; // offset of anonymous struct unsigned anonstructsize; // size of anonymous struct @@ -175,8 +175,8 @@ class StaticIfDeclaration final : public ConditionalDeclaration { public: ScopeDsymbol *scopesym; - bool addisdone; - bool onStack; + d_bool addisdone; + d_bool onStack; StaticIfDeclaration *syntaxCopy(Dsymbol *s) override; Dsymbols *include(Scope *sc) override; @@ -193,8 +193,8 @@ class StaticForeachDeclaration final : public AttribDeclaration public: StaticForeach *sfe; ScopeDsymbol *scopesym; - bool onStack; - bool cached; + d_bool onStack; + d_bool cached; Dsymbols *cache; StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override; @@ -221,15 +221,15 @@ class ForwardingAttribDeclaration final : public AttribDeclaration // Mixin declarations -class CompileDeclaration final : public AttribDeclaration +class MixinDeclaration final : public AttribDeclaration { public: Expressions *exps; ScopeDsymbol *scopesym; - bool compiled; + d_bool compiled; - CompileDeclaration *syntaxCopy(Dsymbol *s) override; + MixinDeclaration *syntaxCopy(Dsymbol *s) override; void addMember(Scope *sc, ScopeDsymbol *sds) override; void setScope(Scope *sc) override; const char *kind() const override; diff --git a/dmd/blockexit.d b/dmd/blockexit.d index bd5b78e92dc..db738b4076c 100644 --- a/dmd/blockexit.d +++ b/dmd/blockexit.d @@ -63,34 +63,21 @@ enum BE : int */ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) { - extern (C++) final class BlockExit : Visitor - { - alias visit = Visitor.visit; - public: - FuncDeclaration func; - bool mustNotThrow; - int result; - - extern (D) this(FuncDeclaration func, bool mustNotThrow) scope - { - this.func = func; - this.mustNotThrow = mustNotThrow; - result = BE.none; - } + int result = BE.none; - override void visit(Statement s) + void visitDefaultCase(Statement s) { printf("Statement::blockExit(%p)\n", s); printf("%s\n", s.toChars()); assert(0); } - override void visit(ErrorStatement s) + void visitError(ErrorStatement s) { result = BE.none; } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { result = BE.fallthru; if (s.exp) @@ -115,13 +102,18 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(CompileStatement s) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitMixin(MixinStatement s) { assert(global.errors); result = BE.fallthru; } - override void visit(CompoundStatement cs) + void visitCompound(CompoundStatement cs) { //printf("CompoundStatement.blockExit(%p) %d result = x%X\n", cs, cs.statements.length, result); result = BE.fallthru; @@ -175,7 +167,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(UnrolledLoopStatement uls) + void visitUnrolledLoop(UnrolledLoopStatement uls) { result = BE.fallthru; foreach (s; *uls.statements) @@ -190,19 +182,19 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) } } - override void visit(ScopeStatement s) + void visitScope(ScopeStatement s) { //printf("ScopeStatement::blockExit(%p)\n", s.statement); result = blockExit(s.statement, func, mustNotThrow); } - override void visit(WhileStatement s) + void visitWhile(WhileStatement s) { assert(global.errors); result = BE.fallthru; } - override void visit(DoStatement s) + void visitDo(DoStatement s) { if (s._body) { @@ -227,7 +219,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result &= ~(BE.break_ | BE.continue_); } - override void visit(ForStatement s) + void visitFor(ForStatement s) { result = BE.fallthru; if (s._init) @@ -259,7 +251,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= canThrow(s.increment, func, mustNotThrow); } - override void visit(ForeachStatement s) + void visitForeach(ForeachStatement s) { result = BE.fallthru; result |= canThrow(s.aggr, func, mustNotThrow); @@ -268,13 +260,13 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= blockExit(s._body, func, mustNotThrow) & ~(BE.break_ | BE.continue_); } - override void visit(ForeachRangeStatement s) + void visitForeachRange(ForeachRangeStatement s) { assert(global.errors); result = BE.fallthru; } - override void visit(IfStatement s) + void visitIf(IfStatement s) { //printf("IfStatement::blockExit(%p)\n", s); result = BE.none; @@ -297,24 +289,24 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) //printf("IfStatement::blockExit(%p) = x%x\n", s, result); } - override void visit(ConditionalStatement s) + void visitConditional(ConditionalStatement s) { result = blockExit(s.ifbody, func, mustNotThrow); if (s.elsebody) result |= blockExit(s.elsebody, func, mustNotThrow); } - override void visit(PragmaStatement s) + void visitPragma(PragmaStatement s) { result = BE.fallthru; } - override void visit(StaticAssertStatement s) + void visitStaticAssert(StaticAssertStatement s) { result = BE.fallthru; } - override void visit(SwitchStatement s) + void visitSwitch(SwitchStatement s) { result = BE.none; result |= canThrow(s.condition, func, mustNotThrow); @@ -332,63 +324,63 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= BE.fallthru; } - override void visit(CaseStatement s) + void visitCase(CaseStatement s) { result = blockExit(s.statement, func, mustNotThrow); } - override void visit(DefaultStatement s) + void visitDefault(DefaultStatement s) { result = blockExit(s.statement, func, mustNotThrow); } - override void visit(GotoDefaultStatement s) + void visitGotoDefault(GotoDefaultStatement s) { result = BE.goto_; } - override void visit(GotoCaseStatement s) + void visitGotoCase(GotoCaseStatement s) { result = BE.goto_; } - override void visit(SwitchErrorStatement s) + void visitSwitchError(SwitchErrorStatement s) { // Switch errors are non-recoverable result = BE.halt; } - override void visit(ReturnStatement s) + void visitReturn(ReturnStatement s) { result = BE.return_; if (s.exp) result |= canThrow(s.exp, func, mustNotThrow); } - override void visit(BreakStatement s) + void visitBreak(BreakStatement s) { //printf("BreakStatement::blockExit(%p) = x%x\n", s, s.ident ? BE.goto_ : BE.break_); result = s.ident ? BE.goto_ : BE.break_; } - override void visit(ContinueStatement s) + void visitContinue(ContinueStatement s) { result = s.ident ? BE.continue_ | BE.goto_ : BE.continue_; } - override void visit(SynchronizedStatement s) + void visitSynchronized(SynchronizedStatement s) { result = blockExit(s._body, func, mustNotThrow); } - override void visit(WithStatement s) + void visitWith(WithStatement s) { result = BE.none; result |= canThrow(s.exp, func, mustNotThrow); result |= blockExit(s._body, func, mustNotThrow); } - override void visit(TryCatchStatement s) + void visitTryCatch(TryCatchStatement s) { assert(s._body); result = blockExit(s._body, func, false); @@ -428,7 +420,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= catchresult; } - override void visit(TryFinallyStatement s) + void visitTryFinally(TryFinallyStatement s) { result = BE.fallthru; if (s._body) @@ -470,13 +462,13 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= finalresult & ~BE.fallthru; } - override void visit(ScopeGuardStatement s) + void visitScopeGuard(ScopeGuardStatement s) { // At this point, this statement is just an empty placeholder result = BE.fallthru; } - override void visit(ThrowStatement s) + void visitThrow(ThrowStatement s) { if (s.internalThrow) { @@ -486,16 +478,16 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) return; } - result = checkThrow(s.loc, s.exp, mustNotThrow); + result = checkThrow(s.loc, s.exp, mustNotThrow, func); } - override void visit(GotoStatement s) + void visitGoto(GotoStatement s) { //printf("GotoStatement::blockExit(%p)\n", s); result = BE.goto_; } - override void visit(LabelStatement s) + void visitLabel(LabelStatement s) { //printf("LabelStatement::blockExit(%p)\n", s); result = blockExit(s.statement, func, mustNotThrow); @@ -503,30 +495,31 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) result |= BE.fallthru; } - override void visit(CompoundAsmStatement s) + void visitCompoundAsm(CompoundAsmStatement s) { // Assume the worst result = BE.fallthru | BE.return_ | BE.goto_ | BE.halt; if (!(s.stc & STC.nothrow_)) { - if (mustNotThrow && !(s.stc & STC.nothrow_)) - s.error("`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); + if(func) + func.setThrow(s.loc, "`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); + if (mustNotThrow) + s.error("`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); // TODO else result |= BE.throw_; } } - override void visit(ImportStatement s) + void visitImport(ImportStatement s) { result = BE.fallthru; } - } if (!s) return BE.fallthru; - scope BlockExit be = new BlockExit(func, mustNotThrow); - s.accept(be); - return be.result; + mixin VisitStatement!void visit; + visit.VisitStatement(s); + return result; } /++ @@ -537,10 +530,11 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow) + loc = location of the `throw` + exp = expression yielding the throwable + mustNotThrow = inside of a `nothrow` scope? + + func = function containing the `throw` + + Returns: `BE.[err]throw` depending on the type of `exp` +/ -BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow) +BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow, FuncDeclaration func) { import dmd.errors : error; @@ -554,6 +548,8 @@ BE checkThrow(ref const Loc loc, Expression exp, const bool mustNotThrow) } if (mustNotThrow) loc.error("`%s` is thrown but not caught", exp.type.toChars()); + else if (func) + func.setThrow(loc, "`%s` is thrown but not caught", exp.type); return BE.throw_; } diff --git a/dmd/canthrow.d b/dmd/canthrow.d index 0c237e6da56..e0e473d6ff0 100644 --- a/dmd/canthrow.d +++ b/dmd/canthrow.d @@ -53,7 +53,7 @@ enum CT : BE */ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustNotThrow) { - //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); + //printf("Expression::canThrow(%d) %s\n", mustNotThrow, e.toChars()); // stop walking if we determine this expression can throw extern (C++) final class CanThrow : StoppableVisitor { @@ -76,11 +76,16 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN { if (mustNotThrow) { - e.error("%s `%s` is not `nothrow`", - f.kind(), f.toPrettyChars()); + e.error("%s `%s` is not `nothrow`", f.kind(), f.toPrettyChars()); + if (!f.isDtorDeclaration()) + errorSupplementalInferredAttr(f, 10, false, STC.nothrow_); e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow"); } + else if (func) + { + func.setThrowCall(e.loc, f); + } result |= CT.exception; } } @@ -113,7 +118,7 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN { auto sd = ts.sym; const id = ce.f.ident; - if (sd.postblit && isArrayConstructionOrAssign(id)) + if (sd.postblit && isArrayConstruction(id)) { checkFuncThrows(ce, sd.postblit); return; @@ -205,7 +210,7 @@ extern (C++) /* CT */ BE canThrow(Expression e, FuncDeclaration func, bool mustN override void visit(ThrowExp te) { - const res = checkThrow(te.loc, te.e1, mustNotThrow); + const res = checkThrow(te.loc, te.e1, mustNotThrow, func); assert((res & ~(CT.exception | CT.error)) == 0); result |= res; } diff --git a/dmd/cli.d b/dmd/cli.d index 68f92c16c85..90e123a96b6 100644 --- a/dmd/cli.d +++ b/dmd/cli.d @@ -272,6 +272,12 @@ dmd -cov -unittest myprog.d --- `, ), + Option("cpp=", + "use filename as the name of the C preprocessor to use for ImportC files", + `Normally the C preprocessor used by the associated C compiler is used to + preprocess ImportC files, + this is overridden by the $(TT -cpp) switch.` + ), Option("D", "generate documentation", `$(P Generate $(LINK2 $(ROOT_DIR)spec/ddoc.html, documentation) from source.) diff --git a/dmd/clone.d b/dmd/clone.d index 19bf83e4ec3..60e373c502b 100644 --- a/dmd/clone.d +++ b/dmd/clone.d @@ -840,7 +840,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) " else " ~ " h = h * 33 + typeid(T).getHash(cast(const void*)&p.tupleof[i]);" ~ "return h;"; - fop.fbody = new CompileStatement(loc, new StringExp(loc, code)); + fop.fbody = new MixinStatement(loc, new StringExp(loc, code)); Scope* sc2 = sc.push(); sc2.stc = 0; sc2.linkage = LINK.d; @@ -1261,8 +1261,9 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) // if this field's postblit is not `nothrow`, add a `scope(failure)` // block to destroy any prior successfully postblitted fields should - // this field's postblit fail - if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow) + // this field's postblit fail. + // Don't generate it for betterC code since it cannot throw exceptions. + if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow && !global.params.betterC) { // create a list of destructors that need to be called Expression[] dtorCalls; diff --git a/dmd/common/outbuffer.h b/dmd/common/outbuffer.h index b672842e74d..4c1dceea3f6 100644 --- a/dmd/common/outbuffer.h +++ b/dmd/common/outbuffer.h @@ -21,11 +21,11 @@ struct OutBuffer private: DArray data; d_size_t offset; - bool notlinehead; + d_bool notlinehead; void *fileMapping; // pointer to a file mapping object not used on the C++ side public: - bool doindent; - bool spaces; + d_bool doindent; + d_bool spaces; int level; OutBuffer() diff --git a/dmd/common/string.d b/dmd/common/string.d index 2f7230cb92e..cfae1bb6749 100644 --- a/dmd/common/string.d +++ b/dmd/common/string.d @@ -13,7 +13,7 @@ module dmd.common.string; nothrow: /** -Defines a temporary array using a fixed-length buffer as back store. If the length +Defines a temporary array of `Element`s using a fixed-length buffer as back store. If the length of the buffer suffices, it is readily used. Otherwise, `malloc` is used to allocate memory for the array and `free` is used for deallocation in the destructor. @@ -21,19 +21,26 @@ destructor. This type is meant to use exclusively as an automatic variable. It is not default constructible or copyable. */ -struct SmallBuffer(T) +struct SmallBuffer(Element) { import core.stdc.stdlib : malloc, free; - private T[] _extent; + private Element[] _extent; private bool needsFree; nothrow: + @nogc: @disable this(); // no default ctor - @disable this(ref const SmallBuffer!T); // noncopyable, nonassignable - - this(size_t len, T[] buffer) + @disable this(ref const SmallBuffer!Element); // noncopyable, nonassignable + + /*********** + * Construct a SmallBuffer + * Params: + * len = number of elements in array + * buffer = slice to use as backing-store, if len will fit in it + */ + scope this(size_t len, return scope Element[] buffer) { if (len <= buffer.length) { @@ -41,7 +48,8 @@ struct SmallBuffer(T) } else { - _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len]; + assert(len < sizeof.max / Element.sizeof); + _extent = (cast(typeof(_extent.ptr)) malloc(len * Element.sizeof))[0 .. len]; _extent.ptr || assert(0, "Out of memory."); needsFree = true; } @@ -54,16 +62,22 @@ struct SmallBuffer(T) free(_extent.ptr); } - void create(size_t len) + /****** + * Resize existing SmallBuffer. + * Params: + * len = number of elements after resize + */ + scope void create(size_t len) { if (len <= _extent.length) { - _extent = _extent[0 .. len]; + _extent = _extent[0 .. len]; // reuse existing storage } else { __dtor(); - _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len]; + assert(len < sizeof.max / Element.sizeof); + _extent = (cast(typeof(_extent.ptr)) malloc(len * Element.sizeof))[0 .. len]; _extent.ptr || assert(0, "Out of memory."); needsFree = true; } diff --git a/dmd/cond.h b/dmd/cond.h index 422a715bdba..45094d14991 100644 --- a/dmd/cond.h +++ b/dmd/cond.h @@ -52,7 +52,7 @@ class StaticForeach final : public RootObject ForeachStatement *aggrfe; ForeachRangeStatement *rangefe; - bool needExpansion; + d_bool needExpansion; StaticForeach *syntaxCopy(); }; diff --git a/dmd/constfold.d b/dmd/constfold.d index 4ece94496f6..c542ee444fb 100644 --- a/dmd/constfold.d +++ b/dmd/constfold.d @@ -1450,7 +1450,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); StringExp es = ue.exp().isStringExp(); es.type = type; - es.committed = 1; + es.committed = true; } else { diff --git a/dmd/cparse.d b/dmd/cparse.d index a39fa1d3491..d25a23868c4 100644 --- a/dmd/cparse.d +++ b/dmd/cparse.d @@ -14,22 +14,18 @@ module dmd.cparse; import core.stdc.stdio; -import core.stdc.string; +import core.stdc.string : memcpy; + import dmd.astenums; import dmd.errorsink; -import dmd.globals; import dmd.id; import dmd.identifier; import dmd.lexer; import dmd.location; import dmd.parse; -import dmd.errors; import dmd.root.array; -import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; -import dmd.root.rootobject; -import dmd.root.string; import dmd.tokens; version (LDC) private enum LDC_pre_2084 = __VERSION__ < 2084; // workaround bug with LDC < v1.14 host compilers @@ -74,9 +70,10 @@ final class CParser(AST) : Parser!AST extern (D) this(TARGET)(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, - const ref TARGET target, OutBuffer* defines) scope + const ref TARGET target, OutBuffer* defines, const CompileEnv* compileEnv) scope { - super(_module, input, doDocComment, errorSink); + const bool doUnittests = false; + super(_module, input, doDocComment, errorSink, compileEnv, doUnittests); //printf("CParser.this()\n"); mod = _module; @@ -205,7 +202,7 @@ final class CParser(AST) : Parser!AST else if (token.value == TOK.leftCurly) s = cparseStatement(ParseStatementFlags.curly | ParseStatementFlags.scope_); else - s = cparseStatement(ParseStatementFlags.semiOk); + s = cparseStatement(0); s = new AST.LabelStatement(loc, ident, s); break; } @@ -310,6 +307,7 @@ final class CParser(AST) : Parser!AST case TOK.extern_: case TOK.static_: case TOK._Thread_local: + case TOK.__thread: case TOK.auto_: case TOK.register: @@ -379,7 +377,7 @@ final class CParser(AST) : Parser!AST auto statements = new AST.Statements(); while (token.value != TOK.rightCurly && token.value != TOK.endOfFile) { - statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(cparseStatement(ParseStatementFlags.curlyScope)); } if (endPtr) *endPtr = token.ptr; @@ -513,6 +511,14 @@ final class CParser(AST) : Parser!AST nextToken(); auto exp = cparseAssignExp(); + AST.Expression expHigh; + if (token.value == TOK.dotDotDot) + { + /* Case Ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html + */ + nextToken(); + expHigh = cparseAssignExp(); + } check(TOK.colon); if (flags & ParseStatementFlags.curlyScope) @@ -520,7 +526,7 @@ final class CParser(AST) : Parser!AST auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - auto cur = cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope); + auto cur = cparseStatement(ParseStatementFlags.curlyScope); statements.push(cur); // https://issues.dlang.org/show_bug.cgi?id=21739 @@ -535,10 +541,13 @@ final class CParser(AST) : Parser!AST } else { - s = cparseStatement(ParseStatementFlags.semi); + s = cparseStatement(0); } s = new AST.ScopeStatement(loc, s, token.loc); - s = new AST.CaseStatement(loc, exp, s); + if (expHigh) + s = new AST.CaseRangeStatement(loc, exp, expHigh, s); + else + s = new AST.CaseStatement(loc, exp, s); break; } @@ -552,12 +561,12 @@ final class CParser(AST) : Parser!AST auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - statements.push(cparseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(cparseStatement(ParseStatementFlags.curlyScope)); } s = new AST.CompoundStatement(loc, statements); } else - s = cparseStatement(ParseStatementFlags.semi); + s = cparseStatement(0); s = new AST.ScopeStatement(loc, s, token.loc); s = new AST.DefaultStatement(loc, s); break; @@ -607,7 +616,20 @@ final class CParser(AST) : Parser!AST } case TOK.asm_: - s = parseAsm(); + switch (peekNext()) + { + case TOK.goto_: + case TOK.inline: + case TOK.volatile: + case TOK.leftParenthesis: + s = cparseGnuAsm(); + break; + + default: + // ImportC extensions: parse as a D asm block. + s = parseAsm(); + break; + } break; default: @@ -780,7 +802,10 @@ final class CParser(AST) : Parser!AST case TOK.leftParenthesis: nextToken(); - e = cparseExpression(); + if (token.value == TOK.leftCurly) + e = cparseStatementExpression(); // gcc extension + else + e = cparseExpression(); check(TOK.rightParenthesis); break; @@ -1603,6 +1628,41 @@ final class CParser(AST) : Parser!AST return e; } + /***************************** + * gcc extension: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html + * Represent as a function literal, then call the function literal. + * Parser is on opening curly brace. + */ + private AST.Expression cparseStatementExpression() + { + AST.ParameterList parameterList; + StorageClass stc = 0; + const loc = token.loc; + typedefTab.push(null); + auto fbody = cparseStatement(ParseStatementFlags.scope_); + typedefTab.pop(); // end of function scope + + // Rewrite last ExpStatement (if there is one) as a ReturnStatement + auto ss = fbody.isScopeStatement(); + auto cs = ss.statement.isCompoundStatement(); + assert(cs); + if (const len = (*cs.statements).length) + { + auto s = (*cs.statements)[len - 1]; + if (auto es = s.isExpStatement()) + (*cs.statements)[len - 1] = new AST.ReturnStatement(es.loc, es.exp); + } + + auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); + auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0); + fd.fbody = fbody; + + auto fe = new AST.FuncExp(loc, fd); + auto args = new AST.Expressions(); + auto e = new AST.CallExp(loc, fe, args); // call the function literal + return e; + } + //} /********************************************************************************/ /********************************* Declaration Parser ***************************/ @@ -1663,6 +1723,12 @@ final class CParser(AST) : Parser!AST auto stag = (tt.tok == TOK.struct_) ? new AST.StructDeclaration(tt.loc, tt.id, false) : (tt.tok == TOK.union_) ? new AST.UnionDeclaration(tt.loc, tt.id) : new AST.EnumDeclaration(tt.loc, tt.id, tt.base); + if (!tt.packalign.isUnknown()) + { + // saw `struct __declspec(align(N)) Tag ...` + auto st = stag.isStructDeclaration(); + st.alignment = tt.packalign; + } stag.members = tt.members; tt.members = null; if (!symbols) @@ -1762,7 +1828,7 @@ final class CParser(AST) : Parser!AST case TOK.asm_: case TOK.__attribute__: if (token.value == TOK.asm_) - asmName = cparseSimpleAsmExpr(); + asmName = cparseGnuAsmLabel(); if (token.value == TOK.__attribute__) { cparseGnuAttributes(specifier); @@ -1846,14 +1912,12 @@ final class CParser(AST) : Parser!AST if (tt.id || tt.tok == TOK.enum_) { if (!tt.id && id) + /* This applies for enums declared as + * typedef enum {A} E; + */ tt.id = id; Specifier spec; - auto stag = declareTag(tt, spec); - if (tt.tok == TOK.enum_) - { - isalias = false; - s = new AST.AliasDeclaration(token.loc, id, stag); - } + declareTag(tt, spec); } } if (isalias) @@ -1895,6 +1959,7 @@ final class CParser(AST) : Parser!AST if (specifier.scw & SCW.x_Thread_local) error("functions cannot be `_Thread_local`"); // C11 6.7.1-4 auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, specifiersToSTC(level, specifier), dt, specifier.noreturn); + specifiersToFuncDeclaration(fd, specifier); s = fd; } else @@ -1904,7 +1969,9 @@ final class CParser(AST) : Parser!AST if (!hasInitializer && !(specifier.scw & (SCW.xextern | SCW.xstatic | SCW.x_Thread_local) || level == LVL.global)) initializer = new AST.VoidInitializer(token.loc); - s = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier)); + auto vd = new AST.VarDeclaration(token.loc, dt, id, initializer, specifiersToSTC(level, specifier)); + specifiersToVarDeclaration(vd, specifier); + s = vd; } if (level != LVL.global) insertIdToTypedefTab(id); // non-typedef declarations can hide typedefs in outer scopes @@ -2015,8 +2082,7 @@ final class CParser(AST) : Parser!AST auto pl = ft.parameterList; if (pl.varargs != AST.VarArg.none && pl.length) error("function identifier-list cannot end with `...`"); - ft.parameterList.varargs = AST.VarArg.variadic; // but C11 allows extra arguments - importBuiltins = true; // will need __va_list_tag + ft.parameterList.varargs = AST.VarArg.KRvariadic; // but C11 allows extra arguments auto plLength = pl.length; if (symbols.length != plLength) error(token.loc, "%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length); @@ -2041,6 +2107,10 @@ final class CParser(AST) : Parser!AST error("storage class and type are not allowed in identifier-list"); foreach (s; (*symbols)[]) // yes, quadratic { + auto ad = s.isAttribDeclaration(); + if (ad) + s = (*ad.decl)[0]; // AlignDeclaration wrapping the declaration + auto d = s.isDeclaration(); if (d && p.ident == d.ident && d.type) { @@ -2065,6 +2135,7 @@ final class CParser(AST) : Parser!AST typedefTab.pop(); // end of function scope auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, specifiersToSTC(LVL.global, specifier), ft, specifier.noreturn); + specifiersToFuncDeclaration(fd, specifier); if (addFuncName) { @@ -2237,6 +2308,7 @@ final class CParser(AST) : Parser!AST case TOK.typedef_: scwx = SCW.xtypedef; break; case TOK.inline: scwx = SCW.xinline; break; case TOK._Noreturn: scwx = SCW.x_Noreturn; break; + case TOK.__thread: case TOK._Thread_local: scwx = SCW.x_Thread_local; break; // Type qualifiers @@ -2272,15 +2344,23 @@ final class CParser(AST) : Parser!AST const sloc = token.loc; nextToken(); + Specifier tagSpecifier; + /* GNU Extensions * struct-or-union-specifier: * struct-or-union gnu-attributes (opt) identifier (opt) { struct-declaration-list } gnu-attributes (opt) * struct-or-union gnu-attribute (opt) identifier */ - if (token.value == TOK.__attribute__) - cparseGnuAttributes(specifier); - - t = cparseStruct(sloc, structOrUnion, symbols); + while (1) + { + if (token.value == TOK.__attribute__) + cparseGnuAttributes(tagSpecifier); + else if (token.value == TOK.__declspec) + cparseDeclspec(tagSpecifier); + else + break; + } + t = cparseStruct(sloc, structOrUnion, tagSpecifier.packalign, symbols); tkwx = TKW.xtag; break; } @@ -2300,9 +2380,9 @@ final class CParser(AST) : Parser!AST tk = peek(tk); if (isTypeName(tk) && tk.value == TOK.rightParenthesis) { + nextToken(); nextToken(); t = cparseTypeName(); - // TODO - implement the "atomic" part of t tkwx = TKW.x_Atomic; break; } @@ -2471,6 +2551,12 @@ final class CParser(AST) : Parser!AST error("`inline` and `_Noreturn` function specifiers not allowed for `_Thread_local`"); scw &= ~scwx; } + if (level == LVL.local && + scw & (SCW.x_Thread_local) && !(scw & (SCW.xstatic | SCW.xextern))) + { + error("`_Thread_local` in block scope must be accompanied with `static` or `extern`"); // C11 6.7.1-3 + scw &= ~scwx; + } if (level & (LVL.parameter | LVL.prototype) && scw & ~SCW.xregister) { @@ -2571,6 +2657,7 @@ final class CParser(AST) : Parser!AST } case TKW.xtag: + case TKW.x_Atomic: // no atomics for you break; // t is already set default: @@ -2809,7 +2896,10 @@ final class CParser(AST) : Parser!AST auto parameterList = cparseParameterList(); const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage; - AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, 0); + StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0; + if (specifier._pure) + stc |= STC.pure_; + AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc); // tf = tf.addSTC(storageClass); // TODO insertTx(ts, tf, t); // ts -> ... -> tf -> t @@ -2962,8 +3052,7 @@ final class CParser(AST) : Parser!AST if (token.value == TOK.rightParenthesis) // func() { nextToken(); - importBuiltins = true; // will need __va_list_tag - return AST.ParameterList(parameters, AST.VarArg.variadic, varargsStc); + return AST.ParameterList(parameters, AST.VarArg.KRvariadic, varargsStc); } /* Create function prototype scope @@ -3087,9 +3176,15 @@ final class CParser(AST) : Parser!AST * extended-decl-modifier extended-decl-modifier-seq * * extended-decl-modifier: + * align(number) + * deprecated(depMsg) * dllimport * dllexport + * naked + * noinline * noreturn + * nothrow + * thread * Params: * specifier = filled in with the attribute(s) */ @@ -3099,8 +3194,6 @@ final class CParser(AST) : Parser!AST /* Check for dllexport, dllimport * Ignore the rest */ - bool dllimport; // TODO implement - bool dllexport; // TODO implement nextToken(); // move past __declspec check(TOK.leftParenthesis); while (1) @@ -3116,12 +3209,22 @@ final class CParser(AST) : Parser!AST { if (token.ident == Id.dllimport) { - dllimport = true; + specifier.dllimport = true; nextToken(); } else if (token.ident == Id.dllexport) { - dllexport = true; + specifier.dllexport = true; + nextToken(); + } + else if (token.ident == Id.naked) + { + specifier.naked = true; + nextToken(); + } + else if (token.ident == Id.noinline) + { + specifier.scw |= SCW.xnoinline; nextToken(); } else if (token.ident == Id.noreturn) @@ -3129,6 +3232,49 @@ final class CParser(AST) : Parser!AST specifier.noreturn = true; nextToken(); } + else if (token.ident == Id._nothrow) + { + specifier._nothrow = true; + nextToken(); + } + else if (token.ident == Id.thread) + { + specifier.scw |= SCW.x_Thread_local; + nextToken(); + } + else if (token.ident == Id._align) + { + // Microsoft spec is very imprecise as to how this actually works + nextToken(); + check(TOK.leftParenthesis); + if (token.value == TOK.int32Literal) + { + const n = token.unsvalue; + if (n < 1 || n & (n - 1) || 8192 < n) + error("__decspec(align(%lld)) must be an integer positive power of 2 and be <= 8,192", cast(ulong)n); + specifier.packalign.set(cast(uint)n); + specifier.packalign.setPack(true); + nextToken(); + } + else + { + error("alignment value expected, not `%s`", token.toChars()); + nextToken(); + } + + check(TOK.rightParenthesis); + } + else if (token.ident == Id._deprecated) + { + specifier._deprecated = true; + nextToken(); + if (token.value == TOK.leftParenthesis) // optional deprecation message + { + nextToken(); + specifier.depMsg = cparseExpression(); + check(TOK.rightParenthesis); + } + } else { nextToken(); @@ -3136,6 +3282,8 @@ final class CParser(AST) : Parser!AST cparseParens(); } } + else if (token.value == TOK.restrict) // ImportC assigns no semantics to `restrict`, so just ignore the keyword. + nextToken(); else { error("extended-decl-modifier expected"); @@ -3145,7 +3293,8 @@ final class CParser(AST) : Parser!AST } /************************* - * Simple asm parser + * Parser for asm label. It appears after the declarator, and has apparently + * nothing to do with inline assembler. * https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html * simple-asm-expr: * asm ( asm-string-literal ) @@ -3153,17 +3302,107 @@ final class CParser(AST) : Parser!AST * asm-string-literal: * string-literal */ - private AST.StringExp cparseSimpleAsmExpr() + private AST.StringExp cparseGnuAsmLabel() { nextToken(); // move past asm check(TOK.leftParenthesis); if (token.value != TOK.string_) - error("string literal expected"); + error("string literal expected for Asm Label, not `%s`", token.toChars()); auto label = cparsePrimaryExp(); check(TOK.rightParenthesis); return cast(AST.StringExp) label; } + /******************** + * Parse C inline assembler statement in Gnu format. + * https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html + * asm asm-qualifiers ( AssemblerTemplate : OutputOperands : InputOperands : Clobbers : GotoLabels ) + * Current token is on the `asm`. + * Returns: + * inline assembler expression as a Statement + */ + private AST.Statement cparseGnuAsm() + { + // Defer parsing of AsmStatements until semantic processing. + const loc = token.loc; + + nextToken(); + + // Consume all asm-qualifiers. As a future optimization, we could record + // the `inline` and `volatile` storage classes against the statement. + while (token.value == TOK.goto_ || + token.value == TOK.inline || + token.value == TOK.volatile) + nextToken(); + + check(TOK.leftParenthesis); + if (token.value != TOK.string_) + error("string literal expected for Assembler Template, not `%s`", token.toChars()); + Token* toklist = null; + Token** ptoklist = &toklist; + //Identifier label = null; + auto statements = new AST.Statements(); + + int parens; + while (1) + { + switch (token.value) + { + case TOK.leftParenthesis: + ++parens; + goto default; + + case TOK.rightParenthesis: + --parens; + if (parens >= 0) + goto default; + break; + + case TOK.semicolon: + error("matching `)` expected, not `;`"); + break; + + case TOK.endOfFile: + /* ( */ + error("matching `)` expected, not end of file"); + break; + + case TOK.colonColon: // treat as two separate : tokens for iasmgcc + *ptoklist = allocateToken(); + memcpy(*ptoklist, &token, Token.sizeof); + (*ptoklist).value = TOK.colon; + ptoklist = &(*ptoklist).next; + + *ptoklist = allocateToken(); + memcpy(*ptoklist, &token, Token.sizeof); + (*ptoklist).value = TOK.colon; + ptoklist = &(*ptoklist).next; + + *ptoklist = null; + nextToken(); + continue; + + default: + *ptoklist = allocateToken(); + memcpy(*ptoklist, &token, Token.sizeof); + ptoklist = &(*ptoklist).next; + *ptoklist = null; + nextToken(); + continue; + } + if (toklist) + { + // Create AsmStatement from list of tokens we've saved + AST.Statement s = new AST.AsmStatement(token.loc, toklist); + statements.push(s); + } + break; + } + nextToken(); + auto s = new AST.CompoundAsmStatement(loc, statements, 0); + return s; + } + /************************* * __attribute__ parser * https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html @@ -3225,25 +3464,75 @@ final class CParser(AST) : Parser!AST */ private void cparseGnuAttribute(ref Specifier specifier) { - /* Check for dllimport, dllexport, vector_size(bytes) + /* Check for dllimport, dllexport, naked, noreturn, vector_size(bytes) * Ignore the rest */ - bool dllimport; // TODO implement - bool dllexport; // TODO implement - if (!isGnuAttributeName()) return; if (token.value == TOK.identifier) { - if (token.ident == Id.dllimport) + if (token.ident == Id.aligned) { - dllimport = true; + nextToken(); + if (token.value == TOK.leftParenthesis) + { + nextToken(); + if (token.value == TOK.int32Literal) + { + const n = token.unsvalue; + if (n < 1 || n & (n - 1) || ushort.max < n) + error("__attribute__((aligned(%lld))) must be an integer positive power of 2 and be <= 32,768", cast(ulong)n); + specifier.packalign.set(cast(uint)n); + specifier.packalign.setPack(true); + nextToken(); + } + else + { + error("alignment value expected, not `%s`", token.toChars()); + nextToken(); + } + + check(TOK.rightParenthesis); + } + /* ignore __attribute__((aligned)), which sets the alignment to the largest value for any data + * type on the target machine. It's the opposite of __attribute__((packed)) + */ + } + else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html + { + specifier.scw |= SCW.xinline; + nextToken(); + } + else if (token.ident == Id._deprecated) + { + specifier._deprecated = true; + nextToken(); + if (token.value == TOK.leftParenthesis) // optional deprecation message + { + nextToken(); + specifier.depMsg = cparseExpression(); + check(TOK.rightParenthesis); + } + } + else if (token.ident == Id.dllimport) + { + specifier.dllimport = true; nextToken(); } else if (token.ident == Id.dllexport) { - dllexport = true; + specifier.dllexport = true; + nextToken(); + } + else if (token.ident == Id.naked) + { + specifier.naked = true; + nextToken(); + } + else if (token.ident == Id.noinline) + { + specifier.scw |= SCW.xnoinline; nextToken(); } else if (token.ident == Id.noreturn) @@ -3251,6 +3540,16 @@ final class CParser(AST) : Parser!AST specifier.noreturn = true; nextToken(); } + else if (token.ident == Id._nothrow) + { + specifier._nothrow = true; + nextToken(); + } + else if (token.ident == Id._pure) + { + specifier._pure = true; + nextToken(); + } else if (token.ident == Id.vector_size) { nextToken(); @@ -3411,7 +3710,8 @@ final class CParser(AST) : Parser!AST * https://en.cppreference.com/w/cpp/language/enum * enum Identifier : Type */ - AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type + //AST.Type base = AST.Type.tint32; // C11 6.7.2.2-4 implementation defined default base type + AST.Type base = null; // C23 says base type is determined by enum member values if (token.value == TOK.colon) { nextToken(); @@ -3491,7 +3791,7 @@ final class CParser(AST) : Parser!AST * redeclaration, or reference to existing declaration. * Defer to the semantic() pass with a TypeTag. */ - return new AST.TypeTag(loc, TOK.enum_, tag, base, members); + return new AST.TypeTag(loc, TOK.enum_, tag, structalign_t.init, base, members); } /************************************* @@ -3513,11 +3813,12 @@ final class CParser(AST) : Parser!AST * Params: * loc = location of `struct` or `union` * structOrUnion = TOK.struct_ or TOK.union_ + * packalign = alignment to use for struct members * symbols = symbols to add struct-or-union declaration to * Returns: * type of the struct */ - private AST.Type cparseStruct(Loc loc, TOK structOrUnion, ref AST.Dsymbols* symbols) + private AST.Type cparseStruct(Loc loc, TOK structOrUnion, structalign_t packalign, ref AST.Dsymbols* symbols) { Identifier tag; @@ -3556,7 +3857,7 @@ final class CParser(AST) : Parser!AST * redeclaration, or reference to existing declaration. * Defer to the semantic() pass with a TypeTag. */ - return new AST.TypeTag(loc, structOrUnion, tag, null, members); + return new AST.TypeTag(loc, structOrUnion, tag, packalign, null, members); } /************************************* @@ -3999,6 +4300,13 @@ final class CParser(AST) : Parser!AST case TOK.union_: case TOK.enum_: t = peek(t); + if (t.value == TOK.__attribute__ || + t.value == TOK.__declspec) + { + t = peek(t); + if (!skipParens(t, &t)) + return false; + } if (t.value == TOK.identifier) { t = peek(t); @@ -4022,6 +4330,7 @@ final class CParser(AST) : Parser!AST case TOK.typedef_: case TOK.extern_: case TOK.static_: + case TOK.__thread: case TOK._Thread_local: case TOK.auto_: case TOK.register: @@ -4623,6 +4932,8 @@ final class CParser(AST) : Parser!AST // C11 6.7.4 Function specifiers xinline = 0x40, x_Noreturn = 0x80, + + xnoinline = 0x100, } /// C11 6.7.3 Type qualifiers @@ -4642,6 +4953,14 @@ final class CParser(AST) : Parser!AST struct Specifier { bool noreturn; /// noreturn attribute + bool naked; /// naked attribute + bool _nothrow; /// nothrow attribute + bool _pure; /// pure attribute + bool dllimport; /// dllimport attribute + bool dllexport; /// dllexport attribute + bool _deprecated; /// deprecated attribute + AST.Expression depMsg; /// deprecated message + SCW scw; /// storage-class specifiers MOD mod; /// type qualifiers AST.Expressions* alignExps; /// alignment @@ -4665,6 +4984,8 @@ final class CParser(AST) : Parser!AST { if (specifier.scw & SCW.xextern) stc = AST.STC.extern_; + else if (specifier.scw & SCW.xstatic) + stc = AST.STC.static_; } else if (level == LVL.local) { @@ -4716,9 +5037,41 @@ final class CParser(AST) : Parser!AST stc = AST.STC.gshared; } } + if (specifier._deprecated && !specifier.depMsg) + stc |= AST.STC.deprecated_; return stc; } + /*********************** + * Add attributes from Specifier to function + * Params: + * fd = function to apply them to + * specifier = specifiers + */ + void specifiersToFuncDeclaration(AST.FuncDeclaration fd, const ref Specifier specifier) + { + fd.isNaked = specifier.naked; + fd.dllImport = specifier.dllimport; + fd.dllExport = specifier.dllexport; + + if (specifier.scw & SCW.xnoinline) + fd.inlining = PINLINE.never; + else if (specifier.scw & SCW.xinline) + fd.inlining = PINLINE.always; + } + + /*********************** + * Add attributes from Specifier to variable + * Params: + * vd = function to apply them to + * specifier = specifiers + */ + void specifiersToVarDeclaration(AST.VarDeclaration vd, const ref Specifier specifier) + { + vd.dllImport = specifier.dllimport; + vd.dllExport = specifier.dllexport; + } + /*********************** * Return suitable signed integer type for the given size * Params: @@ -4829,7 +5182,7 @@ final class CParser(AST) : Parser!AST auto lenfn = new AST.IntegerExp(loc, fn.length + 1, AST.Type.tuns32); // +1 for terminating 0 auto tfn = new AST.TypeSArray(AST.Type.tchar, lenfn); efn.type = tfn.immutableOf(); - efn.committed = 1; + efn.committed = true; auto sfn = new AST.VarDeclaration(loc, tfn, Id.__func__, ifn, STC.gshared | STC.immutable_); auto e = new AST.DeclarationExp(loc, sfn); return new AST.ExpStatement(loc, e); @@ -4882,6 +5235,17 @@ final class CParser(AST) : Parser!AST private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier) { //printf("applySpecifier() %s\n", s.toChars()); + if (specifier._deprecated) + { + if (specifier.depMsg) + { + // Wrap declaration in a DeprecatedDeclaration + auto decls = new AST.Dsymbols(1); + (*decls)[0] = s; + s = new AST.DeprecatedDeclaration(specifier.depMsg, decls); + } + } + if (specifier.alignExps) { //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign); diff --git a/dmd/cppmangle.d b/dmd/cppmangle.d index 32b38518953..ee1340d6342 100644 --- a/dmd/cppmangle.d +++ b/dmd/cppmangle.d @@ -211,7 +211,7 @@ private final class CppMangleVisitor : Visitor */ void mangleReturnType(TypeFunction preSemantic) { - auto tf = cast(TypeFunction)this.context.res.asFuncDecl().type; + auto tf = this.context.res.asFuncDecl().type.isTypeFunction(); Type rt = preSemantic.nextOf(); // https://issues.dlang.org/show_bug.cgi?id=22739 // auto return type means that rt is null. @@ -347,14 +347,14 @@ private final class CppMangleVisitor : Visitor * * Params: * off = Offset to insert at - * fd = Type of the function to mangle the return type of + * tf = Type of the function to mangle the return type of */ void writeRemainingTags(size_t off, TypeFunction tf) { - scope remainingVisitor = new LeftoverVisitor(&this.abiTags.written); - tf.next.accept(remainingVisitor); + Array!StringExp toWrite; + leftOver(tf, &this.abiTags.written, &toWrite); OutBuffer b2; - foreach (se; remainingVisitor.toWrite) + foreach (se; toWrite) { auto tag = se.peekString(); // We can only insert a slice, and each insert is a memmove, @@ -446,7 +446,15 @@ private final class CppMangleVisitor : Visitor if (this.context.res.dyncast() == DYNCAST.dsymbol) parentti = this.context.res.asFuncDecl().parent.isTemplateInstance(); else - parentti = this.context.res.asType().toDsymbol(null).parent.isTemplateInstance(); + { + auto parent = this.context.res.asType().toDsymbol(null).parent; + parentti = parent.isTemplateInstance(); + // https://issues.dlang.org/show_bug.cgi?id=22760 + // The template instance may sometimes have the form + // S1!int.S1, therefore the above instruction might yield null + if (parentti is null && parent.parent) + parentti = parent.parent.isTemplateInstance(); + } return (*parentti.tiargs)[arg]; }()); scope (exit) this.context.pop(prev); @@ -496,9 +504,9 @@ private final class CppMangleVisitor : Visitor mangle_function(d.isFuncDeclaration()); buf.writestring("EE"); } - else if (e && e.op == EXP.variable && (cast(VarExp)e).var.isVarDeclaration()) + else if (e && e.isVarExp() && e.isVarExp().var.isVarDeclaration()) { - VarDeclaration vd = (cast(VarExp)e).var.isVarDeclaration(); + VarDeclaration vd = e.isVarExp().var.isVarDeclaration(); buf.writeByte('L'); mangle_variable(vd, true); buf.writeByte('E'); @@ -757,9 +765,9 @@ private final class CppMangleVisitor : Visitor bool isIdent_char(Identifier ident, RootObject o) { Type t = isType(o); - if (!t || t.ty != Tstruct) + if (!t || !t.isTypeStruct()) return false; - Dsymbol s = (cast(TypeStruct)t).toDsymbol(null); + Dsymbol s = t.toDsymbol(null); if (s.ident != ident) return false; Dsymbol p = s.toParent(); @@ -1059,7 +1067,7 @@ private final class CppMangleVisitor : Visitor * ::= * ::= */ - TypeFunction tf = cast(TypeFunction)d.type; + TypeFunction tf = d.type.isTypeFunction(); if (TemplateDeclaration ftd = getFuncTemplateDecl(d)) { @@ -1173,7 +1181,7 @@ private final class CppMangleVisitor : Visitor this.context.ti = ti; this.context.fd = d; this.context.res = d; - TypeFunction preSemantic = cast(TypeFunction)d.originalType; + TypeFunction preSemantic = d.originalType.isTypeFunction(); auto nspace = ti.toParent(); if (nspace && nspace.isNspace()) this.writeChained(ti.toParent(), () => source_name(ti, true)); @@ -1347,7 +1355,7 @@ private final class CppMangleVisitor : Visitor auto prev = this.context.push({ TypeFunction tf; if (isDsymbol(this.context.res)) - tf = cast(TypeFunction)this.context.res.asFuncDecl().type; + tf = this.context.res.asFuncDecl().type.isTypeFunction(); else tf = this.context.res.asType().isTypeFunction(); assert(tf); @@ -1391,9 +1399,9 @@ private final class CppMangleVisitor : Visitor */ void headOfType(Type t) { - if (t.ty == Tclass) + if (auto tc = t.isTypeClass()) { - mangleTypeClass(cast(TypeClass)t, true); + mangleTypeClass(tc, true); } else { @@ -1960,7 +1968,7 @@ extern(C++): */ override void visit(TypeIdentifier t) { - auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl; + auto decl = this.context.ti.tempdecl.isTemplateDeclaration(); assert(decl.parameters !is null); auto idx = templateParamIndex(t.ident, decl.parameters); // If not found, default to the post-semantic type @@ -2019,7 +2027,7 @@ extern(C++): { // If the resolved AST has more args than the parse one, // we have default arguments - auto oparams = (cast(TemplateDeclaration)analyzed_ti.tempdecl).origParameters; + auto oparams = analyzed_ti.tempdecl.isTemplateDeclaration().origParameters; foreach (idx, arg; (*oparams)[t.tiargs.length .. $]) { this.context.res = (*analyzed_ti.tiargs)[idx + t.tiargs.length]; @@ -2044,7 +2052,7 @@ extern(C++): assert(t.tiargs !is null); bool needsTa; - auto decl = cast(TemplateDeclaration)this.context.ti.tempdecl; + auto decl = this.context.ti.tempdecl.isTemplateDeclaration(); // Attempt to substitute the template itself auto idx = templateParamIndex(t.name, decl.parameters); if (idx < decl.parameters.length) @@ -2125,12 +2133,13 @@ private void visitObject(V : Visitor)(RootObject o, V this_) /// Helper function to safely get a type out of a `RootObject` private Type asType(RootObject o) { - Type ta = isType(o); + if (Type ta = isType(o)) + return ta; + // When called with context.res as argument, it can be `FuncDeclaration` - if (!ta && o.asFuncDecl()) - ta = (cast(FuncDeclaration)o).type; - assert(ta !is null, o.toString()); - return ta; + if (auto fd = o.asFuncDecl()) + return fd.type; + assert(0); } /// Helper function to safely get a `FuncDeclaration` out of a `RootObject` @@ -2183,12 +2192,12 @@ private extern(C++) final class ComponentVisitor : Visitor case DYNCAST.type: auto t = cast(Type)base; - if (t.ty == Tpointer) - this.tpointer = cast(TypePointer)t; - else if (t.ty == Treference) - this.tref = cast(TypeReference)t; - else if (t.ty == Tident) - this.tident = cast(TypeIdentifier)t; + if (auto tp = t.isTypePointer()) + this.tpointer = tp; + else if (auto tr = t.isTypeReference()) + this.tref = tr; + else if (auto ti = t.isTypeIdentifier()) + this.tident = ti; else goto default; break; @@ -2531,58 +2540,70 @@ unittest assert(closestIndex([s1, s2, s4], s5, match) == 3 && !match); } -/** +/*** * Visits the return type of a function and writes leftover ABI tags + * Params: + * tf = Type of the function to mangle the return type of + * previous = already written ones + * toWrite = where to put StringExp's to be written */ -extern(C++) private final class LeftoverVisitor : Visitor +private +void leftOver(TypeFunction tf, const(Array!StringExp)* previous, Array!StringExp* toWrite) { - /// List of tags to write - private Array!StringExp toWrite; - /// List of tags to ignore - private const(Array!StringExp)* ignore; - - /// - public this(const(Array!StringExp)* previous) + extern(C++) final class LeftoverVisitor : Visitor { - this.ignore = previous; - } + /// List of tags to write + private Array!StringExp* toWrite; + /// List of tags to ignore + private const(Array!StringExp)* ignore; - /// Reintroduce base class overloads - public alias visit = Visitor.visit; + /// + public this(const(Array!StringExp)* previous, Array!StringExp* toWrite) + { + this.ignore = previous; + this.toWrite = toWrite; + } - /// Least specialized overload of each direct child of `RootObject` - public override void visit(Dsymbol o) - { - auto ale = ABITagContainer.forSymbol(o); - if (!ale) return; + /// Reintroduce base class overloads + public alias visit = Visitor.visit; - bool match; - foreach (elem; *ale.elements) + /// Least specialized overload of each direct child of `RootObject` + public override void visit(Dsymbol o) { - auto se = elem.toStringExp(); - closestIndex((*this.ignore)[], se, match); - if (match) continue; - auto idx = closestIndex(this.toWrite[], se, match); - if (!match) - this.toWrite.insert(idx, se); + auto ale = ABITagContainer.forSymbol(o); + if (!ale) return; + + bool match; + foreach (elem; *ale.elements) + { + auto se = elem.toStringExp(); + closestIndex((*this.ignore)[], se, match); + if (match) continue; + auto idx = closestIndex((*this.toWrite)[], se, match); + if (!match) + (*this.toWrite).insert(idx, se); + } } - } - /// Ditto - public override void visit(Type o) - { - if (auto sym = o.toDsymbol(null)) - sym.accept(this); - } + /// Ditto + public override void visit(Type o) + { + if (auto sym = o.toDsymbol(null)) + sym.accept(this); + } - /// Composite type - public override void visit(TypePointer o) - { - o.next.accept(this); - } + /// Composite type + public override void visit(TypePointer o) + { + o.next.accept(this); + } - public override void visit(TypeReference o) - { - o.next.accept(this); + public override void visit(TypeReference o) + { + o.next.accept(this); + } } + + scope remainingVisitor = new LeftoverVisitor(previous, toWrite); + tf.next.accept(remainingVisitor); } diff --git a/dmd/cppmanglewin.d b/dmd/cppmanglewin.d index c59a9a6de1a..a8113c9381b 100644 --- a/dmd/cppmanglewin.d +++ b/dmd/cppmanglewin.d @@ -101,57 +101,37 @@ private extern (D) bool checkImmutableShared(Type type, Loc loc) private final class VisualCPPMangler : Visitor { - enum VC_SAVED_TYPE_CNT = 10u; - enum VC_SAVED_IDENT_CNT = 10u; - alias visit = Visitor.visit; - Identifier[VC_SAVED_IDENT_CNT] saved_idents; - Type[VC_SAVED_TYPE_CNT] saved_types; - Loc loc; /// location for use in error messages - - // IS_NOT_TOP_TYPE: when we mangling one argument, we can call visit several times (for base types of arg type) - // but we must save only arg type: - // For example: if we have an int** argument, we should save "int**" but visit will be called for "int**", "int*", "int" - // This flag is set up by the visit(NextType, ) function and should be reset when the arg type output is finished. - // MANGLE_RETURN_TYPE: return type shouldn't be saved and substituted in arguments - // IGNORE_CONST: in some cases we should ignore CV-modifiers. - // ESCAPE: toplevel const non-pointer types need a '$$C' escape in addition to a cv qualifier. - - enum Flags : int - { - IS_NOT_TOP_TYPE = 0x1, - MANGLE_RETURN_TYPE = 0x2, - IGNORE_CONST = 0x4, - IS_DMC = 0x8, - ESCAPE = 0x10, - } + Identifier[10] saved_idents; + Type[10] saved_types; + Loc loc; /// location for use in error messages + + bool isNotTopType; /** When mangling one argument, we can call visit several times (for base types of arg type) + * but must save only arg type: + * For example: if we have an int** argument, we should save "int**" but visit will be called for "int**", "int*", "int" + * This flag is set up by the visit(NextType, ) function and should be reset when the arg type output is finished. + */ + bool ignoreConst; /// in some cases we should ignore CV-modifiers. + bool escape; /// toplevel const non-pointer types need a '$$C' escape in addition to a cv qualifier. + bool mangleReturnType; /// return type shouldn't be saved and substituted in arguments + bool isDmc; /// Digital Mars C++ name mangling - alias IS_NOT_TOP_TYPE = Flags.IS_NOT_TOP_TYPE; - alias MANGLE_RETURN_TYPE = Flags.MANGLE_RETURN_TYPE; - alias IGNORE_CONST = Flags.IGNORE_CONST; - alias IS_DMC = Flags.IS_DMC; - alias ESCAPE = Flags.ESCAPE; - - int flags; OutBuffer buf; extern (D) this(VisualCPPMangler rvl) scope { - flags |= (rvl.flags & IS_DMC); saved_idents[] = rvl.saved_idents[]; - saved_types[] = rvl.saved_types[]; - loc = rvl.loc; + saved_types[] = rvl.saved_types[]; + isDmc = rvl.isDmc; + loc = rvl.loc; } public: - extern (D) this(bool isdmc, Loc loc) scope + extern (D) this(bool isDmc, Loc loc) scope { - if (isdmc) - { - flags |= IS_DMC; - } saved_idents[] = null; saved_types[] = null; + this.isDmc = isDmc; this.loc = loc; } @@ -172,8 +152,8 @@ public: return; buf.writestring("$$T"); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeNoreturn type) @@ -184,17 +164,17 @@ public: return; buf.writeByte('X'); // yes, mangle it like `void` - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeBasic type) { - //printf("visit(TypeBasic); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeBasic); is_not_top_type = %d\n", isNotTopType); if (checkImmutableShared(type, loc)) return; - if (type.isConst() && ((flags & IS_NOT_TOP_TYPE) || (flags & IS_DMC))) + if (type.isConst() && (isNotTopType || isDmc)) { if (checkTypeSaved(type)) return; @@ -203,7 +183,7 @@ public: { return; } - if (!(flags & IS_DMC)) + if (!isDmc) { switch (type.ty) { @@ -275,7 +255,7 @@ version (IN_LLVM) } else { - if (flags & IS_DMC) + if (isDmc) buf.writestring("_Z"); // DigitalMars long double else buf.writestring("_T"); // Intel long double @@ -297,33 +277,33 @@ else visit(cast(Type)type); return; } - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeVector type) { - //printf("visit(TypeVector); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeVector); is_not_top_type = %d\n", isNotTopType); if (checkTypeSaved(type)) return; mangleModifier(type); buf.writestring("T__m128@@"); // may be better as __m128i or __m128d? - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeSArray type) { // This method can be called only for static variable type mangling. - //printf("visit(TypeSArray); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeSArray); is_not_top_type = %d\n", isNotTopType); if (checkTypeSaved(type)) return; // first dimension always mangled as const pointer - if (flags & IS_DMC) + if (isDmc) buf.writeByte('Q'); else buf.writeByte('P'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; assert(type.next); if (type.next.ty == Tsarray) { @@ -339,7 +319,7 @@ else // There is not way to map int C++ (*arr)[2][1] to D override void visit(TypePointer type) { - //printf("visit(TypePointer); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypePointer); is_not_top_type = %d\n", isNotTopType); if (checkImmutableShared(type, loc)) return; @@ -359,8 +339,8 @@ else buf.writeByte('P'); // mutable buf.writeByte('6'); // pointer to a function buf.writestring(arg); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; return; } else if (type.next.ty == Tsarray) @@ -368,13 +348,13 @@ else if (checkTypeSaved(type)) return; mangleModifier(type); - if (type.isConst() || !(flags & IS_DMC)) + if (type.isConst() || !isDmc) buf.writeByte('Q'); // const else buf.writeByte('P'); // mutable if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; mangleArray(cast(TypeSArray)type.next); return; } @@ -393,7 +373,7 @@ else } if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; type.next.accept(this); } } @@ -410,7 +390,7 @@ else buf.writeByte('A'); // mutable if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; assert(type.next); if (type.next.ty == Tsarray) { @@ -425,7 +405,7 @@ else override void visit(TypeFunction type) { const(char)* arg = mangleFunctionType(type); - if ((flags & IS_DMC)) + if (isDmc) { if (checkTypeSaved(type)) return; @@ -435,14 +415,15 @@ else buf.writestring("$$A6"); } buf.writestring(arg); - flags &= ~(IS_NOT_TOP_TYPE | IGNORE_CONST); + isNotTopType = false; + ignoreConst = false; } override void visit(TypeStruct type) { if (checkTypeSaved(type)) return; - //printf("visit(TypeStruct); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeStruct); is_not_top_type = %d\n", isNotTopType); mangleModifier(type); const agg = type.sym.isStructDeclaration(); if (type.sym.isUnionDeclaration()) @@ -450,13 +431,13 @@ else else buf.writeByte(agg.cppmangle == CPPMANGLE.asClass ? 'V' : 'U'); mangleIdent(type.sym); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } override void visit(TypeEnum type) { - //printf("visit(TypeEnum); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeEnum); is_not_top_type = %d\n", cast(int)(flags & isNotTopType)); const id = type.sym.ident; string c; if (id == Id.__c_long_double) @@ -473,7 +454,7 @@ else c = "D"; // VC++ char else if (id == Id.__c_wchar_t) { - c = (flags & IS_DMC) ? "_Y" : "_W"; + c = isDmc ? "_Y" : "_W"; } if (c.length) @@ -481,7 +462,7 @@ else if (checkImmutableShared(type, loc)) return; - if (type.isConst() && ((flags & IS_NOT_TOP_TYPE) || (flags & IS_DMC))) + if (type.isConst() && (isNotTopType || isDmc)) { if (checkTypeSaved(type)) return; @@ -497,18 +478,18 @@ else buf.writestring("W4"); mangleIdent(type.sym); } - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } // D class mangled as pointer to C++ class // const(Object) mangled as Object const* const override void visit(TypeClass type) { - //printf("visit(TypeClass); is_not_top_type = %d\n", cast(int)(flags & IS_NOT_TOP_TYPE)); + //printf("visit(TypeClass); is_not_top_type = %d\n", isNotTopType); if (checkTypeSaved(type)) return; - if (flags & IS_NOT_TOP_TYPE) + if (isNotTopType) mangleModifier(type); if (type.isConst()) buf.writeByte('Q'); @@ -516,13 +497,13 @@ else buf.writeByte('P'); if (target.isLP64) buf.writeByte('E'); - flags |= IS_NOT_TOP_TYPE; + isNotTopType = true; mangleModifier(type); const cldecl = type.sym.isClassDeclaration(); buf.writeByte(cldecl.cppmangle == CPPMANGLE.asStruct ? 'U' : 'V'); mangleIdent(type.sym); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + isNotTopType = false; + ignoreConst = false; } const(char)* mangleOf(Dsymbol s) @@ -547,22 +528,6 @@ else private: extern(D): - void mangleVisibility(Declaration d, string privProtDef) - { - switch (d.visibility.kind) - { - case Visibility.Kind.private_: - buf.writeByte(privProtDef[0]); - break; - case Visibility.Kind.protected_: - buf.writeByte(privProtDef[1]); - break; - default: - buf.writeByte(privProtDef[2]); - break; - } - } - void mangleFunction(FuncDeclaration d) { // ? @@ -576,11 +541,11 @@ extern(D): //d.toChars(), d.isVirtualMethod(), d.isVirtual(), cast(int)d.vtblIndex, d.interfaceVirtual); if ((d.isVirtual() && (d.vtblIndex != -1 || d.interfaceVirtual || d.overrideInterface())) || (d.isDtorDeclaration() && d.parent.isClassDeclaration() && !d.isFinal())) { - mangleVisibility(d, "EMU"); + mangleVisibility(buf, d, "EMU"); } else { - mangleVisibility(d, "AIQ"); + mangleVisibility(buf, d, "AIQ"); } if (target.isLP64) buf.writeByte('E'); @@ -596,7 +561,7 @@ extern(D): else if (d.isMember2()) // static function { // ::= - mangleVisibility(d, "CKS"); + mangleVisibility(buf, d, "CKS"); } else // top-level function { @@ -631,7 +596,7 @@ extern(D): } else { - mangleVisibility(d, "012"); + mangleVisibility(buf, d, "012"); } Type t = d.type; @@ -649,141 +614,6 @@ extern(D): buf.writeByte(cv_mod); } - /** - * Computes mangling for symbols with special mangling. - * Params: - * sym = symbol to mangle - * Returns: - * mangling for special symbols, - * null if not a special symbol - */ - static string mangleSpecialName(Dsymbol sym) - { - string mangle; - if (sym.isCtorDeclaration()) - mangle = "?0"; - else if (sym.isAggregateDtor()) - mangle = "?1"; - else if (!sym.ident) - return null; - else if (sym.ident == Id.assign) - mangle = "?4"; - else if (sym.ident == Id.eq) - mangle = "?8"; - else if (sym.ident == Id.index) - mangle = "?A"; - else if (sym.ident == Id.call) - mangle = "?R"; - else if (sym.ident == Id.cppdtor) - mangle = "?_G"; - else - return null; - - return mangle; - } - - /** - * Mangles an operator, if any - * - * Params: - * ti = associated template instance of the operator - * symName = symbol name - * firstTemplateArg = index if the first argument of the template (because the corresponding c++ operator is not a template) - * Returns: - * true if sym has no further mangling needed - * false otherwise - */ - bool mangleOperator(TemplateInstance ti, ref const(char)[] symName, ref int firstTemplateArg) - { - auto whichOp = isCppOperator(ti.name); - final switch (whichOp) - { - case CppOperator.Unknown: - return false; - case CppOperator.Cast: - buf.writestring("?B"); - return true; - case CppOperator.Assign: - symName = "?4"; - return false; - case CppOperator.Eq: - symName = "?8"; - return false; - case CppOperator.Index: - symName = "?A"; - return false; - case CppOperator.Call: - symName = "?R"; - return false; - - case CppOperator.Unary: - case CppOperator.Binary: - case CppOperator.OpAssign: - TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); - assert(td); - assert(ti.tiargs.length >= 1); - TemplateParameter tp = (*td.parameters)[0]; - TemplateValueParameter tv = tp.isTemplateValueParameter(); - if (!tv || !tv.valType.isString()) - return false; // expecting a string argument to operators! - Expression exp = (*ti.tiargs)[0].isExpression(); - StringExp str = exp.toStringExp(); - switch (whichOp) - { - case CppOperator.Unary: - switch (str.peekString()) - { - case "*": symName = "?D"; goto continue_template; - case "++": symName = "?E"; goto continue_template; - case "--": symName = "?F"; goto continue_template; - case "-": symName = "?G"; goto continue_template; - case "+": symName = "?H"; goto continue_template; - case "~": symName = "?S"; goto continue_template; - default: return false; - } - case CppOperator.Binary: - switch (str.peekString()) - { - case ">>": symName = "?5"; goto continue_template; - case "<<": symName = "?6"; goto continue_template; - case "*": symName = "?D"; goto continue_template; - case "-": symName = "?G"; goto continue_template; - case "+": symName = "?H"; goto continue_template; - case "&": symName = "?I"; goto continue_template; - case "/": symName = "?K"; goto continue_template; - case "%": symName = "?L"; goto continue_template; - case "^": symName = "?T"; goto continue_template; - case "|": symName = "?U"; goto continue_template; - default: return false; - } - case CppOperator.OpAssign: - switch (str.peekString()) - { - case "*": symName = "?X"; goto continue_template; - case "+": symName = "?Y"; goto continue_template; - case "-": symName = "?Z"; goto continue_template; - case "/": symName = "?_0"; goto continue_template; - case "%": symName = "?_1"; goto continue_template; - case ">>": symName = "?_2"; goto continue_template; - case "<<": symName = "?_3"; goto continue_template; - case "&": symName = "?_4"; goto continue_template; - case "|": symName = "?_5"; goto continue_template; - case "^": symName = "?_6"; goto continue_template; - default: return false; - } - default: assert(0); - } - } - continue_template: - if (ti.tiargs.length == 1) - { - buf.writestring(symName); - return true; - } - firstTemplateArg = 1; - return false; - } - /** * Mangles a template value * @@ -806,13 +636,13 @@ extern(D): assert(e); if (tv.valType.isunsigned()) { - mangleNumber(e.toUInteger()); + mangleNumber(buf, e.toUInteger()); } else if (is_dmc_template) { // NOTE: DMC mangles everything based on // unsigned int - mangleNumber(e.toInteger()); + mangleNumber(buf, e.toInteger()); } else { @@ -822,7 +652,7 @@ extern(D): val = -val; buf.writeByte('?'); } - mangleNumber(val); + mangleNumber(buf, val); } } @@ -846,7 +676,7 @@ extern(D): else if (e && e.op == EXP.variable && (cast(VarExp)e).var.isVarDeclaration()) { buf.writeByte('$'); - if (flags & IS_DMC) + if (isDmc) buf.writeByte('1'); else buf.writeByte('E'); @@ -855,7 +685,7 @@ extern(D): else if (d && d.isTemplateDeclaration() && d.isTemplateDeclaration().onemember) { Dsymbol ds = d.isTemplateDeclaration().onemember; - if (flags & IS_DMC) + if (isDmc) { buf.writeByte('V'); } @@ -896,11 +726,11 @@ extern(D): */ void mangleTemplateType(RootObject o) { - flags |= ESCAPE; + escape = true; Type t = isType(o); assert(t); t.accept(this); - flags &= ~ESCAPE; + escape = false; } /** @@ -952,7 +782,7 @@ extern(D): int firstTemplateArg = 0; // test for special symbols - if (mangleOperator(ti,symName,firstTemplateArg)) + if (mangleOperator(buf, ti,symName,firstTemplateArg)) return; TemplateInstance actualti = ti; bool needNamespaces; @@ -986,14 +816,14 @@ extern(D): } } - scope VisualCPPMangler tmp = new VisualCPPMangler((flags & IS_DMC) ? true : false, loc); + scope VisualCPPMangler tmp = new VisualCPPMangler(isDmc ? true : false, loc); tmp.buf.writeByte('?'); tmp.buf.writeByte('$'); tmp.buf.writestring(symName); tmp.saved_idents[0] = id; if (symName == id.toString()) tmp.buf.writeByte('@'); - if (flags & IS_DMC) + if (isDmc) { tmp.mangleIdent(sym.parent, true); is_dmc_template = true; @@ -1054,16 +884,16 @@ extern(D): // returns true if name already saved bool checkAndSaveIdent(Identifier name) { - foreach (i; 0 .. VC_SAVED_IDENT_CNT) + foreach (i, ref id; saved_idents) { - if (!saved_idents[i]) // no saved same name + if (!id) // no saved same name { - saved_idents[i] = name; + id = name; break; } - if (saved_idents[i] == name) // ok, we've found same name. use index instead of name + if (id == name) // ok, we've found same name. use index instead of name { - buf.writeByte(i + '0'); + buf.writeByte(cast(uint)i + '0'); return true; } } @@ -1072,14 +902,14 @@ extern(D): void saveIdent(Identifier name) { - foreach (i; 0 .. VC_SAVED_IDENT_CNT) + foreach (ref id; saved_idents) { - if (!saved_idents[i]) // no saved same name + if (!id) // no saved same name { - saved_idents[i] = name; + id = name; break; } - if (saved_idents[i] == name) // ok, we've found same name. use index instead of name + if (id == name) // ok, we've found same name. use index instead of name { return; } @@ -1122,50 +952,24 @@ extern(D): buf.writeByte('@'); } - void mangleNumber(dinteger_t num) - { - if (!num) // 0 encoded as "A@" - { - buf.writeByte('A'); - buf.writeByte('@'); - return; - } - if (num <= 10) // 5 encoded as "4" - { - buf.writeByte(cast(char)(num - 1 + '0')); - return; - } - char[17] buff; - buff[16] = 0; - size_t i = 16; - while (num) - { - --i; - buff[i] = num % 16 + 'A'; - num /= 16; - } - buf.writestring(&buff[i]); - buf.writeByte('@'); - } - bool checkTypeSaved(Type type) { - if (flags & IS_NOT_TOP_TYPE) + if (isNotTopType) return false; - if (flags & MANGLE_RETURN_TYPE) + if (mangleReturnType) return false; - for (uint i = 0; i < VC_SAVED_TYPE_CNT; i++) + foreach (i, ref ty; saved_types) { - if (!saved_types[i]) // no saved same type + if (!ty) // no saved same type { - saved_types[i] = type; + ty = type; return false; } - if (saved_types[i].equals(type)) // ok, we've found same type. use index instead of type + if (ty.equals(type)) // ok, we've found same type. use index instead of type { - buf.writeByte(i + '0'); - flags &= ~IS_NOT_TOP_TYPE; - flags &= ~IGNORE_CONST; + buf.writeByte(cast(uint)i + '0'); + isNotTopType = false; + ignoreConst = false; return true; } } @@ -1174,7 +978,7 @@ extern(D): void mangleModifier(Type type) { - if (flags & IGNORE_CONST) + if (ignoreConst) return; if (checkImmutableShared(type, loc)) return; @@ -1183,17 +987,17 @@ extern(D): { // Template parameters that are not pointers and are const need an $$C escape // in addition to 'B' (const). - if ((flags & ESCAPE) && type.ty != Tpointer) + if (escape && type.ty != Tpointer) buf.writestring("$$CB"); - else if (flags & IS_NOT_TOP_TYPE) + else if (isNotTopType) buf.writeByte('B'); // const - else if ((flags & IS_DMC) && type.ty != Tpointer) + else if (isDmc && type.ty != Tpointer) buf.writestring("_O"); } - else if (flags & IS_NOT_TOP_TYPE) + else if (isNotTopType) buf.writeByte('A'); // mutable - flags &= ~ESCAPE; + escape = false; } void mangleArray(TypeSArray type) @@ -1207,15 +1011,15 @@ extern(D): cur = cur.nextOf(); } buf.writeByte('Y'); - mangleNumber(i); // count of dimensions + mangleNumber(buf, i); // count of dimensions cur = type; while (cur && cur.ty == Tsarray) // sizes of dimensions { TypeSArray sa = cast(TypeSArray)cur; - mangleNumber(sa.dim ? sa.dim.toInteger() : 0); + mangleNumber(buf, sa.dim ? sa.dim.toInteger() : 0); cur = cur.nextOf(); } - flags |= IGNORE_CONST; + ignoreConst = true; cur.accept(this); } @@ -1252,7 +1056,7 @@ extern(D): assert(0); } } - tmp.flags &= ~IS_NOT_TOP_TYPE; + tmp.isNotTopType = false; if (noreturn) { tmp.buf.writeByte('@'); @@ -1262,7 +1066,7 @@ extern(D): Type rettype = type.next; if (type.isref) rettype = rettype.referenceTo(); - flags &= ~IGNORE_CONST; + ignoreConst = false; if (rettype.ty == Tstruct) { tmp.buf.writeByte('?'); @@ -1277,9 +1081,9 @@ extern(D): tmp.buf.writeByte('A'); } } - tmp.flags |= MANGLE_RETURN_TYPE; + tmp.mangleReturnType = true; rettype.accept(tmp); - tmp.flags &= ~MANGLE_RETURN_TYPE; + tmp.mangleReturnType = false; } if (!type.parameterList.parameters || !type.parameterList.parameters.length) { @@ -1310,8 +1114,8 @@ extern(D): errorSupplemental(loc, "Use pointer instead."); assert(0); } - tmp.flags &= ~IS_NOT_TOP_TYPE; - tmp.flags &= ~IGNORE_CONST; + tmp.isNotTopType = false; + ignoreConst = false; t.accept(tmp); } @@ -1331,3 +1135,188 @@ extern(D): return ret; } } + +private: +extern(D): + +/** + * Computes mangling for symbols with special mangling. + * Params: + * sym = symbol to mangle + * Returns: + * mangling for special symbols, + * null if not a special symbol + */ +string mangleSpecialName(Dsymbol sym) +{ + string mangle; + if (sym.isCtorDeclaration()) + mangle = "?0"; + else if (sym.isAggregateDtor()) + mangle = "?1"; + else if (!sym.ident) + return null; + else if (sym.ident == Id.assign) + mangle = "?4"; + else if (sym.ident == Id.eq) + mangle = "?8"; + else if (sym.ident == Id.index) + mangle = "?A"; + else if (sym.ident == Id.call) + mangle = "?R"; + else if (sym.ident == Id.cppdtor) + mangle = "?_G"; + else + return null; + + return mangle; +} + +/** + * Mangles an operator, if any + * + * Params: + * buf = buffer to write mangling to + * ti = associated template instance of the operator + * symName = symbol name + * firstTemplateArg = index if the first argument of the template (because the corresponding c++ operator is not a template) + * Returns: + * true if sym has no further mangling needed + * false otherwise + */ +bool mangleOperator(ref OutBuffer buf, TemplateInstance ti, ref const(char)[] symName, ref int firstTemplateArg) +{ + auto whichOp = isCppOperator(ti.name); + final switch (whichOp) + { + case CppOperator.Unknown: + return false; + case CppOperator.Cast: + buf.writestring("?B"); + return true; + case CppOperator.Assign: + symName = "?4"; + return false; + case CppOperator.Eq: + symName = "?8"; + return false; + case CppOperator.Index: + symName = "?A"; + return false; + case CppOperator.Call: + symName = "?R"; + return false; + + case CppOperator.Unary: + case CppOperator.Binary: + case CppOperator.OpAssign: + TemplateDeclaration td = ti.tempdecl.isTemplateDeclaration(); + assert(td); + assert(ti.tiargs.length >= 1); + TemplateParameter tp = (*td.parameters)[0]; + TemplateValueParameter tv = tp.isTemplateValueParameter(); + if (!tv || !tv.valType.isString()) + return false; // expecting a string argument to operators! + Expression exp = (*ti.tiargs)[0].isExpression(); + StringExp str = exp.toStringExp(); + switch (whichOp) + { + case CppOperator.Unary: + switch (str.peekString()) + { + case "*": symName = "?D"; goto continue_template; + case "++": symName = "?E"; goto continue_template; + case "--": symName = "?F"; goto continue_template; + case "-": symName = "?G"; goto continue_template; + case "+": symName = "?H"; goto continue_template; + case "~": symName = "?S"; goto continue_template; + default: return false; + } + case CppOperator.Binary: + switch (str.peekString()) + { + case ">>": symName = "?5"; goto continue_template; + case "<<": symName = "?6"; goto continue_template; + case "*": symName = "?D"; goto continue_template; + case "-": symName = "?G"; goto continue_template; + case "+": symName = "?H"; goto continue_template; + case "&": symName = "?I"; goto continue_template; + case "/": symName = "?K"; goto continue_template; + case "%": symName = "?L"; goto continue_template; + case "^": symName = "?T"; goto continue_template; + case "|": symName = "?U"; goto continue_template; + default: return false; + } + case CppOperator.OpAssign: + switch (str.peekString()) + { + case "*": symName = "?X"; goto continue_template; + case "+": symName = "?Y"; goto continue_template; + case "-": symName = "?Z"; goto continue_template; + case "/": symName = "?_0"; goto continue_template; + case "%": symName = "?_1"; goto continue_template; + case ">>": symName = "?_2"; goto continue_template; + case "<<": symName = "?_3"; goto continue_template; + case "&": symName = "?_4"; goto continue_template; + case "|": symName = "?_5"; goto continue_template; + case "^": symName = "?_6"; goto continue_template; + default: return false; + } + default: assert(0); + } + } + continue_template: + if (ti.tiargs.length == 1) + { + buf.writestring(symName); + return true; + } + firstTemplateArg = 1; + return false; +} + +/**********************************' + */ +void mangleNumber(ref OutBuffer buf, dinteger_t num) +{ + if (!num) // 0 encoded as "A@" + { + buf.writeByte('A'); + buf.writeByte('@'); + return; + } + if (num <= 10) // 5 encoded as "4" + { + buf.writeByte(cast(char)(num - 1 + '0')); + return; + } + char[17] buff = void; + buff[16] = 0; + size_t i = 16; + while (num) + { + --i; + buff[i] = num % 16 + 'A'; + num /= 16; + } + buf.writestring(&buff[i]); + buf.writeByte('@'); +} + +/************************************* + */ +void mangleVisibility(ref OutBuffer buf, Declaration d, string privProtDef) +{ + switch (d.visibility.kind) + { + case Visibility.Kind.private_: + buf.writeByte(privProtDef[0]); + break; + case Visibility.Kind.protected_: + buf.writeByte(privProtDef[1]); + break; + default: + buf.writeByte(privProtDef[2]); + break; + } +} diff --git a/dmd/ctfeexpr.d b/dmd/ctfeexpr.d index 8109e12d43d..289ebeb81ca 100644 --- a/dmd/ctfeexpr.d +++ b/dmd/ctfeexpr.d @@ -47,7 +47,7 @@ extern (C++) final class ClassReferenceExp : Expression extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) { - super(loc, EXP.classReference, __traits(classInstanceSize, ClassReferenceExp)); + super(loc, EXP.classReference); assert(lit && lit.sd && lit.sd.isClassDeclaration()); this.value = lit; this.type = type; @@ -132,7 +132,7 @@ extern (C++) final class ThrownExceptionExp : Expression extern (D) this(const ref Loc loc, ClassReferenceExp victim) { - super(loc, EXP.thrownException, __traits(classInstanceSize, ThrownExceptionExp)); + super(loc, EXP.thrownException); this.thrown = victim; this.type = victim.type; } @@ -170,7 +170,7 @@ extern (C++) final class CTFEExp : Expression { extern (D) this(EXP tok) { - super(Loc.initial, tok, __traits(classInstanceSize, CTFEExp)); + super(Loc.initial, tok); type = Type.tvoid; } @@ -369,7 +369,6 @@ UnionExp copyLiteral(Expression e) case EXP.dotVariable: case EXP.int64: case EXP.float64: - case EXP.char_: case EXP.complex80: case EXP.void_: case EXP.vector: @@ -1468,7 +1467,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) memset(cast(char*)s + len * sz, 0, sz); emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); StringExp es = ue.exp().isStringExp(); - es.committed = 0; + es.committed = false; es.type = type; return ue; } @@ -1499,7 +1498,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(StringExp)(&ue, loc, s[0 .. len * sz], len, sz); StringExp es = ue.exp().isStringExp(); es.sz = sz; - es.committed = 0; //es1.committed; + es.committed = false; //es1.committed; es.type = type; return ue; } @@ -1837,7 +1836,6 @@ bool isCtfeValueValid(Expression newval) { case EXP.int64: case EXP.float64: - case EXP.char_: case EXP.complex80: return tb.isscalar(); diff --git a/dmd/dcast.d b/dmd/dcast.d index 2830b25d651..6fcc2806585 100644 --- a/dmd/dcast.d +++ b/dmd/dcast.d @@ -71,6 +71,8 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) { + // no need for an extra cast when matching is exact + if (match == MATCH.convert && e.type.isTypeNoreturn()) { return specialNoreturnCast(e, t); @@ -88,6 +90,8 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) auto ad = isAggregate(e.type); if (ad && ad.aliasthis) { + if (!ad.type || ad.type.isTypeError()) + return e; auto ts = ad.type.isTypeStruct(); const adMatch = ts ? ts.implicitConvToWithoutAliasThis(t) @@ -1845,7 +1849,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) if (!e.committed) { se = e.copy().isStringExp(); - se.committed = 1; + se.committed = true; copied = 1; } @@ -1887,7 +1891,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) assert(szx <= 255); se.sz = cast(ubyte)szx; se.len = cast(size_t)tb.isTypeSArray().dim.toInteger(); - se.committed = 1; + se.committed = true; se.type = t; /* If larger than source, pad with zeros. diff --git a/dmd/declaration.d b/dmd/declaration.d index e87bd3baa53..a92d3fb2c42 100644 --- a/dmd/declaration.d +++ b/dmd/declaration.d @@ -747,7 +747,7 @@ extern (C++) final class AliasDeclaration : Declaration extern (D) this(const ref Loc loc, Identifier ident, Type type) { super(loc, ident); - //printf("AliasDeclaration(id = '%s', type = %p)\n", id.toChars(), type); + //printf("AliasDeclaration(id = '%s', type = %p)\n", ident.toChars(), type); //printf("type = '%s'\n", type.toChars()); this.type = type; assert(type); @@ -756,7 +756,7 @@ extern (C++) final class AliasDeclaration : Declaration extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s) { super(loc, ident); - //printf("AliasDeclaration(id = '%s', s = %p)\n", id.toChars(), s); + //printf("AliasDeclaration(id = '%s', s = %p)\n", ident.toChars(), s); assert(s != this); this.aliassym = s; assert(s); @@ -1162,6 +1162,8 @@ version (IN_LLVM) bool isArgDtorVar; /// temporary created to handle scope destruction of a function argument bool isCmacro; /// it is a C macro turned into a C declaration + bool dllImport; /// __declspec(dllimport) + bool dllExport; /// __declspec(dllexport) version (MARS) { bool inClosure; /// is inserted into a GC allocated closure @@ -1327,7 +1329,7 @@ version (IN_LLVM) override final bool isExport() const { - return visibility.kind == Visibility.Kind.export_; + return visibility.kind == Visibility.Kind.export_ || dllExport; } override final bool isImportedSymbol() const @@ -1338,6 +1340,7 @@ version (IN_LLVM) * export extern int sym3 = 0; // error, extern cannot have initializer */ bool result = + dllImport || visibility.kind == Visibility.Kind.export_ && storage_class & STC.extern_ && (storage_class & STC.static_ || parent.isModule()); @@ -1944,8 +1947,12 @@ extern (C++) class BitFieldDeclaration : VarDeclaration { // If the bit-field spans more units of alignment than its type, // start a new field at the next alignment boundary. - if (fieldState.bitOffset == fieldState.fieldSize * 8) + if (fieldState.bitOffset == fieldState.fieldSize * 8 && + fieldState.bitOffset + fieldWidth > memalignsize * 8) + { + if (log) printf("more units of alignment than its type\n"); startNewField(); // the bit field is full + } else { // if alignment boundary is crossed @@ -1954,7 +1961,7 @@ extern (C++) class BitFieldDeclaration : VarDeclaration //printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize); if (start / (memalignsize * 8) != (end - 1) / (memalignsize * 8)) { - //printf("alignment is crossed\n"); + if (log) printf("alignment is crossed\n"); startNewField(); } } @@ -1994,6 +2001,7 @@ extern (C++) class BitFieldDeclaration : VarDeclaration fieldState.bitOffset = pastField; } + //printf("\t%s: offset = %d bitOffset = %d fieldWidth = %d memsize = %d\n", toChars(), offset, bitOffset, fieldWidth, memsize); //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize); //printf(" addField '%s' to '%s' at offset %d, size = %d\n", toChars(), ad.toChars(), offset, memsize); } diff --git a/dmd/declaration.h b/dmd/declaration.h index b4b78af9ce8..6b1677d03d3 100644 --- a/dmd/declaration.h +++ b/dmd/declaration.h @@ -169,8 +169,8 @@ class TupleDeclaration final : public Declaration public: Objects *objects; TypeTuple *tupletype; // !=NULL if this is a type tuple - bool isexp; // true: expression tuple - bool building; // it's growing in AliasAssign semantic + d_bool isexp; // true: expression tuple + d_bool building; // it's growing in AliasAssign semantic TupleDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; @@ -637,7 +637,7 @@ class FuncDeclaration : public Declaration // set if someone took the address of this function int tookAddressOf; - bool requiresClosure; // this function needs a closure + d_bool requiresClosure; // this function needs a closure // local variables in this function which are referenced by nested functions VarDeclarations closureVars; @@ -653,6 +653,9 @@ class FuncDeclaration : public Declaration FuncDeclarations *inlinedNestedCallees; AttributeViolation* safetyViolation; + AttributeViolation* nogcViolation; + AttributeViolation* pureViolation; + AttributeViolation* nothrowViolation; // Formerly FUNCFLAGS uint32_t flags; @@ -702,6 +705,10 @@ class FuncDeclaration : public Declaration bool isCrtCtor(bool v); bool isCrtDtor() const; bool isCrtDtor(bool v); + bool dllImport() const; + bool dllImport(bool v); + bool dllExport() const; + bool dllExport(bool v); // Data for a function declaration that is needed for the Objective-C // integration. @@ -772,7 +779,7 @@ class FuncAliasDeclaration final : public FuncDeclaration { public: FuncDeclaration *funcalias; - bool hasOverloads; + d_bool hasOverloads; FuncAliasDeclaration *isFuncAliasDeclaration() override { return this; } const char *kind() const override; @@ -788,7 +795,7 @@ class FuncLiteralDeclaration final : public FuncDeclaration Type *treq; // target of return type inference // backend - bool deferToObj; + d_bool deferToObj; FuncLiteralDeclaration *syntaxCopy(Dsymbol *) override; bool isNested() const override; @@ -808,7 +815,7 @@ class FuncLiteralDeclaration final : public FuncDeclaration class CtorDeclaration final : public FuncDeclaration { public: - bool isCpCtor; + d_bool isCpCtor; CtorDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; const char *toChars() const override; diff --git a/dmd/denum.d b/dmd/denum.d index a2e4c858e46..62a6e30636a 100644 --- a/dmd/denum.d +++ b/dmd/denum.d @@ -169,7 +169,12 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol return defaultval; } //printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars()); - if (defaultval) + // https://issues.dlang.org/show_bug.cgi?id=23904 + // Return defaultval only if it is not ErrorExp. + // A speculative context may set defaultval to ErrorExp; + // subsequent non-speculative contexts need to be able + // to print the error. + if (defaultval && !defaultval.isErrorExp()) return defaultval; if (isCsymbol()) diff --git a/dmd/dimport.d b/dmd/dimport.d index b653d9bbf89..c4d5ddbc079 100644 --- a/dmd/dimport.d +++ b/dmd/dimport.d @@ -26,6 +26,7 @@ import dmd.location; import dmd.mtype; import dmd.visitor; +import core.stdc.stdio; /*********************************************************** */ extern (C++) final class Import : Dsymbol @@ -232,7 +233,20 @@ extern (C++) final class Import : Dsymbol * most likely because of parsing errors. * Therefore we cannot trust the resulting AST. */ - if (load(sc)) return; + if (load(sc)) + { + // https://issues.dlang.org/show_bug.cgi?id=23873 + // For imports that are not at module or function level, + // e.g. aggregate level, the import symbol is added to the + // symbol table and later semantic is performed on it. + // This leads to semantic analysis on an malformed AST + // which causes all kinds of segfaults. + // The fix is to note that the module has errors and avoid + // semantic analysis on it. + if(mod) + mod.errors = true; + return; + } if (!mod) return; // Failed diff --git a/dmd/dinterpret.d b/dmd/dinterpret.d index ff406296343..bdac73a8ae6 100644 --- a/dmd/dinterpret.d +++ b/dmd/dinterpret.d @@ -673,7 +673,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta e = CTFEExp.cantexp; break; } - e = interpret(pue, fd.fbody, &istatex); + e = interpretStatement(pue, fd.fbody, &istatex); if (CTFEExp.isCantExp(e)) { debug (LOG) @@ -769,21 +769,30 @@ void incUsageCtfe(InterState* istate, const ref Loc loc) } } -private extern (C++) final class Interpreter : Visitor +/*********************************** + * Interpret the statement. + * Params: + * s = Statement to interpret + * istate = context + * Returns: + * NULL continue to next statement + * EXP.cantExpression cannot interpret statement at compile time + * !NULL expression from return statement, or thrown exception + */ + +Expression interpretStatement(Statement s, InterState* istate) { - alias visit = Visitor.visit; -public: - InterState* istate; - CTFEGoal goal; - Expression result; - UnionExp* pue; // storage for `result` + UnionExp ue = void; + auto result = interpretStatement(&ue, s, istate); + if (result == ue.exp()) + result = ue.copy(); + return result; +} - extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal) scope - { - this.pue = pue; - this.istate = istate; - this.goal = goal; - } +/// +Expression interpretStatement(UnionExp* pue, Statement s, InterState* istate) +{ + Expression result; // If e is EXP.throw_exception or EXP.cantExpression, // set it to 'result' and returns true. @@ -798,26 +807,13 @@ public: return false; } - static Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) - { - if (exps is original) - { - if (!original) - exps = new Expressions(); - else - exps = original.copy(); - ++ctfeGlobals.numArrayAllocs; - } - return exps; - } - /******************************** Statement ***************************/ - override void visit(Statement s) + void visitDefaultCase(Statement s) { debug (LOG) { - printf("%s Statement::interpret()\n", s.loc.toChars()); + printf("%s Statement::interpret() %s\n", s.loc.toChars(), s.toChars()); } if (istate.start) { @@ -830,7 +826,7 @@ public: result = CTFEExp.cantexp; } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { debug (LOG) { @@ -850,7 +846,12 @@ public: return; } - override void visit(CompoundStatement s) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitCompound(CompoundStatement s) { debug (LOG) { @@ -863,7 +864,7 @@ public: foreach (i; 0 .. dim) { Statement sx = (*s.statements)[i]; - result = interpret(pue, sx, istate); + result = interpretStatement(pue, sx, istate); if (result) break; } @@ -873,7 +874,12 @@ public: } } - override void visit(UnrolledLoopStatement s) + void visitCompoundAsm(CompoundAsmStatement s) + { + visitCompound(s); + } + + void visitUnrolledLoop(UnrolledLoopStatement s) { debug (LOG) { @@ -886,7 +892,7 @@ public: foreach (i; 0 .. dim) { Statement sx = (*s.statements)[i]; - Expression e = interpret(pue, sx, istate); + Expression e = interpretStatement(pue, sx, istate); if (!e) // succeeds to interpret, or goto target was not found continue; if (exceptionOrCant(e)) @@ -919,7 +925,7 @@ public: } } - override void visit(IfStatement s) + void visitIf(IfStatement s) { debug (LOG) { @@ -931,9 +937,9 @@ public: if (istate.start) { Expression e = null; - e = interpret(s.ifbody, istate); + e = interpretStatement(s.ifbody, istate); if (!e && istate.start) - e = interpret(s.elsebody, istate); + e = interpretStatement(s.elsebody, istate); result = e; return; } @@ -945,9 +951,9 @@ public: return; if (isTrueBool(e)) - result = interpret(pue, s.ifbody, istate); + result = interpretStatement(pue, s.ifbody, istate); else if (e.toBool().hasValue(false)) - result = interpret(pue, s.elsebody, istate); + result = interpretStatement(pue, s.elsebody, istate); else { // no error, or assert(0)? @@ -955,7 +961,7 @@ public: } } - override void visit(ScopeStatement s) + void visitScope(ScopeStatement s) { debug (LOG) { @@ -964,78 +970,10 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); - } - - /** - Given an expression e which is about to be returned from the current - function, generate an error if it contains pointers to local variables. - - Only checks expressions passed by value (pointers to local variables - may already be stored in members of classes, arrays, or AAs which - were passed as mutable function parameters). - Returns: - true if it is safe to return, false if an error was generated. - */ - static bool stopPointersEscaping(const ref Loc loc, Expression e) - { - if (!e.type.hasPointers()) - return true; - if (isPointer(e.type)) - { - Expression x = e; - if (auto eaddr = e.isAddrExp()) - x = eaddr.e1; - VarDeclaration v; - while (x.op == EXP.variable && (v = x.isVarExp().var.isVarDeclaration()) !is null) - { - if (v.storage_class & STC.ref_) - { - x = getValue(v); - if (auto eaddr = e.isAddrExp()) - eaddr.e1 = x; - continue; - } - if (ctfeGlobals.stack.isInCurrentFrame(v)) - { - error(loc, "returning a pointer to a local stack variable"); - return false; - } - else - break; - } - // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not - // pointing to a local struct or static array. - } - if (auto se = e.isStructLiteralExp()) - { - return stopPointersEscapingFromArray(loc, se.elements); - } - if (auto ale = e.isArrayLiteralExp()) - { - return stopPointersEscapingFromArray(loc, ale.elements); - } - if (auto aae = e.isAssocArrayLiteralExp()) - { - if (!stopPointersEscapingFromArray(loc, aae.keys)) - return false; - return stopPointersEscapingFromArray(loc, aae.values); - } - return true; - } - - // Check all elements of an array for escaping local variables. Return false if error - static bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems) - { - foreach (e; *elems) - { - if (e && !stopPointersEscaping(loc, e)) - return false; - } - return true; + result = interpretStatement(pue, s.statement, istate); } - override void visit(ReturnStatement s) + void visitReturn(ReturnStatement s) { debug (LOG) { @@ -1092,7 +1030,7 @@ public: if (isRuntimeHook(s.exp, Id._d_arrayappendT) || isRuntimeHook(s.exp, Id._d_arrayappendTTrace)) { auto rs = new ReturnStatement(s.loc, e); - rs.accept(this); + visitReturn(rs); return; } @@ -1113,20 +1051,7 @@ public: result = e; } - static Statement findGotoTarget(InterState* istate, Identifier ident) - { - Statement target = null; - if (ident) - { - LabelDsymbol label = istate.fd.searchLabel(ident); - assert(label && label.statement); - LabelStatement ls = label.statement; - target = ls.gotoTarget ? ls.gotoTarget : ls.statement; - } - return target; - } - - override void visit(BreakStatement s) + void visitBreak(BreakStatement s) { debug (LOG) { @@ -1144,7 +1069,7 @@ public: result = CTFEExp.breakexp; } - override void visit(ContinueStatement s) + void visitContinue(ContinueStatement s) { debug (LOG) { @@ -1162,7 +1087,7 @@ public: result = CTFEExp.continueexp; } - override void visit(WhileStatement s) + void visitWhile(WhileStatement s) { debug (LOG) { @@ -1171,7 +1096,7 @@ public: assert(0); // rewritten to ForStatement } - override void visit(DoStatement s) + void visitDo(DoStatement s) { debug (LOG) { @@ -1182,7 +1107,7 @@ public: while (1) { - Expression e = interpret(s._body, istate); + Expression e = interpretStatement(s._body, istate); if (!e && istate.start) // goto target was not found return; assert(!istate.start); @@ -1232,7 +1157,7 @@ public: assert(result is null); } - override void visit(ForStatement s) + void visitFor(ForStatement s) { debug (LOG) { @@ -1242,7 +1167,7 @@ public: istate.start = null; UnionExp ueinit = void; - Expression ei = interpret(&ueinit, s._init, istate); + Expression ei = interpretStatement(&ueinit, s._init, istate); if (exceptionOrCant(ei)) return; assert(!ei); // s.init never returns from function, or jumps out from it @@ -1261,7 +1186,7 @@ public: assert(isTrueBool(e)); } - Expression e = interpret(pue, s._body, istate); + Expression e = interpretStatement(pue, s._body, istate); if (!e && istate.start) // goto target was not found return; assert(!istate.start); @@ -1304,17 +1229,17 @@ public: assert(result is null); } - override void visit(ForeachStatement s) + void visitForeach(ForeachStatement s) { assert(0); // rewritten to ForStatement } - override void visit(ForeachRangeStatement s) + void visitForeachRange(ForeachRangeStatement s) { assert(0); // rewritten to ForStatement } - override void visit(SwitchStatement s) + void visitSwitch(SwitchStatement s) { debug (LOG) { @@ -1325,7 +1250,7 @@ public: istate.start = null; if (istate.start) { - Expression e = interpret(s._body, istate); + Expression e = interpretStatement(s._body, istate); if (istate.start) // goto target was not found return; if (exceptionOrCant(e)) @@ -1375,7 +1300,7 @@ public: /* Jump to scase */ istate.start = scase; - Expression e = interpret(pue, s._body, istate); + Expression e = interpretStatement(pue, s._body, istate); assert(!istate.start); // jump must not fail if (e && e.op == EXP.break_) { @@ -1390,7 +1315,7 @@ public: result = e; } - override void visit(CaseStatement s) + void visitCase(CaseStatement s) { debug (LOG) { @@ -1400,10 +1325,10 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); + result = interpretStatement(pue, s.statement, istate); } - override void visit(DefaultStatement s) + void visitDefault(DefaultStatement s) { debug (LOG) { @@ -1413,10 +1338,10 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); + result = interpretStatement(pue, s.statement, istate); } - override void visit(GotoStatement s) + void visitGoto(GotoStatement s) { debug (LOG) { @@ -1435,7 +1360,7 @@ public: result = CTFEExp.gotoexp; } - override void visit(GotoCaseStatement s) + void visitGotoCase(GotoCaseStatement s) { debug (LOG) { @@ -1454,7 +1379,7 @@ public: result = CTFEExp.gotoexp; } - override void visit(GotoDefaultStatement s) + void visitGotoDefault(GotoDefaultStatement s) { debug (LOG) { @@ -1473,7 +1398,7 @@ public: result = CTFEExp.gotoexp; } - override void visit(LabelStatement s) + void visitLabel(LabelStatement s) { debug (LOG) { @@ -1482,10 +1407,10 @@ public: if (istate.start == s) istate.start = null; - result = interpret(pue, s.statement, istate); + result = interpretStatement(pue, s.statement, istate); } - override void visit(TryCatchStatement s) + void visitTryCatch(TryCatchStatement s) { debug (LOG) { @@ -1496,18 +1421,18 @@ public: if (istate.start) { Expression e = null; - e = interpret(pue, s._body, istate); + e = interpretStatement(pue, s._body, istate); foreach (ca; *s.catches) { if (e || !istate.start) // goto target was found break; - e = interpret(pue, ca.handler, istate); + e = interpretStatement(pue, ca.handler, istate); } result = e; return; } - Expression e = interpret(s._body, istate); + Expression e = interpretStatement(s._body, istate); // An exception was thrown if (e && e.isThrownExceptionExp()) @@ -1528,7 +1453,7 @@ public: ctfeGlobals.stack.push(ca.var); setValue(ca.var, ex.thrown); } - e = interpret(ca.handler, istate); + e = interpretStatement(ca.handler, istate); if (CTFEExp.isGotoExp(e)) { /* This is an optimization that relies on the locality of the jump target. @@ -1540,7 +1465,7 @@ public: InterState istatex = *istate; istatex.start = istate.gotoTarget; // set starting statement istatex.gotoTarget = null; - Expression eh = interpret(ca.handler, &istatex); + Expression eh = interpretStatement(ca.handler, &istatex); if (!istatex.start) { istate.gotoTarget = null; @@ -1553,39 +1478,7 @@ public: result = e; } - static ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest) - { - debug (LOG) - { - printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars()); - } - // Little sanity check to make sure it's really a Throwable - ClassReferenceExp boss = oldest.thrown; - const next = 5; // index of Throwable.next - assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next - ClassReferenceExp collateral = newest.thrown; - if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException()) - { - /* Find the index of the Error.bypassException field - */ - auto bypass = next + 1; - if ((*collateral.value.elements)[bypass].type.ty == Tuns32) - bypass += 1; // skip over _refcount field - assert((*collateral.value.elements)[bypass].type.ty == Tclass); - - // The new exception bypass the existing chain - (*collateral.value.elements)[bypass] = boss; - return newest; - } - while ((*boss.value.elements)[next].op == EXP.classReference) - { - boss = (*boss.value.elements)[next].isClassReferenceExp(); - } - (*boss.value.elements)[next] = collateral; - return oldest; - } - - override void visit(TryFinallyStatement s) + void visitTryFinally(TryFinallyStatement s) { debug (LOG) { @@ -1596,14 +1489,14 @@ public: if (istate.start) { Expression e = null; - e = interpret(pue, s._body, istate); + e = interpretStatement(pue, s._body, istate); // Jump into/out from finalbody is disabled in semantic analysis. // and jump inside will be handled by the ScopeStatement == finalbody. result = e; return; } - Expression ex = interpret(s._body, istate); + Expression ex = interpretStatement(s._body, istate); if (CTFEExp.isCantExp(ex)) { result = ex; @@ -1616,7 +1509,7 @@ public: InterState istatex = *istate; istatex.start = istate.gotoTarget; // set starting statement istatex.gotoTarget = null; - Expression bex = interpret(s._body, &istatex); + Expression bex = interpretStatement(s._body, &istatex); if (istatex.start) { // The goto target is outside the current scope. @@ -1632,7 +1525,7 @@ public: ex = bex; } - Expression ey = interpret(s.finalbody, istate); + Expression ey = interpretStatement(s.finalbody, istate); if (CTFEExp.isCantExp(ey)) { result = ey; @@ -1649,7 +1542,7 @@ public: result = ex; } - override void visit(ThrowStatement s) + void visitThrow(ThrowStatement s) { debug (LOG) { @@ -1662,35 +1555,15 @@ public: istate.start = null; } - interpretThrow(s.exp, s.loc); + interpretThrow(result, s.exp, s.loc, istate); } - /// Interpret `throw ` found at the specified location `loc` - private void interpretThrow(Expression exp, const ref Loc loc) - { - incUsageCtfe(istate, loc); - - Expression e = interpretRegion(exp, istate); - if (exceptionOrCant(e)) - return; - - if (e.op == EXP.classReference) - { - result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp()); - } - else - { - exp.error("to be thrown `%s` must be non-null", exp.toChars()); - result = ErrorExp.get(); - } - } - - override void visit(ScopeGuardStatement s) + void visitScopeGuard(ScopeGuardStatement s) { assert(0); } - override void visit(WithStatement s) + void visitWith(WithStatement s) { debug (LOG) { @@ -1700,14 +1573,14 @@ public: istate.start = null; if (istate.start) { - result = s._body ? interpret(s._body, istate) : null; + result = s._body ? interpretStatement(s._body, istate) : null; return; } // If it is with(Enum) {...}, just execute the body. if (s.exp.op == EXP.scope_ || s.exp.op == EXP.type) { - result = interpret(pue, s._body, istate); + result = interpretStatement(pue, s._body, istate); return; } @@ -1723,7 +1596,7 @@ public: } ctfeGlobals.stack.push(s.wthis); setValue(s.wthis, e); - e = interpret(s._body, istate); + e = interpretStatement(s._body, istate); if (CTFEExp.isGotoExp(e)) { /* This is an optimization that relies on the locality of the jump target. @@ -1735,7 +1608,7 @@ public: InterState istatex = *istate; istatex.start = istate.gotoTarget; // set starting statement istatex.gotoTarget = null; - Expression ex = interpret(s._body, &istatex); + Expression ex = interpretStatement(s._body, &istatex); if (!istatex.start) { istate.gotoTarget = null; @@ -1746,7 +1619,7 @@ public: result = e; } - override void visit(AsmStatement s) + void visitAsm(AsmStatement s) { debug (LOG) { @@ -1762,7 +1635,17 @@ public: result = CTFEExp.cantexp; } - override void visit(ImportStatement s) + void visitInlineAsm(InlineAsmStatement s) + { + visitAsm(s); + } + + void visitGccAsm(GccAsmStatement s) + { + visitAsm(s); + } + + void visitImport(ImportStatement s) { debug (LOG) { @@ -1776,6 +1659,45 @@ public: } } + if (!s) + return null; + + mixin VisitStatement!void visit; + visit.VisitStatement(s); + return result; +} + +/// + +private extern (C++) final class Interpreter : Visitor +{ + alias visit = Visitor.visit; +public: + InterState* istate; + CTFEGoal goal; + Expression result; + UnionExp* pue; // storage for `result` + + extern (D) this(UnionExp* pue, InterState* istate, CTFEGoal goal) scope + { + this.pue = pue; + this.istate = istate; + this.goal = goal; + } + + // If e is EXP.throw_exception or EXP.cantExpression, + // set it to 'result' and returns true. + bool exceptionOrCant(Expression e) + { + if (exceptionOrCantInterpret(e)) + { + // Make sure e is not pointing to a stack temporary + result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; + return true; + } + return false; + } + /******************************** Expression ***************************/ override void visit(Expression e) @@ -2550,7 +2472,7 @@ public: { debug (LOG) { - printf("%s ArrayLiteralExp::interpret() %s\n", e.loc.toChars(), e.toChars()); + printf("%s ArrayLiteralExp::interpret() %s, %s\n", e.loc.toChars(), e.type.toChars(), e.toChars()); } if (e.ownedByCtfe >= OwnedBy.ctfe) // We've already interpreted all the elements { @@ -2558,7 +2480,8 @@ public: return; } - Type tn = e.type.toBasetype().nextOf().toBasetype(); + Type tb = e.type.toBasetype(); + Type tn = tb.nextOf().toBasetype(); bool wantCopy = (tn.ty == Tsarray || tn.ty == Tstruct); auto basis = interpretRegion(e.basis, istate); @@ -2567,6 +2490,7 @@ public: auto expsx = e.elements; size_t dim = expsx ? expsx.length : 0; + for (size_t i = 0; i < dim; i++) { Expression exp = (*expsx)[i]; @@ -3998,7 +3922,7 @@ public: newval = copyLiteral(newval).copy(); assignInPlace(oldval, newval); } - else if (wantCopy && e.op == EXP.assign) + else if (wantCopy && (e.op == EXP.assign || e.op == EXP.loweredAssignExp)) { // Currently postblit/destructor calls on static array are done // in the druntime internal functions so they don't appear in AST. @@ -4080,6 +4004,8 @@ public: */ private Expression interpretAssignToSlice(UnionExp* pue, BinExp e, Expression e1, Expression newval, bool isBlockAssignment) { + //printf("interpretAssignToSlice(e: %s e1: %s newval: %s\n", e.toChars(), e1.toChars(), newval.toChars()); + dinteger_t lowerbound; dinteger_t upperbound; dinteger_t firstIndex; @@ -4139,7 +4065,7 @@ public: return newval; // For slice assignment, we check that the lengths match. - if (!isBlockAssignment) + if (!isBlockAssignment && e1.type.ty != Tpointer) { const srclen = resolveArrayLength(newval); if (srclen != (upperbound - lowerbound)) @@ -4354,8 +4280,8 @@ public: Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr) { Expressions* w = ae.elements; - assert(ae.type.ty == Tsarray || ae.type.ty == Tarray); - bool directblk = (cast(TypeArray)ae.type).next.equivalent(newval.type); + assert(ae.type.ty == Tsarray || ae.type.ty == Tarray || ae.type.ty == Tpointer); + bool directblk = (cast(TypeNext)ae.type).next.equivalent(newval.type); for (size_t k = lwr; k < upr; k++) { if (!directblk && (*w)[k].op == EXP.arrayLiteral) @@ -4405,7 +4331,7 @@ public: rb.newval = newval; rb.refCopy = wantRef || cow; rb.needsPostblit = sd && sd.postblit && e.op != EXP.blit && e.e2.isLvalue(); - rb.needsDtor = sd && sd.dtor && e.op == EXP.assign; + rb.needsDtor = sd && sd.dtor && (e.op == EXP.assign || e.op == EXP.loweredAssignExp); if (Expression ex = rb.assignTo(existingAE, cast(size_t)lowerbound, cast(size_t)upperbound)) return ex; @@ -4879,31 +4805,11 @@ public: result = CTFEExp.voidexp; return; } - else if (fd.ident == Id._d_arraysetlengthT) + else if (isArrayConstruction(fd.ident)) { - // In expressionsem.d `ea.length = eb;` got lowered to `_d_arraysetlengthT(ea, eb);`. - // The following code will rewrite it back to `ea.length = eb` and then interpret that expression. - assert(e.arguments.length == 2); - - Expression ea = (*e.arguments)[0]; - Expression eb = (*e.arguments)[1]; - - auto ale = ctfeEmplaceExp!ArrayLengthExp(e.loc, ea); - ale.type = Type.tsize_t; - AssignExp ae = ctfeEmplaceExp!AssignExp(e.loc, ale, eb); - ae.type = ea.type; - - // if (global.params.verbose) - // message("interpret %s =>\n %s", e.toChars(), ae.toChars()); - result = interpretRegion(ae, istate); - return; - } - else if (isArrayConstructionOrAssign(fd.ident)) - { - // In expressionsem.d, the following lowerings were performed: - // * `T[x] ea = eb;` to `_d_array{,set}ctor(ea[], eb[]);`. - // * `ea = eb` to `_d_array{,setassign,assign_l,assign_r}(ea[], eb)`. - // The following code will rewrite them back to `ea = eb` and + // In expressionsem.d, `T[x] ea = eb;` was lowered to: + // `_d_array{,set}ctor(ea[], eb[]);`. + // The following code will rewrite it back to `ea = eb` and // then interpret that expression. if (fd.ident == Id._d_arrayctor) @@ -4916,17 +4822,14 @@ public: ea = ea.isCastExp.e1; Expression eb = (*e.arguments)[1]; - if (eb.isCastExp() && fd.ident != Id._d_arraysetctor) + if (eb.isCastExp() && fd.ident == Id._d_arrayctor) eb = eb.isCastExp.e1; - Expression rewrittenExp; - if (fd.ident == Id._d_arrayctor || fd.ident == Id._d_arraysetctor) - rewrittenExp = new ConstructExp(e.loc, ea, eb); - else - rewrittenExp = new AssignExp(e.loc, ea, eb); + ConstructExp ce = new ConstructExp(e.loc, ea, eb); + ce.type = ea.type; - rewrittenExp.type = ea.type; - result = interpret(rewrittenExp, istate); + ce.type = ea.type; + result = interpret(ce, istate); return; } @@ -5878,7 +5781,25 @@ public: e2 = ue2.copy(); } - *pue = ctfeCat(e.loc, e.type, e1, e2); + Expression prepareCatOperand(Expression exp) + { + /* Convert `elem ~ array` to `[elem] ~ array` if `elem` is itself an + * array. This is needed because interpreting the `CatExp` calls + * `Cat()`, which cannot handle concatenations between different + * types, except for strings and chars. + */ + auto tb = e.type.toBasetype(); + auto tbNext = tb.nextOf(); + auto expTb = exp.type.toBasetype(); + + if (exp.type.implicitConvTo(tbNext) >= MATCH.convert && + (tb.ty == Tarray || tb.ty == Tsarray) && + (expTb.ty == Tarray || expTb.ty == Tsarray)) + return new ArrayLiteralExp(exp.loc, e.type, exp); + return exp; + } + + *pue = ctfeCat(e.loc, e.type, prepareCatOperand(e1), prepareCatOperand(e2)); result = pue.exp(); if (CTFEExp.isCantExp(result)) @@ -6239,15 +6160,18 @@ public: { printf("%s ThrowExpression::interpret()\n", te.loc.toChars()); } - interpretThrow(te.e1, te.loc); + interpretThrow(result, te.e1, te.loc, istate); } override void visit(PtrExp e) { + // Called for both lvalues and rvalues + const lvalue = goal == CTFEGoal.LValue; debug (LOG) { - printf("%s PtrExp::interpret() %s\n", e.loc.toChars(), e.toChars()); + printf("%s PtrExp::interpret(%d) %s, %s\n", e.loc.toChars(), lvalue, e.type.toChars(), e.toChars()); } + // Check for int<->float and long<->double casts. if (auto soe1 = e.e1.isSymOffExp()) if (soe1.offset == 0 && soe1.var.isVarDeclaration() && isFloatIntPaint(e.type, soe1.var.type)) @@ -6305,6 +6229,20 @@ public: return; } + if (!lvalue && result.isArrayLiteralExp() && + result.type.isTypePointer()) + { + /* A pointer variable can point to an array literal like `[3]`. + * Dereferencing it means accessing the first element value. + * Dereference it only if result should be an rvalue + */ + auto ae = result.isArrayLiteralExp(); + if (ae.elements.length == 1) + { + result = (*ae.elements)[0]; + return; + } + } if (result.isStringExp() || result.isArrayLiteralExp()) return; @@ -6545,33 +6483,57 @@ public: { assert(0); // This should never be interpreted } +} - /********************************************* - * Checks if the given expresion is a call to the runtime hook `id`. - * Params: - * e = the expression to check - * id = the identifier of the runtime hook - * Returns: - * `e` cast to `CallExp` if it's the hook, `null` otherwise - */ - private CallExp isRuntimeHook(Expression e, Identifier id) +/// Interpret `throw ` found at the specified location `loc` +private +void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, InterState* istate) +{ + incUsageCtfe(istate, loc); + + Expression e = interpretRegion(exp, istate); + if (exceptionOrCantInterpret(e)) { - if (auto ce = e.isCallExp()) + // Make sure e is not pointing to a stack temporary + result = (e.op == EXP.cantExpression) ? CTFEExp.cantexp : e; + } + else if (e.op == EXP.classReference) + { + result = ctfeEmplaceExp!ThrownExceptionExp(loc, e.isClassReferenceExp()); + } + else + { + exp.error("to be thrown `%s` must be non-null", exp.toChars()); + result = ErrorExp.get(); + } +} + +/********************************************* + * Checks if the given expresion is a call to the runtime hook `id`. + * + * Params: + * e = the expression to check + * id = the identifier of the runtime hook + * Returns: + * `e` cast to `CallExp` if it's the hook, `null` otherwise + */ +public CallExp isRuntimeHook(Expression e, Identifier id) +{ + if (auto ce = e.isCallExp()) + { + if (auto ve = ce.e1.isVarExp()) { - if (auto ve = ce.e1.isVarExp()) + if (auto fd = ve.var.isFuncDeclaration()) { - if (auto fd = ve.var.isFuncDeclaration()) - { - // If `_d_HookTraceImpl` is found, resolve the underlying - // hook and replace `e` and `fd` with it. - removeHookTraceImpl(ce, fd); - return fd.ident == id ? ce : null; - } + // If `_d_HookTraceImpl` is found, resolve the underlying hook + // and replace `e` and `fd` with it. + removeHookTraceImpl(ce, fd); + return fd.ident == id ? ce : null; } } - - return null; } + + return null; } /******************************************** @@ -6589,10 +6551,12 @@ Expression interpret(UnionExp* pue, Expression e, InterState* istate, CTFEGoal g { if (!e) return null; + //printf("+interpret() e : %s, %s\n", e.type.toChars(), e.toChars()); scope Interpreter v = new Interpreter(pue, istate, goal); e.accept(v); Expression ex = v.result; assert(goal == CTFEGoal.Nothing || ex !is null); + //if (ex) printf("-interpret() ex: %s, %s\n", ex.type.toChars(), ex.toChars()); else printf("-interpret()\n"); return ex; } @@ -6639,34 +6603,135 @@ Expression interpretRegion(Expression e, InterState* istate, CTFEGoal goal = CTF return cast(Expression)memcpy(p, cast(void*)uexp, uexp.size); } -/*********************************** - * Interpret the statement. - * Params: - * pue = non-null pointer to temporary storage that can be used to store the return value - * s = Statement to interpret - * istate = context - * Returns: - * NULL continue to next statement - * EXP.cantExpression cannot interpret statement at compile time - * !NULL expression from return statement, or thrown exception +private +Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) +{ + if (exps is original) + { + if (!original) + exps = new Expressions(); + else + exps = original.copy(); + ++ctfeGlobals.numArrayAllocs; + } + return exps; +} + +/** + Given an expression e which is about to be returned from the current + function, generate an error if it contains pointers to local variables. + + Only checks expressions passed by value (pointers to local variables + may already be stored in members of classes, arrays, or AAs which + were passed as mutable function parameters). + Returns: + true if it is safe to return, false if an error was generated. */ -Expression interpret(UnionExp* pue, Statement s, InterState* istate) +private +bool stopPointersEscaping(const ref Loc loc, Expression e) { - if (!s) - return null; - scope Interpreter v = new Interpreter(pue, istate, CTFEGoal.Nothing); - s.accept(v); - return v.result; + if (!e.type.hasPointers()) + return true; + if (isPointer(e.type)) + { + Expression x = e; + if (auto eaddr = e.isAddrExp()) + x = eaddr.e1; + VarDeclaration v; + while (x.op == EXP.variable && (v = x.isVarExp().var.isVarDeclaration()) !is null) + { + if (v.storage_class & STC.ref_) + { + x = getValue(v); + if (auto eaddr = e.isAddrExp()) + eaddr.e1 = x; + continue; + } + if (ctfeGlobals.stack.isInCurrentFrame(v)) + { + error(loc, "returning a pointer to a local stack variable"); + return false; + } + else + break; + } + // TODO: If it is a EXP.dotVariable or EXP.index, we should check that it is not + // pointing to a local struct or static array. + } + if (auto se = e.isStructLiteralExp()) + { + return stopPointersEscapingFromArray(loc, se.elements); + } + if (auto ale = e.isArrayLiteralExp()) + { + return stopPointersEscapingFromArray(loc, ale.elements); + } + if (auto aae = e.isAssocArrayLiteralExp()) + { + if (!stopPointersEscapingFromArray(loc, aae.keys)) + return false; + return stopPointersEscapingFromArray(loc, aae.values); + } + return true; } -/// -Expression interpret(Statement s, InterState* istate) +// Check all elements of an array for escaping local variables. Return false if error +private +bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems) { - UnionExp ue = void; - auto result = interpret(&ue, s, istate); - if (result == ue.exp()) - result = ue.copy(); - return result; + foreach (e; *elems) + { + if (e && !stopPointersEscaping(loc, e)) + return false; + } + return true; +} + +private +Statement findGotoTarget(InterState* istate, Identifier ident) +{ + Statement target = null; + if (ident) + { + LabelDsymbol label = istate.fd.searchLabel(ident); + assert(label && label.statement); + LabelStatement ls = label.statement; + target = ls.gotoTarget ? ls.gotoTarget : ls.statement; + } + return target; +} + +private +ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp newest) +{ + debug (LOG) + { + printf("Collided exceptions %s %s\n", oldest.thrown.toChars(), newest.thrown.toChars()); + } + // Little sanity check to make sure it's really a Throwable + ClassReferenceExp boss = oldest.thrown; + const next = 5; // index of Throwable.next + assert((*boss.value.elements)[next].type.ty == Tclass); // Throwable.next + ClassReferenceExp collateral = newest.thrown; + if (collateral.originalClass().isErrorException() && !boss.originalClass().isErrorException()) + { + /* Find the index of the Error.bypassException field + */ + auto bypass = next + 1; + if ((*collateral.value.elements)[bypass].type.ty == Tuns32) + bypass += 1; // skip over _refcount field + assert((*collateral.value.elements)[bypass].type.ty == Tclass); + + // The new exception bypass the existing chain + (*collateral.value.elements)[bypass] = boss; + return newest; + } + while ((*boss.value.elements)[next].op == EXP.classReference) + { + boss = (*boss.value.elements)[next].isClassReferenceExp(); + } + (*boss.value.elements)[next] = collateral; + return oldest; } /** @@ -7009,7 +7074,6 @@ private Expression copyRegionExp(Expression e) case EXP.null_: case EXP.void_: case EXP.symbolOffset: - case EXP.char_: break; case EXP.cantExpression: diff --git a/dmd/dmodule.d b/dmd/dmodule.d index 1900f368063..58b5b1ff3eb 100644 --- a/dmd/dmodule.d +++ b/dmd/dmodule.d @@ -835,7 +835,7 @@ version (IN_LLVM) { filetype = FileType.c; - scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines); + scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines, &global.compileEnv); p.nextToken(); checkCompiledImport(); members = p.parseModule(); @@ -844,7 +844,9 @@ version (IN_LLVM) } else { - scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests); + p.transitionIn = global.params.vin; p.nextToken(); p.parseModuleDeclaration(); md = p.md; @@ -1297,8 +1299,7 @@ version (IN_LLVM) return this.importedFrom == this; } - // true if the module source file is directly - // listed in command line. + /// Returns: Whether this module is in the `core` package and has name `ident` bool isCoreModule(Identifier ident) nothrow { return this.ident == ident && parent && parent.ident == Id.core && !parent.parent; @@ -1378,6 +1379,20 @@ else return _escapetable; } + /**************************** + * A Singleton that loads core.stdc.config + * Returns: + * Module of core.stdc.config, null if couldn't find it + */ + extern (D) static Module loadCoreStdcConfig() + { + __gshared Module core_stdc_config; + auto pkgids = new Identifier[2]; + pkgids[0] = Id.core; + pkgids[1] = Id.stdc; + return loadModuleFromLibrary(core_stdc_config, pkgids, Id.config); + } + /**************************** * A Singleton that loads core.atomic * Returns: @@ -1386,7 +1401,9 @@ else extern (D) static Module loadCoreAtomic() { __gshared Module core_atomic; - return loadModuleFromLibrary(core_atomic, Id.core, Id.atomic); + auto pkgids = new Identifier[1]; + pkgids[0] = Id.core; + return loadModuleFromLibrary(core_atomic, pkgids, Id.atomic); } /**************************** @@ -1397,26 +1414,26 @@ else extern (D) static Module loadStdMath() { __gshared Module std_math; - return loadModuleFromLibrary(std_math, Id.std, Id.math); + auto pkgids = new Identifier[1]; + pkgids[0] = Id.std; + return loadModuleFromLibrary(std_math, pkgids, Id.math); } /********************************** * Load a Module from the library. * Params: * mod = cached return value of this call - * pkgid = package id + * pkgids = package identifiers * modid = module id * Returns: * Module loaded, null if cannot load it */ - private static Module loadModuleFromLibrary(ref Module mod, Identifier pkgid, Identifier modid) + extern (D) private static Module loadModuleFromLibrary(ref Module mod, Identifier[] pkgids, Identifier modid) { if (mod) return mod; - auto ids = new Identifier[1]; - ids[0] = pkgid; - auto imp = new Import(Loc.initial, ids[], modid, null, true); + auto imp = new Import(Loc.initial, pkgids[], modid, null, true); // Module.load will call fatal() if there's no module available. // Gag the error here, pushing the error handling to the caller. const errors = global.startGagging(); diff --git a/dmd/doc.d b/dmd/doc.d index 65c917909f2..1b54844b206 100644 --- a/dmd/doc.d +++ b/dmd/doc.d @@ -5188,7 +5188,7 @@ private void highlightCode2(Scope* sc, Dsymbols* a, ref OutBuffer buf, size_t of scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, global.errorSink, - global.vendor, global.versionNumber()); + &global.compileEnv); OutBuffer res; const(char)* lastp = cast(char*)buf[].ptr; //printf("highlightCode2('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr); diff --git a/dmd/dscope.d b/dmd/dscope.d index a0a80b27264..749c9f1454c 100644 --- a/dmd/dscope.d +++ b/dmd/dscope.d @@ -818,4 +818,14 @@ version (IN_LLVM) { return this.intypeof || this.flags & SCOPE.compile; } + + + /** + * Returns: true if the code needs to go all the way through to code generation. + * This implies things like needing lowering to simpler forms. + */ + extern (D) bool needsCodegen() + { + return (flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) == 0; + } } diff --git a/dmd/dstruct.d b/dmd/dstruct.d index bb5ecbbdff6..dfda7836613 100644 --- a/dmd/dstruct.d +++ b/dmd/dstruct.d @@ -13,6 +13,8 @@ module dmd.dstruct; +import core.stdc.stdio; + import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; @@ -75,7 +77,7 @@ extern (C++) void semanticTypeInfo(Scope* sc, Type t) { if (sc.intypeof) return; - if (sc.flags & (SCOPE.ctfe | SCOPE.compile | SCOPE.ctfeBlock)) + if (!sc.needsCodegen()) return; } @@ -576,7 +578,7 @@ version (IN_LLVM) {} else * Returns: * true if it's all binary 0 */ -private bool _isZeroInit(Expression exp) +bool _isZeroInit(Expression exp) { switch (exp.op) { @@ -584,20 +586,22 @@ private bool _isZeroInit(Expression exp) return exp.toInteger() == 0; case EXP.null_: - case EXP.false_: return true; case EXP.structLiteral: { - auto sle = cast(StructLiteralExp) exp; + auto sle = exp.isStructLiteralExp(); + if (sle.sd.isNested()) + return false; + const isCstruct = sle.sd.isCsymbol(); // C structs are default initialized to all zeros foreach (i; 0 .. sle.sd.fields.length) { auto field = sle.sd.fields[i]; if (field.type.size(field.loc)) { - auto e = (*sle.elements)[i]; + auto e = sle.elements && i < sle.elements.length ? (*sle.elements)[i] : null; if (e ? !_isZeroInit(e) - : !field.type.isZeroInit(field.loc)) + : !isCstruct && !field.type.isZeroInit(field.loc)) return false; } } diff --git a/dmd/dsymbol.d b/dmd/dsymbol.d index a9239be6d0c..c91327c4da1 100644 --- a/dmd/dsymbol.d +++ b/dmd/dsymbol.d @@ -1443,7 +1443,7 @@ version (IN_LLVM) inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return null; } inout(VisibilityDeclaration) isVisibilityDeclaration() inout { return null; } inout(OverloadSet) isOverloadSet() inout { return null; } - inout(CompileDeclaration) isCompileDeclaration() inout { return null; } + inout(MixinDeclaration) isMixinDeclaration() inout { return null; } inout(StaticAssert) isStaticAssert() inout { return null; } inout(StaticIfDeclaration) isStaticIfDeclaration() inout { return null; } } diff --git a/dmd/dsymbol.h b/dmd/dsymbol.h index 0e6bb72b439..81442d69731 100644 --- a/dmd/dsymbol.h +++ b/dmd/dsymbol.h @@ -174,7 +174,7 @@ struct FieldState unsigned fieldAlign; unsigned bitOffset; - bool inFlight; + d_bool inFlight; }; struct DsymbolAttributes; @@ -196,7 +196,7 @@ class Dsymbol : public ASTNode private: DsymbolAttributes* atts; public: - bool errors; // this symbol failed to pass semantic() + d_bool errors; // this symbol failed to pass semantic() PASS semanticRun; unsigned short localNum; // perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab static Dsymbol *create(Identifier *); @@ -328,7 +328,7 @@ class Dsymbol : public ASTNode virtual CPPNamespaceDeclaration *isCPPNamespaceDeclaration() { return NULL; } virtual VisibilityDeclaration *isVisibilityDeclaration() { return NULL; } virtual OverloadSet *isOverloadSet() { return NULL; } - virtual CompileDeclaration *isCompileDeclaration() { return NULL; } + virtual MixinDeclaration *isMixinDeclaration() { return NULL; } virtual StaticAssert *isStaticAssert() { return NULL; } virtual StaticIfDeclaration *isStaticIfDeclaration() { return NULL; } void accept(Visitor *v) override { v->visit(this); } diff --git a/dmd/dsymbolsem.d b/dmd/dsymbolsem.d index dd92286c88c..53c2c39fbec 100644 --- a/dmd/dsymbolsem.d +++ b/dmd/dsymbolsem.d @@ -494,7 +494,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // Infering the type requires running semantic, // so mark the scope as ctfe if required - bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0; + bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func; if (needctfe) { sc.flags |= SCOPE.condition; @@ -1345,6 +1345,9 @@ version (IN_LLVM) if (dsym.errors) return; + if (!(global.params.bitfields || sc.flags & SCOPE.Cfile)) + dsym.error("use -preview=bitfields for bitfield support"); + if (!dsym.parent.isStructDeclaration() && !dsym.parent.isClassDeclaration()) { dsym.error("- bit-field must be member of struct, union, or class"); @@ -1387,9 +1390,9 @@ version (IN_LLVM) { static if (LOG) { - printf("Import::semantic('%s') %s\n", toPrettyChars(), id.toChars()); + printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars()); scope(exit) - printf("-Import::semantic('%s'), pkg = %p\n", toChars(), pkg); + printf("-Import::semantic('%s'), pkg = %p\n", imp.toChars(), imp.pkg); } if (imp.semanticRun > PASS.initial) return; @@ -1455,7 +1458,9 @@ version (IN_LLVM) imp.addPackageAccess(scopesym); } - imp.mod.dsymbolSemantic(null); + // if a module has errors it means that parsing has failed. + if (!imp.mod.errors) + imp.mod.dsymbolSemantic(null); if (imp.mod.needmoduleinfo) { @@ -1994,9 +1999,9 @@ else // !IN_LLVM attribSemantic(sfd); } - private Dsymbols* compileIt(CompileDeclaration cd) + private Dsymbols* compileIt(MixinDeclaration cd) { - //printf("CompileDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars()); + //printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars()); OutBuffer buf; if (expressionsToString(buf, sc, cd.exps)) return null; @@ -2005,7 +2010,10 @@ else // !IN_LLVM const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(cd.loc, sc._module, str, false, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + 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.vin; p.nextToken(); auto d = p.parseDeclDefs(0); @@ -2023,9 +2031,9 @@ else // !IN_LLVM /*********************************************************** * https://dlang.org/spec/module.html#mixin-declaration */ - override void visit(CompileDeclaration cd) + override void visit(MixinDeclaration cd) { - //printf("CompileDeclaration::semantic()\n"); + //printf("MixinDeclaration::semantic()\n"); if (!cd.compiled) { cd.decl = compileIt(cd); @@ -2323,32 +2331,39 @@ else // !IN_LLVM { /* C11 6.7.2.2 */ - assert(ed.memtype); - int nextValue = 0; // C11 6.7.2.2-3 first member value defaults to 0 + Type commonType = ed.memtype; + if (!commonType) + commonType = Type.tint32; + ulong nextValue = 0; // C11 6.7.2.2-3 first member value defaults to 0 // C11 6.7.2.2-2 value must be representable as an int. // The sizemask represents all values that int will fit into, // from 0..uint.max. We want to cover int.min..uint.max. - const mask = Type.tint32.sizemask(); - IntRange ir = IntRange(SignExtendedNumber(~(mask >> 1), true), - SignExtendedNumber(mask)); + IntRange ir = IntRange.fromType(commonType); - void emSemantic(EnumMember em, ref int nextValue) + void emSemantic(EnumMember em, ref ulong nextValue) { static void errorReturn(EnumMember em) { + em.value = ErrorExp.get(); em.errors = true; em.semanticRun = PASS.semanticdone; } em.semanticRun = PASS.semantic; - em.type = Type.tint32; + em.type = commonType; em._linkage = LINK.c; em.storage_class |= STC.manifest; if (em.value) { Expression e = em.value; assert(e.dyncast() == DYNCAST.expression); + + /* To merge the type of e with commonType, add 0 of type commonType + */ + if (!ed.memtype) + e = new AddExp(em.loc, e, new IntegerExp(em.loc, 0, commonType)); + e = e.expressionSemantic(sc); e = resolveProperties(sc, e); e = e.integralPromotions(sc); @@ -2362,14 +2377,16 @@ else // !IN_LLVM em.error("enum member must be an integral constant expression, not `%s` of type `%s`", e.toChars(), e.type.toChars()); return errorReturn(em); } - if (!ir.contains(getIntRange(ie))) + if (ed.memtype && !ir.contains(getIntRange(ie))) { // C11 6.7.2.2-2 - em.error("enum member value `%s` does not fit in an `int`", e.toChars()); + em.error("enum member value `%s` does not fit in `%s`", e.toChars(), commonType.toChars()); return errorReturn(em); } - nextValue = cast(int)ie.toInteger(); - em.value = new IntegerExp(em.loc, nextValue, Type.tint32); + nextValue = ie.toInteger(); + if (!ed.memtype) + commonType = e.type; + em.value = new IntegerExp(em.loc, nextValue, commonType); } else { @@ -2377,17 +2394,17 @@ else // !IN_LLVM bool first = (em == (*em.ed.members)[0]); if (!first) { - import core.checkedint : adds; - bool overflow; - nextValue = adds(nextValue, 1, overflow); - if (overflow) + Expression max = getProperty(commonType, null, em.loc, Id.max, 0); + if (nextValue == max.toInteger()) { - em.error("initialization with `%d+1` causes overflow for type `int`", nextValue - 1); + em.error("initialization with `%s+1` causes overflow for type `%s`", max.toChars(), commonType.toChars()); return errorReturn(em); } + nextValue += 1; } - em.value = new IntegerExp(em.loc, nextValue, Type.tint32); + em.value = new IntegerExp(em.loc, nextValue, commonType); } + em.type = commonType; em.semanticRun = PASS.semanticdone; } @@ -2396,6 +2413,21 @@ else // !IN_LLVM if (EnumMember em = s.isEnumMember()) emSemantic(em, nextValue); }); + + if (!ed.memtype) + { + // cast all members to commonType + ed.members.foreachDsymbol( (s) + { + if (EnumMember em = s.isEnumMember()) + { + em.type = commonType; + em.value = em.value.castTo(sc, commonType); + } + }); + } + + ed.memtype = commonType; ed.semanticRun = PASS.semanticdone; return; } @@ -4742,7 +4774,8 @@ version (IN_LLVM) { sd.visibility = sc.visibility; - sd.alignment = sc.alignment(); + if (sd.alignment.isUnknown()) // can be set already by `struct __declspec(align(N)) Tag { ... }` + sd.alignment = sc.alignment(); sd.storage_class |= sc.stc; if (sd.storage_class & STC.abstract_) @@ -5939,6 +5972,31 @@ void addEnumMembers(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) }); } +/****************************************************** + * Verifies if the given Identifier is a DRuntime hook. It uses the hooks + * defined in `id.d`. + * + * Params: + * id = Identifier to verify + * Returns: + * true if `id` is a DRuntime hook + * false otherwise + */ +private bool isDRuntimeHook(Identifier id) +{ + return id == Id._d_HookTraceImpl || + id == Id._d_newclassT || id == Id._d_newclassTTrace || + id == Id._d_arraycatnTX || id == Id._d_arraycatnTXTrace || + id == Id._d_newThrowable || id == Id._d_delThrowable || + id == Id._d_arrayassign_l || id == Id._d_arrayassign_r || + id == Id._d_arraysetassign || id == Id._d_arraysetctor || + id == Id._d_arrayctor || + id == Id._d_arraysetlengthTImpl || id == Id._d_arraysetlengthT || + id == Id._d_arraysetlengthTTrace || + id == Id._d_arrayappendT || id == Id._d_arrayappendTTrace || + id == Id._d_arrayappendcTXImpl; +} + void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList argumentList) { //printf("[%s] TemplateInstance.dsymbolSemantic('%s', this=%p, gag = %d, sc = %p)\n", tempinst.loc.toChars(), tempinst.toChars(), tempinst, global.gag, sc); @@ -6440,8 +6498,20 @@ version (IN_LLVM) tempinst.deferred = &deferred; //printf("Run semantic3 on %s\n", toChars()); + + /* https://issues.dlang.org/show_bug.cgi?id=23965 + * DRuntime hooks are not deprecated, but may be used for deprecated + * types. Deprecations are disabled while analysing hooks to avoid + * spurious error messages. + */ + auto saveUseDeprecated = global.params.useDeprecated; + if (sc.isDeprecated() && isDRuntimeHook(tempinst.name)) + global.params.useDeprecated = DiagnosticReporting.off; + tempinst.trySemantic3(sc2); + global.params.useDeprecated = saveUseDeprecated; + for (size_t i = 0; i < deferred.length; i++) { //printf("+ run deferred semantic3 on %s\n", deferred[i].toChars()); @@ -7330,3 +7400,83 @@ PINLINE evalPragmaInline(Loc loc, Scope* sc, Expressions* args) else return PINLINE.never; } + +/*************************************************** + * Set up loc for a parse of a mixin. Append the input text to the mixin. + * Params: + * input = mixin text + * loc = location to adjust + * mixinOut = sink for mixin text data + * Returns: + * adjusted loc suitable for Parser + */ + +Loc adjustLocForMixin(const(char)[] input, ref const Loc loc, ref Output mixinOut) +{ + Loc result; + if (mixinOut.doOutput) + { + const lines = mixinOut.bufferLines; + writeMixin(input, loc, mixinOut.bufferLines, *mixinOut.buffer); + result = Loc(mixinOut.name.ptr, lines + 2, loc.charnum); + } + else if (loc.filename) + { + /* Create a pseudo-filename for the mixin string, as it may not even exist + * in the source file. + */ + auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1; + char* filename = cast(char*)mem.xmalloc(len); + snprintf(filename, len, "%s-mixin-%d", loc.filename, cast(int)loc.linnum); + result = Loc(filename, loc.linnum, loc.charnum); + } + else + result = loc; + return result; +} + +/************************************** + * Append source code text to output for better debugging. + * Canonicalize line endings. + * Params: + * s = source code text + * loc = location of source code text + * lines = line count to update + * output = sink for output + */ +private void writeMixin(const(char)[] s, ref const Loc loc, ref int lines, ref OutBuffer buf) +{ + buf.writestring("// expansion at "); + buf.writestring(loc.toChars()); + buf.writenl(); + + ++lines; + + // write by line to create consistent line endings + size_t lastpos = 0; + for (size_t i = 0; i < s.length; ++i) + { + // detect LF and CRLF + const c = s[i]; + if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n')) + { + buf.writestring(s[lastpos .. i]); + buf.writenl(); + ++lines; + if (c == '\r') + ++i; + lastpos = i + 1; + } + } + + if(lastpos < s.length) + buf.writestring(s[lastpos .. $]); + + if (s.length == 0 || s[$-1] != '\n') + { + buf.writenl(); // ensure empty line after expansion + ++lines; + } + buf.writenl(); + ++lines; +} diff --git a/dmd/dtemplate.d b/dmd/dtemplate.d index 8d173673cf4..b0e616c87a1 100644 --- a/dmd/dtemplate.d +++ b/dmd/dtemplate.d @@ -757,9 +757,6 @@ else const(char)* toCharsMaybeConstraints(bool includeConstraints) const { - if (literal) - return Dsymbol.toChars(); - OutBuffer buf; HdrGenState hgs; @@ -1874,19 +1871,16 @@ else Type taai; if (argtype.ty == Tarray && (prmtype.ty == Tsarray || prmtype.ty == Taarray && (taai = (cast(TypeAArray)prmtype).index).ty == Tident && (cast(TypeIdentifier)taai).idents.length == 0)) { - if (farg.op == EXP.string_) + if (StringExp se = farg.isStringExp()) { - StringExp se = cast(StringExp)farg; argtype = se.type.nextOf().sarrayOf(se.len); } - else if (farg.op == EXP.arrayLiteral) + else if (ArrayLiteralExp ae = farg.isArrayLiteralExp()) { - ArrayLiteralExp ae = cast(ArrayLiteralExp)farg; argtype = ae.type.nextOf().sarrayOf(ae.elements.length); } - else if (farg.op == EXP.slice) + else if (SliceExp se = farg.isSliceExp()) { - SliceExp se = cast(SliceExp)farg; if (Type tsa = toStaticArrayType(se)) argtype = tsa; } @@ -2299,18 +2293,23 @@ else Declaration d; VarDeclaration v = null; - if (ea && ea.op == EXP.type) - ta = ea.type; - else if (ea && ea.op == EXP.scope_) - sa = (cast(ScopeExp)ea).sds; - else if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_)) - sa = (cast(ThisExp)ea).var; - else if (ea && ea.op == EXP.function_) + if (ea) { - if ((cast(FuncExp)ea).td) - sa = (cast(FuncExp)ea).td; - else - sa = (cast(FuncExp)ea).fd; + if (ea.op == EXP.type) + ta = ea.type; + else if (auto se = ea.isScopeExp()) + sa = se.sds; + else if (auto te = ea.isThisExp()) + sa = te.var; + else if (auto se = ea.isSuperExp()) + sa = se.var; + else if (auto fe = ea.isFuncExp()) + { + if (fe.td) + sa = fe.td; + else + sa = fe.fd; + } } if (ta) @@ -3872,9 +3871,9 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param if (tparam.ty == Tsarray) { TypeSArray tsa = cast(TypeSArray)tparam; - if (tsa.dim.op == EXP.variable && (cast(VarExp)tsa.dim).var.storage_class & STC.templateparameter) + if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) { - Identifier id = (cast(VarExp)tsa.dim).var.ident; + Identifier id = tsa.dim.isVarExp().var.ident; i = templateIdentifierLookup(id, parameters); assert(i != IDX_NOTFOUND); tp = (*parameters)[i]; @@ -4266,12 +4265,12 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, TemplateParameters* param /* If it is one of the template parameters for this template, * we should not attempt to interpret it. It already has a value. */ - if (e2.op == EXP.variable && ((cast(VarExp)e2).var.storage_class & STC.templateparameter)) + if (e2.op == EXP.variable && (e2.isVarExp().var.storage_class & STC.templateparameter)) { /* * (T:Number!(e2), int e2) */ - j = templateIdentifierLookup((cast(VarExp)e2).var.ident, parameters); + j = templateIdentifierLookup(e2.isVarExp().var.ident, parameters); if (j != IDX_NOTFOUND) goto L1; // The template parameter was not from this template @@ -5252,7 +5251,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(DotTemplateInstanceExp e) { //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.ti.tiargs) { foreach (oa; *e.ti.tiargs) @@ -5270,7 +5269,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(CallExp e) { //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.arguments) { foreach (ea; *e.arguments) @@ -5285,7 +5284,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(CastExp e) { //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); // e.to can be null for cast() with no type if (!result && e.to) result = e.to.reliesOnTemplateParameters(tparams); @@ -5294,7 +5293,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(SliceExp e) { //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.lwr) e.lwr.accept(this); if (!result && e.upr) @@ -5312,7 +5311,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(ArrayExp e) { //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars()); - visit(cast(UnaExp)e); + visit(e.isUnaExp()); if (!result && e.arguments) { foreach (ea; *e.arguments) @@ -5333,7 +5332,7 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars()); e.econd.accept(this); if (!result) - visit(cast(BinExp)e); + visit(e.isBinExp()); } } @@ -6748,7 +6747,7 @@ version (IN_LLVM) ea = ea.expressionSemantic(sc); // must not interpret the args, excepting template parameters - if (ea.op != EXP.variable || ((cast(VarExp)ea).var.storage_class & STC.templateparameter)) + if (!ea.isVarExp() || (ea.isVarExp().var.storage_class & STC.templateparameter)) { ea = ea.optimize(WANTvalue); } @@ -6759,13 +6758,13 @@ version (IN_LLVM) ea = ea.expressionSemantic(sc); sc = sc.endCTFE(); - if (ea.op == EXP.variable) + if (auto varExp = ea.isVarExp()) { /* If the parameter is a function that is not called * explicitly, i.e. `foo!func` as opposed to `foo!func()`, * then it is a dsymbol, not the return value of `func()` */ - Declaration vd = (cast(VarExp)ea).var; + Declaration vd = varExp.var; if (auto fd = vd.isFuncDeclaration()) { sa = fd; @@ -6788,10 +6787,9 @@ version (IN_LLVM) } } //printf("-[%d] ea = %s %s\n", j, EXPtoString(ea.op).ptr, ea.toChars()); - if (ea.op == EXP.tuple) + if (TupleExp te = ea.isTupleExp()) { // Expand tuple - TupleExp te = cast(TupleExp)ea; size_t dim = te.exps.length; tiargs.remove(j); if (dim) @@ -6817,12 +6815,11 @@ version (IN_LLVM) } if (ea.op == EXP.scope_) { - sa = (cast(ScopeExp)ea).sds; + sa = ea.isScopeExp().sds; goto Ldsym; } - if (ea.op == EXP.function_) + if (FuncExp fe = ea.isFuncExp()) { - FuncExp fe = cast(FuncExp)ea; /* A function literal, that is passed to template and * already semanticed as function pointer, never requires * outer frame. So convert it to global function is valid. @@ -6844,23 +6841,23 @@ version (IN_LLVM) if (ea.op == EXP.dotVariable && !(flags & 1)) { // translate expression to dsymbol. - sa = (cast(DotVarExp)ea).var; + sa = ea.isDotVarExp().var; goto Ldsym; } - if (ea.op == EXP.template_) + if (auto te = ea.isTemplateExp()) { - sa = (cast(TemplateExp)ea).td; + sa = te.td; goto Ldsym; } if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1)) { // translate expression to dsymbol. - sa = (cast(DotTemplateExp)ea).td; + sa = ea.isDotTemplateExp().td; goto Ldsym; } - if (ea.op == EXP.dot) + if (auto de = ea.isDotExp()) { - if (auto se = (cast(DotExp)ea).e2.isScopeExp()) + if (auto se = de.e2.isScopeExp()) { sa = se.sds; goto Ldsym; @@ -7361,22 +7358,22 @@ version (IN_LLVM) Tuple va = isTuple(o); if (ea) { - if (ea.op == EXP.variable) + if (auto ve = ea.isVarExp()) { - sa = (cast(VarExp)ea).var; + sa = ve.var; goto Lsa; } - if (ea.op == EXP.this_) + if (auto te = ea.isThisExp()) { - sa = (cast(ThisExp)ea).var; + sa = te.var; goto Lsa; } - if (ea.op == EXP.function_) + if (auto fe = ea.isFuncExp()) { - if ((cast(FuncExp)ea).td) - sa = (cast(FuncExp)ea).td; + if (fe.td) + sa = fe.td; else - sa = (cast(FuncExp)ea).fd; + sa = fe.fd; goto Lsa; } // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent. @@ -7767,13 +7764,13 @@ bool definitelyValueParameter(Expression e) */ // x.y.f cannot be a value - FuncDeclaration f = (cast(DotVarExp)e).var.isFuncDeclaration(); + FuncDeclaration f = e.isDotVarExp().var.isFuncDeclaration(); if (f) return false; while (e.op == EXP.dotVariable) { - e = (cast(DotVarExp)e).e1; + e = e.isDotVarExp().e1; } // this.x.y and super.x.y couldn't possibly be valid values. if (e.op == EXP.this_ || e.op == EXP.super_) @@ -7787,7 +7784,7 @@ bool definitelyValueParameter(Expression e) if (e.op != EXP.variable) return true; - VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration(); + VarDeclaration v = e.isVarExp().var.isVarDeclaration(); // func.x.y is not an alias if (!v) return true; @@ -8282,10 +8279,15 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ Type ta = isType(oarg); RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg); Expression ea = isExpression(oarg); - if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_)) - sa = (cast(ThisExp)ea).var; - else if (ea && ea.op == EXP.scope_) - sa = (cast(ScopeExp)ea).sds; + if (ea) + { + if (auto te = ea.isThisExp()) + sa = te.var; + else if (auto se = ea.isSuperExp()) + sa = se.var; + else if (auto se = ea.isScopeExp()) + sa = se.sds; + } if (sa) { if ((cast(Dsymbol)sa).isAggregateDeclaration()) diff --git a/dmd/dtoh.d b/dmd/dtoh.d index 7c3ff4bccc2..f00b8dba86c 100644 --- a/dmd/dtoh.d +++ b/dmd/dtoh.d @@ -84,9 +84,9 @@ extern(C++) void genCppHdrFiles(ref Modules ms) m.accept(v); if (global.params.cxxhdr.fullOutput) - buf.printf("// Automatically generated by %s Compiler v%d", global.vendor.ptr, global.versionNumber()); + buf.printf("// Automatically generated by %s Compiler v%d", global.compileEnv.vendor.ptr, global.versionNumber()); else - buf.printf("// Automatically generated by %s Compiler", global.vendor.ptr); + buf.printf("// Automatically generated by %s Compiler", global.compileEnv.vendor.ptr); buf.writenl(); buf.writenl(); diff --git a/dmd/errors.d b/dmd/errors.d index 05b884c280e..f1087ad9429 100644 --- a/dmd/errors.d +++ b/dmd/errors.d @@ -73,6 +73,14 @@ class ErrorSinkCompiler : ErrorSink vdeprecationSupplemental(loc, format, ap); va_end(ap); } + + void message(const ref Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vmessage(loc, format, ap); + va_end(ap); + } } @@ -819,8 +827,11 @@ private void colorHighlightCode(ref OutBuffer buf) } ++nested; - auto gaggedErrorsSave = global.startGagging(); - scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, global.errorSink, global.vendor, global.versionNumber()); + __gshared ErrorSinkNull errorSinkNull; + if (!errorSinkNull) + errorSinkNull = new ErrorSinkNull; + + scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, errorSinkNull, &global.compileEnv); OutBuffer res; const(char)* lastp = cast(char*)buf[].ptr; //printf("colorHighlightCode('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr); @@ -871,7 +882,6 @@ private void colorHighlightCode(ref OutBuffer buf) //printf("res = '%.*s'\n", cast(int)buf.length, buf[].ptr); buf.setsize(0); buf.write(&res); - global.endGagging(gaggedErrorsSave); --nested; } diff --git a/dmd/errorsink.d b/dmd/errorsink.d index b519db7e9bc..e57c2b6e388 100644 --- a/dmd/errorsink.d +++ b/dmd/errorsink.d @@ -27,6 +27,8 @@ abstract class ErrorSink void warning(const ref Loc loc, const(char)* format, ...); + void message(const ref Loc loc, const(char)* format, ...); + void deprecation(const ref Loc loc, const(char)* format, ...); void deprecationSupplemental(const ref Loc loc, const(char)* format, ...); @@ -47,6 +49,8 @@ class ErrorSinkNull : ErrorSink void warning(const ref Loc loc, const(char)* format, ...) { } + void message(const ref Loc loc, const(char)* format, ...) { } + void deprecation(const ref Loc loc, const(char)* format, ...) { } void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } @@ -117,5 +121,21 @@ class ErrorSinkStderr : ErrorSink va_end(ap); } + void message(const ref Loc loc, const(char)* format, ...) + { + const p = loc.toChars(); + if (*p) + { + fprintf(stderr, "%s: ", p); + //mem.xfree(cast(void*)p); // loc should provide the free() + } + + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + fputc('\n', stderr); + va_end(ap); + } + void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } } diff --git a/dmd/escape.d b/dmd/escape.d index 420fa7f80bb..b4b0fd92472 100644 --- a/dmd/escape.d +++ b/dmd/escape.d @@ -93,22 +93,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, bool isMutable; // true if reference to mutable } - /* Store escapeBy as static data escapeByStorage so we can keep reusing the same - * arrays rather than reallocating them. - */ - __gshared EscapeBy[] escapeByStorage; - auto escapeBy = escapeByStorage; - if (escapeBy.length < len) - { - auto newPtr = cast(EscapeBy*)mem.xrealloc(escapeBy.ptr, len * EscapeBy.sizeof); - // Clear the new section - memset(newPtr + escapeBy.length, 0, (len - escapeBy.length) * EscapeBy.sizeof); - escapeBy = newPtr[0 .. len]; - escapeByStorage = escapeBy; - } - else - escapeBy = escapeBy[0 .. len]; - + auto escapeBy = new EscapeBy[len]; const paramLength = tf.parameterList.length; // Fill in escapeBy[] with arguments[], ethis, and outerVars[] @@ -181,7 +166,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, if (!(eb.isMutable || eb2.isMutable)) return; - if (!tf.islive && !(global.params.useDIP1000 == FeatureState.enabled && sc.func.setUnsafe())) + if (!tf.islive && !(global.params.useDIP1000 == FeatureState.enabled && sc.func && sc.func.setUnsafe())) return; if (!gag) @@ -228,13 +213,6 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, escape(i, eb, false); } - /* Reset the arrays in escapeBy[] so we can reuse them next time through - */ - foreach (ref eb; escapeBy) - { - eb.er.reset(); - } - return errors; } @@ -503,7 +481,7 @@ bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Pa const byRef = param.isReference() && !(param.storageClass & STC.scope_) && !(param.storageClass & STC.returnScope); // fixme: it's possible to infer returnScope without scope with vaIsFirstRef - scope e = new AssignExp(arg.loc, firstArg, arg); + auto e = new AssignExp(arg.loc, firstArg, arg); return checkAssignEscape(sc, e, gag, byRef); } @@ -2377,7 +2355,7 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) foreach (u, p; f.parameterList) { auto v = (*funcdecl.parameters)[u]; - if (!v.isScope() && inferScope(v)) + if (!v.isScope() && v.type.hasPointers() && inferScope(v)) { //printf("Inferring scope for %s\n", v.toChars()); p.storageClass |= STC.scope_ | STC.scopeinferred; @@ -2497,6 +2475,9 @@ bool isReferenceToMutable(Type t) } break; + case Tnull: + return false; + default: assert(0); } diff --git a/dmd/expression.d b/dmd/expression.d index b27afc7f42b..9b8e6a1a3a6 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -721,20 +721,21 @@ enum WANTexpand = 1; // expand const/immutable variables if possible // IN_LLVM: instantiated in gen/asm-x86.h (`Handled = createExpression(...)`) extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode { - const EXP op; // to minimize use of dynamic_cast - ubyte size; // # of bytes in Expression so we can copy() it - bool parens; // if this is a parenthesized expression Type type; // !=null means that semantic() has been run Loc loc; // file location + const EXP op; // to minimize use of dynamic_cast + bool parens; // if this is a parenthesized expression - extern (D) this(const ref Loc loc, EXP op, int size) scope + extern (D) this(const ref Loc loc, EXP op) scope { //printf("Expression::Expression(op = %d) this = %p\n", op, this); this.loc = loc; this.op = op; - this.size = cast(ubyte)size; } + /// Returns: class instance size of this expression (implemented manually because `extern(C++)`) + final size_t size() nothrow @nogc pure @safe const { return expSize[op]; } + static void _init() { CTFEExp.cantexp = new CTFEExp(EXP.cantExpression); @@ -1220,12 +1221,15 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode return false; // If the call has a pure parent, then the called func must be pure. - if (!f.isPure() && checkImpure(sc)) + if (!f.isPure() && checkImpure(sc, loc, null, f)) { error("`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_); + checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure"); return true; } @@ -1356,7 +1360,7 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode if (v.ident == Id.gate) return false; - if (checkImpure(sc)) + if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v)) { error("`pure` %s `%s` cannot access mutable static data `%s`", sc.func.kind(), sc.func.toPrettyChars(), v.toChars()); @@ -1432,11 +1436,11 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode 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) + 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()); + : sc.func.setImpure(loc, fmt, arg0)); } /********************************************* @@ -1485,7 +1489,8 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode error("`@safe` %s `%s` cannot call `@system` %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), f.kind(), prettyChars); - f.errorSupplementalInferredSafety(/*max depth*/ 10, /*deprecation*/ false); + if (!f.isDtorDeclaration) + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe); .errorSupplemental(f.loc, "`%s` is declared here", prettyChars); checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system"); @@ -1499,7 +1504,7 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode if (sc.func.isSafeBypassingInference()) { .deprecation(this.loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars()); - errorSupplementalInferredSafety(f, 10, true); + errorSupplementalInferredAttr(f, 10, true, STC.safe); } else if (!sc.func.safetyViolation) { @@ -1529,7 +1534,7 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode if (!f.isNogc()) { - if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGC()) + if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f)) { if (loc.linnum == 0) // e.g. implicitly generated dtor loc = sc.func.loc; @@ -1538,10 +1543,15 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode // 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_newclassT)) + || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) + { error("`@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); + } + checkOverridenDtor(sc, f, dd => dd.type.toTypeFunction().isnogc, "non-@nogc"); return true; @@ -1776,6 +1786,7 @@ extern (C++) /* IN_LLVM abstract */ class Expression : ASTNode inout(PostExp) isPostExp() { return (op == EXP.plusPlus || op == EXP.minusMinus) ? cast(typeof(return))this : null; } inout(PreExp) isPreExp() { return (op == EXP.prePlusPlus || op == EXP.preMinusMinus) ? cast(typeof(return))this : null; } inout(AssignExp) isAssignExp() { return op == EXP.assign ? cast(typeof(return))this : null; } + inout(LoweredAssignExp) isLoweredAssignExp() { return op == EXP.loweredAssignExp ? cast(typeof(return))this : null; } inout(ConstructExp) isConstructExp() { return op == EXP.construct ? cast(typeof(return))this : null; } inout(BlitExp) isBlitExp() { return op == EXP.blit ? cast(typeof(return))this : null; } inout(AddAssignExp) isAddAssignExp() { return op == EXP.addAssign ? cast(typeof(return))this : null; } @@ -1867,7 +1878,7 @@ extern (C++) final class IntegerExp : Expression extern (D) this(const ref Loc loc, dinteger_t value, Type type) { - super(loc, EXP.int64, __traits(classInstanceSize, IntegerExp)); + super(loc, EXP.int64); //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : ""); assert(type); if (!type.isscalar()) @@ -1883,7 +1894,7 @@ extern (C++) final class IntegerExp : Expression extern (D) this(dinteger_t value) { - super(Loc.initial, EXP.int64, __traits(classInstanceSize, IntegerExp)); + super(Loc.initial, EXP.int64); this.type = Type.tint32; this.value = cast(int)value; } @@ -2083,7 +2094,7 @@ extern (C++) final class ErrorExp : Expression { private extern (D) this() { - super(Loc.initial, EXP.error, __traits(classInstanceSize, ErrorExp)); + super(Loc.initial, EXP.error); type = Type.terror; } @@ -2131,7 +2142,7 @@ extern (C++) final class VoidInitExp : Expression extern (D) this(VarDeclaration var) { - super(var.loc, EXP.void_, __traits(classInstanceSize, VoidInitExp)); + super(var.loc, EXP.void_); this.var = var; this.type = var.type; } @@ -2157,7 +2168,7 @@ extern (C++) final class RealExp : Expression extern (D) this(const ref Loc loc, real_t value, Type type) { - super(loc, EXP.float64, __traits(classInstanceSize, RealExp)); + super(loc, EXP.float64); //printf("RealExp::RealExp(%Lg)\n", value); this.value = value; this.type = type; @@ -2240,7 +2251,7 @@ extern (C++) final class ComplexExp : Expression extern (D) this(const ref Loc loc, complex_t value, Type type) { - super(loc, EXP.complex80, __traits(classInstanceSize, ComplexExp)); + super(loc, EXP.complex80); this.value = value; this.type = type; //printf("ComplexExp::ComplexExp(%s)\n", toChars()); @@ -2331,7 +2342,7 @@ extern (C++) class IdentifierExp : Expression extern (D) this(const ref Loc loc, Identifier ident) scope { - super(loc, EXP.identifier, __traits(classInstanceSize, IdentifierExp)); + super(loc, EXP.identifier); this.ident = ident; } @@ -2384,7 +2395,7 @@ extern (C++) final class DsymbolExp : Expression extern (D) this(const ref Loc loc, Dsymbol s, bool hasOverloads = true) { - super(loc, EXP.dSymbol, __traits(classInstanceSize, DsymbolExp)); + super(loc, EXP.dSymbol); this.s = s; this.hasOverloads = hasOverloads; } @@ -2414,13 +2425,13 @@ extern (C++) class ThisExp : Expression extern (D) this(const ref Loc loc) { - super(loc, EXP.this_, __traits(classInstanceSize, ThisExp)); + super(loc, EXP.this_); //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); } this(const ref Loc loc, const EXP tok) { - super(loc, tok, __traits(classInstanceSize, ThisExp)); + super(loc, tok); //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); } @@ -2486,7 +2497,7 @@ extern (C++) final class NullExp : Expression { extern (D) this(const ref Loc loc, Type type = null) scope { - super(loc, EXP.null_, __traits(classInstanceSize, NullExp)); + super(loc, EXP.null_); this.type = type; } @@ -2530,6 +2541,8 @@ extern (C++) final class NullExp : Expression */ extern (C++) final class StringExp : Expression { + char postfix = NoPostfix; // 'c', 'w', 'd' + OwnedBy ownedByCtfe = OwnedBy.code; private union { char* string; // if sz == 1 @@ -2538,14 +2551,22 @@ extern (C++) final class StringExp : Expression } // (const if ownedByCtfe == OwnedBy.code) size_t len; // number of code units ubyte sz = 1; // 1: char, 2: wchar, 4: dchar - ubyte committed; // !=0 if type is committed + + /** + * Whether the string literal's type is fixed + * Example: + * --- + * wstring x = "abc"; // OK, string literal is flexible + * wstring y = cast(string) "abc"; // Error: type was committed after cast + * --- + */ + bool committed; + enum char NoPostfix = 0; - char postfix = NoPostfix; // 'c', 'w', 'd' - OwnedBy ownedByCtfe = OwnedBy.code; extern (D) this(const ref Loc loc, const(void)[] string) scope { - super(loc, EXP.string_, __traits(classInstanceSize, StringExp)); + super(loc, EXP.string_); this.string = cast(char*)string.ptr; // note that this.string should be const this.len = string.length; this.sz = 1; // work around LDC bug #1286 @@ -2553,7 +2574,7 @@ extern (C++) final class StringExp : Expression extern (D) this(const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix) scope { - super(loc, EXP.string_, __traits(classInstanceSize, StringExp)); + super(loc, EXP.string_); this.string = cast(char*)string.ptr; // note that this.string should be const this.len = len; this.sz = sz; @@ -2751,7 +2772,7 @@ extern (C++) final class StringExp : Expression if (sz != 1) { // Convert to UTF-8 string - committed = 0; + committed = false; Expression e = castTo(sc, Type.tchar.arrayOf()); e = e.optimize(WANTvalue); auto se = e.isStringExp(); @@ -2940,7 +2961,7 @@ extern (C++) final class TupleExp : Expression extern (D) this(const ref Loc loc, Expression e0, Expressions* exps) { - super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp)); + super(loc, EXP.tuple); //printf("TupleExp(this = %p)\n", this); this.e0 = e0; this.exps = exps; @@ -2948,14 +2969,14 @@ extern (C++) final class TupleExp : Expression extern (D) this(const ref Loc loc, Expressions* exps) { - super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp)); + super(loc, EXP.tuple); //printf("TupleExp(this = %p)\n", this); this.exps = exps; } extern (D) this(const ref Loc loc, TupleDeclaration tup) { - super(loc, EXP.tuple, __traits(classInstanceSize, TupleExp)); + super(loc, EXP.tuple); this.exps = new Expressions(); this.exps.reserve(tup.objects.length); @@ -3032,6 +3053,9 @@ extern (C++) final class TupleExp : Expression */ extern (C++) final class ArrayLiteralExp : Expression { + OwnedBy ownedByCtfe = OwnedBy.code; + bool onstack = false; + /** If !is null, elements[] can be sparse and basis is used for the * "default" element value. In other words, non-null elements[i] overrides * this 'basis' value. @@ -3039,19 +3063,17 @@ extern (C++) final class ArrayLiteralExp : Expression Expression basis; Expressions* elements; - OwnedBy ownedByCtfe = OwnedBy.code; - bool onstack = false; extern (D) this(const ref Loc loc, Type type, Expressions* elements) { - super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + super(loc, EXP.arrayLiteral); this.type = type; this.elements = elements; } extern (D) this(const ref Loc loc, Type type, Expression e) { - super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + super(loc, EXP.arrayLiteral); this.type = type; elements = new Expressions(); elements.push(e); @@ -3059,7 +3081,7 @@ extern (C++) final class ArrayLiteralExp : Expression extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements) { - super(loc, EXP.arrayLiteral, __traits(classInstanceSize, ArrayLiteralExp)); + super(loc, EXP.arrayLiteral); this.type = type; this.basis = basis; this.elements = elements; @@ -3197,14 +3219,14 @@ extern (C++) final class ArrayLiteralExp : Expression */ extern (C++) final class AssocArrayLiteralExp : Expression { + OwnedBy ownedByCtfe = OwnedBy.code; + Expressions* keys; Expressions* values; - OwnedBy ownedByCtfe = OwnedBy.code; - extern (D) this(const ref Loc loc, Expressions* keys, Expressions* values) { - super(loc, EXP.assocArrayLiteral, __traits(classInstanceSize, AssocArrayLiteralExp)); + super(loc, EXP.assocArrayLiteral); assert(keys.length == values.length); this.keys = keys; this.values = values; @@ -3272,18 +3294,26 @@ extern (C++) final class StructLiteralExp : Expression Expressions* elements; /// parallels sd.fields[] with null entries for fields to skip Type stype; /// final type of result (can be different from sd's type) + // `inlineCopy` is only used temporarily in the `inline.d` pass, + // while `sym` is only used in `e2ir/s2ir/tocsym` which comes after + union + { version (IN_LLVM) { - // With the introduction of pointers returned from CTFE, struct literals can - // now contain pointers to themselves. While in toElem, contains a pointer - // to the memory used to build the literal for resolving such references. - void* inProgressMemory; // llvm::Value* + // With the introduction of pointers returned from CTFE, struct literals can + // now contain pointers to themselves. While in toElem, contains a pointer + // to the memory used to build the literal for resolving such references. + void* inProgressMemory; // llvm::Value* } else { - Symbol* sym; /// back end symbol to initialize with literal + Symbol* sym; /// back end symbol to initialize with literal } + /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. + StructLiteralExp inlinecopy; + } + /** pointer to the origin instance of the expression. * once a new expression is created, origin is set to 'this'. * anytime when an expression copy is created, 'origin' pointer is set to @@ -3291,15 +3321,13 @@ else */ StructLiteralExp origin; - /// those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. - StructLiteralExp inlinecopy; /** anytime when recursive function is calling, 'stageflags' marks with bit flag of * current stage and unmarks before return from this function. * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' * (with infinite recursion) of this expression. */ - int stageflags; + ubyte stageflags; bool useStaticInit; /// if this is true, use the StructDeclaration's init symbol bool isOriginal = false; /// used when moving instances to indicate `this is this.origin` @@ -3307,7 +3335,7 @@ else extern (D) this(const ref Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null) { - super(loc, EXP.structLiteral, __traits(classInstanceSize, StructLiteralExp)); + super(loc, EXP.structLiteral); this.sd = sd; if (!elements) elements = new Expressions(); @@ -3486,7 +3514,7 @@ extern (C++) final class CompoundLiteralExp : Expression extern (D) this(const ref Loc loc, Type type_name, Initializer initializer) { - super(loc, EXP.compoundLiteral, __traits(classInstanceSize, CompoundLiteralExp)); + super(loc, EXP.compoundLiteral); super.type = type_name; this.initializer = initializer; //printf("CompoundLiteralExp::CompoundLiteralExp(%s)\n", toChars()); @@ -3505,7 +3533,7 @@ extern (C++) final class TypeExp : Expression { extern (D) this(const ref Loc loc, Type type) { - super(loc, EXP.type, __traits(classInstanceSize, TypeExp)); + super(loc, EXP.type); //printf("TypeExp::TypeExp(%s)\n", type.toChars()); this.type = type; } @@ -3547,7 +3575,7 @@ extern (C++) final class ScopeExp : Expression extern (D) this(const ref Loc loc, ScopeDsymbol sds) { - super(loc, EXP.scope_, __traits(classInstanceSize, ScopeExp)); + super(loc, EXP.scope_); //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds.toChars()); //static int count; if (++count == 38) *(char*)0=0; this.sds = sds; @@ -3602,7 +3630,7 @@ extern (C++) final class TemplateExp : Expression extern (D) this(const ref Loc loc, TemplateDeclaration td, FuncDeclaration fd = null) { - super(loc, EXP.template_, __traits(classInstanceSize, TemplateExp)); + super(loc, EXP.template_); //printf("TemplateExp(): %s\n", td.toChars()); this.td = td; this.fd = fd; @@ -3663,7 +3691,7 @@ extern (C++) final class NewExp : Expression extern (D) this(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) { - super(loc, EXP.new_, __traits(classInstanceSize, NewExp)); + super(loc, EXP.new_); this.thisexp = thisexp; this.newtype = newtype; this.arguments = arguments; @@ -3701,7 +3729,7 @@ extern (C++) final class NewAnonClassExp : Expression extern (D) this(const ref Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments) { - super(loc, EXP.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp)); + super(loc, EXP.newAnonymousClass); this.thisexp = thisexp; this.cd = cd; this.arguments = arguments; @@ -3726,9 +3754,9 @@ extern (C++) class SymbolExp : Expression Dsymbol originalScope; // original scope before inlining bool hasOverloads; - extern (D) this(const ref Loc loc, EXP op, int size, Declaration var, bool hasOverloads) + extern (D) this(const ref Loc loc, EXP op, Declaration var, bool hasOverloads) { - super(loc, op, size); + super(loc, op); assert(var); this.var = var; this.hasOverloads = hasOverloads; @@ -3757,7 +3785,7 @@ extern (C++) final class SymOffExp : SymbolExp .error(loc, "need `this` for address of `%s`", v.toChars()); hasOverloads = false; } - super(loc, EXP.symbolOffset, __traits(classInstanceSize, SymOffExp), var, hasOverloads); + super(loc, EXP.symbolOffset, var, hasOverloads); this.offset = offset; } @@ -3792,7 +3820,7 @@ extern (C++) final class VarExp : SymbolExp if (var.isVarDeclaration()) hasOverloads = false; - super(loc, EXP.variable, __traits(classInstanceSize, VarExp), var, hasOverloads); + super(loc, EXP.variable, var, hasOverloads); //printf("VarExp(this = %p, '%s', loc = %s)\n", this, var.toChars(), loc.toChars()); //if (strcmp(var.ident.toChars(), "func") == 0) assert(0); this.type = var.type; @@ -3876,7 +3904,7 @@ extern (C++) final class OverExp : Expression extern (D) this(const ref Loc loc, OverloadSet s) { - super(loc, EXP.overloadSet, __traits(classInstanceSize, OverExp)); + super(loc, EXP.overloadSet); //printf("OverExp(this = %p, '%s')\n", this, var.toChars()); vars = s; type = Type.tvoid; @@ -3910,7 +3938,7 @@ extern (C++) final class FuncExp : Expression extern (D) this(const ref Loc loc, Dsymbol s) { - super(loc, EXP.function_, __traits(classInstanceSize, FuncExp)); + super(loc, EXP.function_); this.td = s.isTemplateDeclaration(); this.fd = s.isFuncLiteralDeclaration(); if (td) @@ -4209,7 +4237,7 @@ extern (C++) final class DeclarationExp : Expression extern (D) this(const ref Loc loc, Dsymbol declaration) { - super(loc, EXP.declaration, __traits(classInstanceSize, DeclarationExp)); + super(loc, EXP.declaration); this.declaration = declaration; } @@ -4242,7 +4270,7 @@ extern (C++) final class TypeidExp : Expression extern (D) this(const ref Loc loc, RootObject o) { - super(loc, EXP.typeid_, __traits(classInstanceSize, TypeidExp)); + super(loc, EXP.typeid_); this.obj = o; } @@ -4267,7 +4295,7 @@ extern (C++) final class TraitsExp : Expression extern (D) this(const ref Loc loc, Identifier ident, Objects* args) { - super(loc, EXP.traits, __traits(classInstanceSize, TraitsExp)); + super(loc, EXP.traits); this.ident = ident; this.args = args; } @@ -4292,7 +4320,7 @@ extern (C++) final class HaltExp : Expression { extern (D) this(const ref Loc loc) { - super(loc, EXP.halt, __traits(classInstanceSize, HaltExp)); + super(loc, EXP.halt); } override void accept(Visitor v) @@ -4316,7 +4344,7 @@ extern (C++) final class IsExp : Expression extern (D) this(const ref Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters) scope { - super(loc, EXP.is_, __traits(classInstanceSize, IsExp)); + super(loc, EXP.is_); this.targ = targ; this.id = id; this.tok = tok; @@ -4352,11 +4380,10 @@ extern (C++) final class IsExp : Expression extern (C++) abstract class UnaExp : Expression { Expression e1; - Type att1; // Save alias this type to detect recursion - extern (D) this(const ref Loc loc, EXP op, int size, Expression e1) scope + extern (D) this(const ref Loc loc, EXP op, Expression e1) scope { - super(loc, op, size); + super(loc, op); this.e1 = e1; } @@ -4427,9 +4454,9 @@ extern (C++) abstract class BinExp : Expression Type att1; // Save alias this type to detect recursion Type att2; // Save alias this type to detect recursion - extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2) scope + extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope { - super(loc, op, size); + super(loc, op); this.e1 = e1; this.e2 = e2; } @@ -4718,9 +4745,9 @@ extern (C++) abstract class BinExp : Expression */ extern (C++) class BinAssignExp : BinExp { - extern (D) this(const ref Loc loc, EXP op, int size, Expression e1, Expression e2) scope + extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope { - super(loc, op, size, e1, e2); + super(loc, op, e1, e2); } override final bool isLvalue() @@ -4757,7 +4784,7 @@ extern (C++) final class MixinExp : Expression extern (D) this(const ref Loc loc, Expressions* exps) { - super(loc, EXP.mixin_, __traits(classInstanceSize, MixinExp)); + super(loc, EXP.mixin_); this.exps = exps; } @@ -4805,7 +4832,7 @@ extern (C++) final class ImportExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.import_, __traits(classInstanceSize, ImportExp), e); + super(loc, EXP.import_, e); } override void accept(Visitor v) @@ -4825,7 +4852,7 @@ extern (C++) final class AssertExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Expression msg = null) { - super(loc, EXP.assert_, __traits(classInstanceSize, AssertExp), e); + super(loc, EXP.assert_, e); this.msg = msg; } @@ -4850,7 +4877,7 @@ extern (C++) final class ThrowExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.throw_, __traits(classInstanceSize, ThrowExp), e); + super(loc, EXP.throw_, e); this.type = Type.tnoreturn; } @@ -4876,7 +4903,7 @@ extern (C++) final class DotIdExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Identifier ident) { - super(loc, EXP.dotIdentifier, __traits(classInstanceSize, DotIdExp), e); + super(loc, EXP.dotIdentifier, e); this.ident = ident; } @@ -4900,7 +4927,7 @@ extern (C++) final class DotTemplateExp : UnaExp extern (D) this(const ref Loc loc, Expression e, TemplateDeclaration td) { - super(loc, EXP.dotTemplateDeclaration, __traits(classInstanceSize, DotTemplateExp), e); + super(loc, EXP.dotTemplateDeclaration, e); this.td = td; } @@ -4934,7 +4961,7 @@ extern (C++) final class DotVarExp : UnaExp if (var.isVarDeclaration()) hasOverloads = false; - super(loc, EXP.dotVariable, __traits(classInstanceSize, DotVarExp), e); + super(loc, EXP.dotVariable, e); //printf("DotVarExp()\n"); this.var = var; this.hasOverloads = hasOverloads; @@ -5015,14 +5042,14 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Identifier name, Objects* tiargs) { - super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e); + super(loc, EXP.dotTemplateInstance, e); //printf("DotTemplateInstanceExp()\n"); this.ti = new TemplateInstance(loc, name, tiargs); } extern (D) this(const ref Loc loc, Expression e, TemplateInstance ti) { - super(loc, EXP.dotTemplateInstance, __traits(classInstanceSize, DotTemplateInstanceExp), e); + super(loc, EXP.dotTemplateInstance, e); this.ti = ti; } @@ -5115,7 +5142,7 @@ extern (C++) final class DelegateExp : UnaExp extern (D) this(const ref Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null) { - super(loc, EXP.delegate_, __traits(classInstanceSize, DelegateExp), e); + super(loc, EXP.delegate_, e); this.func = f; this.hasOverloads = hasOverloads; this.vthis2 = vthis2; @@ -5135,7 +5162,7 @@ extern (C++) final class DotTypeExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Dsymbol s) { - super(loc, EXP.dotType, __traits(classInstanceSize, DotTypeExp), e); + super(loc, EXP.dotType, e); this.sym = s; } @@ -5189,19 +5216,19 @@ extern (C++) final class CallExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Expressions* exps, Identifiers* names = null) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); this.arguments = exps; this.names = names; } extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); } extern (D) this(const ref Loc loc, Expression e, Expression earg1) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); this.arguments = new Expressions(); if (earg1) this.arguments.push(earg1); @@ -5209,7 +5236,7 @@ extern (C++) final class CallExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Expression earg1, Expression earg2) { - super(loc, EXP.call, __traits(classInstanceSize, CallExp), e); + super(loc, EXP.call, e); auto arguments = new Expressions(2); (*arguments)[0] = earg1; (*arguments)[1] = earg2; @@ -5365,7 +5392,7 @@ extern (C++) final class AddrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.address, __traits(classInstanceSize, AddrExp), e); + super(loc, EXP.address, e); } extern (D) this(const ref Loc loc, Expression e, Type t) @@ -5387,14 +5414,14 @@ extern (C++) final class PtrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e); + super(loc, EXP.star, e); //if (e.type) // type = ((TypePointer *)e.type).next; } extern (D) this(const ref Loc loc, Expression e, Type t) { - super(loc, EXP.star, __traits(classInstanceSize, PtrExp), e); + super(loc, EXP.star, e); type = t; } @@ -5440,7 +5467,7 @@ extern (C++) final class NegExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.negate, __traits(classInstanceSize, NegExp), e); + super(loc, EXP.negate, e); } override void accept(Visitor v) @@ -5456,7 +5483,7 @@ extern (C++) final class UAddExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) scope { - super(loc, EXP.uadd, __traits(classInstanceSize, UAddExp), e); + super(loc, EXP.uadd, e); } override void accept(Visitor v) @@ -5472,7 +5499,7 @@ extern (C++) final class ComExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.tilde, __traits(classInstanceSize, ComExp), e); + super(loc, EXP.tilde, e); } override void accept(Visitor v) @@ -5488,7 +5515,7 @@ extern (C++) final class NotExp : UnaExp { extern (D) this(const ref Loc loc, Expression e) { - super(loc, EXP.not, __traits(classInstanceSize, NotExp), e); + super(loc, EXP.not, e); } override void accept(Visitor v) @@ -5508,7 +5535,7 @@ extern (C++) final class DeleteExp : UnaExp extern (D) this(const ref Loc loc, Expression e, bool isRAII) { - super(loc, EXP.delete_, __traits(classInstanceSize, DeleteExp), e); + super(loc, EXP.delete_, e); this.isRAII = isRAII; } @@ -5532,7 +5559,7 @@ extern (C++) final class CastExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Type t) { - super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e); + super(loc, EXP.cast_, e); this.to = t; } @@ -5540,7 +5567,7 @@ extern (C++) final class CastExp : UnaExp */ extern (D) this(const ref Loc loc, Expression e, ubyte mod) { - super(loc, EXP.cast_, __traits(classInstanceSize, CastExp), e); + super(loc, EXP.cast_, e); this.mod = mod; } @@ -5594,7 +5621,7 @@ extern (C++) final class VectorExp : UnaExp extern (D) this(const ref Loc loc, Expression e, Type t) { - super(loc, EXP.vector, __traits(classInstanceSize, VectorExp), e); + super(loc, EXP.vector, e); assert(t.ty == Tvector); to = cast(TypeVector)t; } @@ -5630,7 +5657,7 @@ extern (C++) final class VectorArrayExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.vectorArray, __traits(classInstanceSize, VectorArrayExp), e1); + super(loc, EXP.vectorArray, e1); } override bool isLvalue() @@ -5661,21 +5688,27 @@ extern (C++) final class SliceExp : UnaExp Expression lwr; // null if implicit [length - 1] VarDeclaration lengthVar; - bool upperIsInBounds; // true if upr <= e1.length - bool lowerIsLessThanUpper; // true if lwr <= upr - bool arrayop; // an array operation, rather than a slice + + private extern(D) static struct BitFields + { + bool upperIsInBounds; // true if upr <= e1.length + bool lowerIsLessThanUpper; // true if lwr <= upr + bool arrayop; // an array operation, rather than a slice + } + import dmd.common.bitfields : generateBitFields; + mixin(generateBitFields!(BitFields, ubyte)); /************************************************************/ extern (D) this(const ref Loc loc, Expression e1, IntervalExp ie) { - super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1); + super(loc, EXP.slice, e1); this.upr = ie ? ie.upr : null; this.lwr = ie ? ie.lwr : null; } extern (D) this(const ref Loc loc, Expression e1, Expression lwr, Expression upr) { - super(loc, EXP.slice, __traits(classInstanceSize, SliceExp), e1); + super(loc, EXP.slice, e1); this.upr = upr; this.lwr = lwr; } @@ -5725,7 +5758,7 @@ extern (C++) final class ArrayLengthExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.arrayLength, __traits(classInstanceSize, ArrayLengthExp), e1); + super(loc, EXP.arrayLength, e1); } override void accept(Visitor v) @@ -5748,7 +5781,7 @@ extern (C++) final class ArrayExp : UnaExp extern (D) this(const ref Loc loc, Expression e1, Expression index = null) { - super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1); + super(loc, EXP.array, e1); arguments = new Expressions(); if (index) arguments.push(index); @@ -5756,7 +5789,7 @@ extern (C++) final class ArrayExp : UnaExp extern (D) this(const ref Loc loc, Expression e1, Expressions* args) { - super(loc, EXP.array, __traits(classInstanceSize, ArrayExp), e1); + super(loc, EXP.array, e1); arguments = args; } @@ -5793,7 +5826,7 @@ extern (C++) final class DotExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.dot, __traits(classInstanceSize, DotExp), e1, e2); + super(loc, EXP.dot, e1, e2); } override void accept(Visitor v) @@ -5819,7 +5852,7 @@ extern (C++) final class CommaExp : BinExp extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool generated = true) { - super(loc, EXP.comma, __traits(classInstanceSize, CommaExp), e1, e2); + super(loc, EXP.comma, e1, e2); allowCommaExp = isGenerated = generated; } @@ -5888,7 +5921,7 @@ extern (C++) final class IntervalExp : Expression extern (D) this(const ref Loc loc, Expression lwr, Expression upr) { - super(loc, EXP.interval, __traits(classInstanceSize, IntervalExp)); + super(loc, EXP.interval); this.lwr = lwr; this.upr = upr; } @@ -5913,7 +5946,7 @@ extern (C++) final class DelegatePtrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.delegatePointer, __traits(classInstanceSize, DelegatePtrExp), e1); + super(loc, EXP.delegatePointer, e1); } override bool isLvalue() @@ -5951,7 +5984,7 @@ extern (C++) final class DelegateFuncptrExp : UnaExp { extern (D) this(const ref Loc loc, Expression e1) { - super(loc, EXP.delegateFunctionPointer, __traits(classInstanceSize, DelegateFuncptrExp), e1); + super(loc, EXP.delegateFunctionPointer, e1); } override bool isLvalue() @@ -5991,13 +6024,13 @@ extern (C++) final class IndexExp : BinExp extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2); + super(loc, EXP.index, e1, e2); //printf("IndexExp::IndexExp('%s')\n", toChars()); } extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool indexIsInBounds) { - super(loc, EXP.index, __traits(classInstanceSize, IndexExp), e1, e2); + super(loc, EXP.index, e1, e2); this.indexIsInBounds = indexIsInBounds; //printf("IndexExp::IndexExp('%s')\n", toChars()); } @@ -6074,7 +6107,7 @@ extern (C++) final class PostExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e) { - super(loc, op, __traits(classInstanceSize, PostExp), e, IntegerExp.literal!1); + super(loc, op, e, IntegerExp.literal!1); assert(op == EXP.minusMinus || op == EXP.plusPlus); } @@ -6091,7 +6124,7 @@ extern (C++) final class PreExp : UnaExp { extern (D) this(EXP op, const ref Loc loc, Expression e) { - super(loc, op, __traits(classInstanceSize, PreExp), e); + super(loc, op, e); assert(op == EXP.preMinusMinus || op == EXP.prePlusPlus); } @@ -6121,12 +6154,12 @@ extern (C++) class AssignExp : BinExp /* op can be EXP.assign, EXP.construct, or EXP.blit */ extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.assign, __traits(classInstanceSize, AssignExp), e1, e2); + super(loc, EXP.assign, e1, e2); } this(const ref Loc loc, EXP tok, Expression e1, Expression e2) { - super(loc, tok, __traits(classInstanceSize, AssignExp), e1, e2); + super(loc, tok, e1, e2); } override final bool isLvalue() @@ -6160,6 +6193,32 @@ extern (C++) class AssignExp : BinExp } } +/*********************************************************** + * When an assignment expression is lowered to a druntime call + * this class is used to store the lowering. + * It essentially behaves the same as an AssignExp, but it is + * used to not waste space for other AssignExp that are not + * lowered to anything. + */ +extern (C++) final class LoweredAssignExp : AssignExp +{ + Expression lowering; + extern (D) this(AssignExp exp, Expression lowering) + { + super(exp.loc, EXP.loweredAssignExp, exp.e1, exp.e2); + this.lowering = lowering; + } + + override const(char)* toChars() const + { + return lowering.toChars(); + } + override void accept(Visitor v) + { + v.visit(this); + } +} + /*********************************************************** */ extern (C++) final class ConstructExp : AssignExp @@ -6224,7 +6283,7 @@ extern (C++) final class AddAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.addAssign, __traits(classInstanceSize, AddAssignExp), e1, e2); + super(loc, EXP.addAssign, e1, e2); } override void accept(Visitor v) @@ -6240,7 +6299,7 @@ extern (C++) final class MinAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.minAssign, __traits(classInstanceSize, MinAssignExp), e1, e2); + super(loc, EXP.minAssign, e1, e2); } override void accept(Visitor v) @@ -6256,7 +6315,7 @@ extern (C++) final class MulAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.mulAssign, __traits(classInstanceSize, MulAssignExp), e1, e2); + super(loc, EXP.mulAssign, e1, e2); } override void accept(Visitor v) @@ -6272,7 +6331,7 @@ extern (C++) final class DivAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.divAssign, __traits(classInstanceSize, DivAssignExp), e1, e2); + super(loc, EXP.divAssign, e1, e2); } override void accept(Visitor v) @@ -6288,7 +6347,7 @@ extern (C++) final class ModAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.modAssign, __traits(classInstanceSize, ModAssignExp), e1, e2); + super(loc, EXP.modAssign, e1, e2); } override void accept(Visitor v) @@ -6304,7 +6363,7 @@ extern (C++) final class AndAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.andAssign, __traits(classInstanceSize, AndAssignExp), e1, e2); + super(loc, EXP.andAssign, e1, e2); } override void accept(Visitor v) @@ -6320,7 +6379,7 @@ extern (C++) final class OrAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.orAssign, __traits(classInstanceSize, OrAssignExp), e1, e2); + super(loc, EXP.orAssign, e1, e2); } override void accept(Visitor v) @@ -6336,7 +6395,7 @@ extern (C++) final class XorAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.xorAssign, __traits(classInstanceSize, XorAssignExp), e1, e2); + super(loc, EXP.xorAssign, e1, e2); } override void accept(Visitor v) @@ -6352,7 +6411,7 @@ extern (C++) final class PowAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.powAssign, __traits(classInstanceSize, PowAssignExp), e1, e2); + super(loc, EXP.powAssign, e1, e2); } override void accept(Visitor v) @@ -6368,7 +6427,7 @@ extern (C++) final class ShlAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.leftShiftAssign, __traits(classInstanceSize, ShlAssignExp), e1, e2); + super(loc, EXP.leftShiftAssign, e1, e2); } override void accept(Visitor v) @@ -6384,7 +6443,7 @@ extern (C++) final class ShrAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.rightShiftAssign, __traits(classInstanceSize, ShrAssignExp), e1, e2); + super(loc, EXP.rightShiftAssign, e1, e2); } override void accept(Visitor v) @@ -6400,7 +6459,7 @@ extern (C++) final class UshrAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.unsignedRightShiftAssign, __traits(classInstanceSize, UshrAssignExp), e1, e2); + super(loc, EXP.unsignedRightShiftAssign, e1, e2); } override void accept(Visitor v) @@ -6425,12 +6484,12 @@ extern (C++) class CatAssignExp : BinAssignExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.concatenateAssign, __traits(classInstanceSize, CatAssignExp), e1, e2); + super(loc, EXP.concatenateAssign, e1, e2); } extern (D) this(const ref Loc loc, EXP tok, Expression e1, Expression e2) { - super(loc, tok, __traits(classInstanceSize, CatAssignExp), e1, e2); + super(loc, tok, e1, e2); } override void accept(Visitor v) @@ -6482,7 +6541,7 @@ extern (C++) final class AddExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.add, __traits(classInstanceSize, AddExp), e1, e2); + super(loc, EXP.add, e1, e2); } override void accept(Visitor v) @@ -6500,7 +6559,7 @@ extern (C++) final class MinExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.min, __traits(classInstanceSize, MinExp), e1, e2); + super(loc, EXP.min, e1, e2); } override void accept(Visitor v) @@ -6516,9 +6575,11 @@ extern (C++) final class MinExp : BinExp */ extern (C++) final class CatExp : BinExp { + Expression lowering; // call to druntime hook `_d_arraycatnTX` + extern (D) this(const ref Loc loc, Expression e1, Expression e2) scope { - super(loc, EXP.concatenate, __traits(classInstanceSize, CatExp), e1, e2); + super(loc, EXP.concatenate, e1, e2); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -6543,7 +6604,7 @@ extern (C++) final class MulExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.mul, __traits(classInstanceSize, MulExp), e1, e2); + super(loc, EXP.mul, e1, e2); } override void accept(Visitor v) @@ -6561,7 +6622,7 @@ extern (C++) final class DivExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.div, __traits(classInstanceSize, DivExp), e1, e2); + super(loc, EXP.div, e1, e2); } override void accept(Visitor v) @@ -6579,7 +6640,7 @@ extern (C++) final class ModExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.mod, __traits(classInstanceSize, ModExp), e1, e2); + super(loc, EXP.mod, e1, e2); } override void accept(Visitor v) @@ -6597,7 +6658,7 @@ extern (C++) final class PowExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.pow, __traits(classInstanceSize, PowExp), e1, e2); + super(loc, EXP.pow, e1, e2); } override void accept(Visitor v) @@ -6615,7 +6676,7 @@ extern (C++) final class ShlExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.leftShift, __traits(classInstanceSize, ShlExp), e1, e2); + super(loc, EXP.leftShift, e1, e2); } override void accept(Visitor v) @@ -6633,7 +6694,7 @@ extern (C++) final class ShrExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.rightShift, __traits(classInstanceSize, ShrExp), e1, e2); + super(loc, EXP.rightShift, e1, e2); } override void accept(Visitor v) @@ -6651,7 +6712,7 @@ extern (C++) final class UshrExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.unsignedRightShift, __traits(classInstanceSize, UshrExp), e1, e2); + super(loc, EXP.unsignedRightShift, e1, e2); } override void accept(Visitor v) @@ -6669,7 +6730,7 @@ extern (C++) final class AndExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.and, __traits(classInstanceSize, AndExp), e1, e2); + super(loc, EXP.and, e1, e2); } override void accept(Visitor v) @@ -6687,7 +6748,7 @@ extern (C++) final class OrExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.or, __traits(classInstanceSize, OrExp), e1, e2); + super(loc, EXP.or, e1, e2); } override void accept(Visitor v) @@ -6705,7 +6766,7 @@ extern (C++) final class XorExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.xor, __traits(classInstanceSize, XorExp), e1, e2); + super(loc, EXP.xor, e1, e2); } override void accept(Visitor v) @@ -6724,7 +6785,7 @@ extern (C++) final class LogicalExp : BinExp { extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, LogicalExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.andAnd || op == EXP.orOr); } @@ -6746,7 +6807,7 @@ extern (C++) final class CmpExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, CmpExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.lessThan || op == EXP.lessOrEqual || op == EXP.greaterThan || op == EXP.greaterOrEqual); } @@ -6767,7 +6828,7 @@ extern (C++) final class InExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.in_, __traits(classInstanceSize, InExp), e1, e2); + super(loc, EXP.in_, e1, e2); } override void accept(Visitor v) @@ -6785,7 +6846,7 @@ extern (C++) final class RemoveExp : BinExp { extern (D) this(const ref Loc loc, Expression e1, Expression e2) { - super(loc, EXP.remove, __traits(classInstanceSize, RemoveExp), e1, e2); + super(loc, EXP.remove, e1, e2); type = Type.tbool; } @@ -6806,7 +6867,7 @@ extern (C++) final class EqualExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, EqualExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.equal || op == EXP.notEqual); } @@ -6827,7 +6888,7 @@ extern (C++) final class IdentityExp : BinExp { extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) { - super(loc, op, __traits(classInstanceSize, IdentityExp), e1, e2); + super(loc, op, e1, e2); assert(op == EXP.identity || op == EXP.notIdentity); } @@ -6848,7 +6909,7 @@ extern (C++) final class CondExp : BinExp extern (D) this(const ref Loc loc, Expression econd, Expression e1, Expression e2) scope { - super(loc, EXP.question, __traits(classInstanceSize, CondExp), e1, e2); + super(loc, EXP.question, e1, e2); this.econd = econd; } @@ -6986,9 +7047,9 @@ bool isDefaultInitOp(EXP op) pure nothrow @safe @nogc */ extern (C++) class DefaultInitExp : Expression { - extern (D) this(const ref Loc loc, EXP op, int size) + extern (D) this(const ref Loc loc, EXP op) { - super(loc, op, size); + super(loc, op); } override void accept(Visitor v) @@ -7004,7 +7065,7 @@ extern (C++) final class FileInitExp : DefaultInitExp { extern (D) this(const ref Loc loc, EXP tok) { - super(loc, tok, __traits(classInstanceSize, FileInitExp)); + super(loc, tok); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7035,7 +7096,7 @@ extern (C++) final class LineInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.line, __traits(classInstanceSize, LineInitExp)); + super(loc, EXP.line); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7058,7 +7119,7 @@ extern (C++) final class ModuleInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.moduleString, __traits(classInstanceSize, ModuleInitExp)); + super(loc, EXP.moduleString); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7083,7 +7144,7 @@ extern (C++) final class FuncInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.functionString, __traits(classInstanceSize, FuncInitExp)); + super(loc, EXP.functionString); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7114,7 +7175,7 @@ extern (C++) final class PrettyFuncInitExp : DefaultInitExp { extern (D) this(const ref Loc loc) { - super(loc, EXP.prettyFunction, __traits(classInstanceSize, PrettyFuncInitExp)); + super(loc, EXP.prettyFunction); } override Expression resolveLoc(const ref Loc loc, Scope* sc) @@ -7159,8 +7220,7 @@ extern (C++) final class ObjcClassReferenceExp : Expression extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration) { - super(loc, EXP.objcClassReference, - __traits(classInstanceSize, ObjcClassReferenceExp)); + super(loc, EXP.objcClassReference); this.classDeclaration = classDeclaration; type = objc.getRuntimeMetaclass(classDeclaration).getType(); } @@ -7183,7 +7243,7 @@ extern (C++) final class GenericExp : Expression extern (D) this(const ref Loc loc, Expression cntlExp, Types* types, Expressions* exps) { - super(loc, EXP._Generic, __traits(classInstanceSize, GenericExp)); + super(loc, EXP._Generic); this.cntlExp = cntlExp; this.types = types; this.exps = exps; @@ -7350,23 +7410,20 @@ extern(D) Modifiable checkModifiable(Expression exp, Scope* sc, ModifyFlags flag } /** - * Verify if the given identifier is any of - * _d_array{ctor,setctor,setassign,assign_l, assign_r}. + * Verify if the given identifier is _d_array{,set}ctor. * * Params: * id = the identifier to verify * * Returns: - * `true` if the identifier corresponds to a construction of assignement - * runtime hook, `false` otherwise. + * `true` if the identifier corresponds to a construction runtime hook, + * `false` otherwise. */ -bool isArrayConstructionOrAssign(const Identifier id) +bool isArrayConstruction(const Identifier id) { import dmd.id : Id; - return id == Id._d_arrayctor || id == Id._d_arraysetctor || - id == Id._d_arrayassign_l || id == Id._d_arrayassign_r || - id == Id._d_arraysetassign; + return id == Id._d_arrayctor || id == Id._d_arraysetctor; } /****************************** @@ -7418,3 +7475,135 @@ private enum EbinaryAssign = EXP.leftShiftAssign, EXP.rightShiftAssign, EXP.unsignedRightShiftAssign, EXP.concatenateAssign, EXP.concatenateElemAssign, EXP.concatenateDcharAssign, ]; + +/// Given a member of the EXP enum, get the class instance size of the corresponding Expression class. +/// Needed because the classes are `extern(C++)` +private immutable ubyte[EXP.max+1] expSize = [ + EXP.reserved: 0, + EXP.negate: __traits(classInstanceSize, NegExp), + EXP.cast_: __traits(classInstanceSize, CastExp), + EXP.null_: __traits(classInstanceSize, NullExp), + EXP.assert_: __traits(classInstanceSize, AssertExp), + EXP.array: __traits(classInstanceSize, ArrayExp), + EXP.call: __traits(classInstanceSize, CallExp), + EXP.address: __traits(classInstanceSize, AddrExp), + EXP.type: __traits(classInstanceSize, TypeExp), + EXP.throw_: __traits(classInstanceSize, ThrowExp), + EXP.new_: __traits(classInstanceSize, NewExp), + EXP.delete_: __traits(classInstanceSize, DeleteExp), + EXP.star: __traits(classInstanceSize, PtrExp), + EXP.symbolOffset: __traits(classInstanceSize, SymOffExp), + EXP.variable: __traits(classInstanceSize, VarExp), + EXP.dotVariable: __traits(classInstanceSize, DotVarExp), + EXP.dotIdentifier: __traits(classInstanceSize, DotIdExp), + EXP.dotTemplateInstance: __traits(classInstanceSize, DotTemplateInstanceExp), + EXP.dotType: __traits(classInstanceSize, DotTypeExp), + EXP.slice: __traits(classInstanceSize, SliceExp), + EXP.arrayLength: __traits(classInstanceSize, ArrayLengthExp), + EXP.dollar: __traits(classInstanceSize, DollarExp), + EXP.template_: __traits(classInstanceSize, TemplateExp), + EXP.dotTemplateDeclaration: __traits(classInstanceSize, DotTemplateExp), + EXP.declaration: __traits(classInstanceSize, DeclarationExp), + EXP.dSymbol: __traits(classInstanceSize, DsymbolExp), + EXP.typeid_: __traits(classInstanceSize, TypeidExp), + EXP.uadd: __traits(classInstanceSize, UAddExp), + EXP.remove: __traits(classInstanceSize, RemoveExp), + EXP.newAnonymousClass: __traits(classInstanceSize, NewAnonClassExp), + EXP.arrayLiteral: __traits(classInstanceSize, ArrayLiteralExp), + EXP.assocArrayLiteral: __traits(classInstanceSize, AssocArrayLiteralExp), + EXP.structLiteral: __traits(classInstanceSize, StructLiteralExp), + EXP.classReference: __traits(classInstanceSize, ClassReferenceExp), + EXP.thrownException: __traits(classInstanceSize, ThrownExceptionExp), + EXP.delegatePointer: __traits(classInstanceSize, DelegatePtrExp), + EXP.delegateFunctionPointer: __traits(classInstanceSize, DelegateFuncptrExp), + EXP.lessThan: __traits(classInstanceSize, CmpExp), + EXP.greaterThan: __traits(classInstanceSize, CmpExp), + EXP.lessOrEqual: __traits(classInstanceSize, CmpExp), + EXP.greaterOrEqual: __traits(classInstanceSize, CmpExp), + EXP.equal: __traits(classInstanceSize, EqualExp), + EXP.notEqual: __traits(classInstanceSize, EqualExp), + EXP.identity: __traits(classInstanceSize, IdentityExp), + EXP.notIdentity: __traits(classInstanceSize, IdentityExp), + EXP.index: __traits(classInstanceSize, IndexExp), + EXP.is_: __traits(classInstanceSize, IsExp), + EXP.leftShift: __traits(classInstanceSize, ShlExp), + EXP.rightShift: __traits(classInstanceSize, ShrExp), + EXP.leftShiftAssign: __traits(classInstanceSize, ShlAssignExp), + EXP.rightShiftAssign: __traits(classInstanceSize, ShrAssignExp), + EXP.unsignedRightShift: __traits(classInstanceSize, UshrExp), + EXP.unsignedRightShiftAssign: __traits(classInstanceSize, UshrAssignExp), + EXP.concatenate: __traits(classInstanceSize, CatExp), + EXP.concatenateAssign: __traits(classInstanceSize, CatAssignExp), + EXP.concatenateElemAssign: __traits(classInstanceSize, CatElemAssignExp), + EXP.concatenateDcharAssign: __traits(classInstanceSize, CatDcharAssignExp), + EXP.add: __traits(classInstanceSize, AddExp), + EXP.min: __traits(classInstanceSize, MinExp), + EXP.addAssign: __traits(classInstanceSize, AddAssignExp), + EXP.minAssign: __traits(classInstanceSize, MinAssignExp), + EXP.mul: __traits(classInstanceSize, MulExp), + EXP.div: __traits(classInstanceSize, DivExp), + EXP.mod: __traits(classInstanceSize, ModExp), + EXP.mulAssign: __traits(classInstanceSize, MulAssignExp), + EXP.divAssign: __traits(classInstanceSize, DivAssignExp), + EXP.modAssign: __traits(classInstanceSize, ModAssignExp), + EXP.and: __traits(classInstanceSize, AndExp), + EXP.or: __traits(classInstanceSize, OrExp), + EXP.xor: __traits(classInstanceSize, XorExp), + EXP.andAssign: __traits(classInstanceSize, AndAssignExp), + EXP.orAssign: __traits(classInstanceSize, OrAssignExp), + EXP.xorAssign: __traits(classInstanceSize, XorAssignExp), + EXP.assign: __traits(classInstanceSize, AssignExp), + EXP.not: __traits(classInstanceSize, NotExp), + EXP.tilde: __traits(classInstanceSize, ComExp), + EXP.plusPlus: __traits(classInstanceSize, PostExp), + EXP.minusMinus: __traits(classInstanceSize, PostExp), + EXP.construct: __traits(classInstanceSize, ConstructExp), + EXP.blit: __traits(classInstanceSize, BlitExp), + EXP.dot: __traits(classInstanceSize, DotExp), + EXP.comma: __traits(classInstanceSize, CommaExp), + EXP.question: __traits(classInstanceSize, CondExp), + EXP.andAnd: __traits(classInstanceSize, LogicalExp), + EXP.orOr: __traits(classInstanceSize, LogicalExp), + EXP.prePlusPlus: __traits(classInstanceSize, PreExp), + EXP.preMinusMinus: __traits(classInstanceSize, PreExp), + EXP.identifier: __traits(classInstanceSize, IdentifierExp), + EXP.string_: __traits(classInstanceSize, StringExp), + EXP.this_: __traits(classInstanceSize, ThisExp), + EXP.super_: __traits(classInstanceSize, SuperExp), + EXP.halt: __traits(classInstanceSize, HaltExp), + EXP.tuple: __traits(classInstanceSize, TupleExp), + EXP.error: __traits(classInstanceSize, ErrorExp), + EXP.void_: __traits(classInstanceSize, VoidInitExp), + EXP.int64: __traits(classInstanceSize, IntegerExp), + EXP.float64: __traits(classInstanceSize, RealExp), + EXP.complex80: __traits(classInstanceSize, ComplexExp), + EXP.import_: __traits(classInstanceSize, ImportExp), + EXP.delegate_: __traits(classInstanceSize, DelegateExp), + EXP.function_: __traits(classInstanceSize, FuncExp), + EXP.mixin_: __traits(classInstanceSize, MixinExp), + EXP.in_: __traits(classInstanceSize, InExp), + EXP.break_: __traits(classInstanceSize, CTFEExp), + EXP.continue_: __traits(classInstanceSize, CTFEExp), + EXP.goto_: __traits(classInstanceSize, CTFEExp), + EXP.scope_: __traits(classInstanceSize, ScopeExp), + EXP.traits: __traits(classInstanceSize, TraitsExp), + EXP.overloadSet: __traits(classInstanceSize, OverExp), + EXP.line: __traits(classInstanceSize, LineInitExp), + EXP.file: __traits(classInstanceSize, FileInitExp), + EXP.fileFullPath: __traits(classInstanceSize, FileInitExp), + EXP.moduleString: __traits(classInstanceSize, ModuleInitExp), + EXP.functionString: __traits(classInstanceSize, FuncInitExp), + EXP.prettyFunction: __traits(classInstanceSize, PrettyFuncInitExp), + EXP.pow: __traits(classInstanceSize, PowExp), + EXP.powAssign: __traits(classInstanceSize, PowAssignExp), + EXP.vector: __traits(classInstanceSize, VectorExp), + EXP.voidExpression: __traits(classInstanceSize, CTFEExp), + EXP.cantExpression: __traits(classInstanceSize, CTFEExp), + EXP.showCtfeContext: __traits(classInstanceSize, CTFEExp), + EXP.objcClassReference: __traits(classInstanceSize, ObjcClassReferenceExp), + EXP.vectorArray: __traits(classInstanceSize, VectorArrayExp), + EXP.compoundLiteral: __traits(classInstanceSize, CompoundLiteralExp), + EXP._Generic: __traits(classInstanceSize, GenericExp), + EXP.interval: __traits(classInstanceSize, IntervalExp), + EXP.loweredAssignExp : __traits(classInstanceSize, LoweredAssignExp), +]; diff --git a/dmd/expression.h b/dmd/expression.h index b4dc9988db0..35ec908b5a5 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -38,6 +38,7 @@ class TemplateDeclaration; class ClassDeclaration; class OverloadSet; class StringExp; +class LoweredAssignExp; struct UnionExp; #ifdef IN_GCC typedef union tree_node Symbol; @@ -95,12 +96,12 @@ enum class ModifyFlags class Expression : public ASTNode { public: - EXP op; // to minimize use of dynamic_cast - unsigned char size; // # of bytes in Expression so we can copy() it - bool parens; // if this is a parenthesized expression Type *type; // !=NULL means that semantic() has been run Loc loc; // file location + EXP op; // to minimize use of dynamic_cast + d_bool parens; // if this is a parenthesized expression + size_t size() const; static void _init(); Expression *copy(); virtual Expression *syntaxCopy(); @@ -256,6 +257,7 @@ class Expression : public ASTNode UnaExp* isUnaExp(); BinExp* isBinExp(); BinAssignExp* isBinAssignExp(); + LoweredAssignExp* isLoweredAssignExp(); void accept(Visitor *v) override { v->visit(this); } }; @@ -347,7 +349,7 @@ class DsymbolExp final : public Expression { public: Dsymbol *s; - bool hasOverloads; + d_bool hasOverloads; DsymbolExp *syntaxCopy() override; bool isLvalue() override; @@ -386,12 +388,12 @@ class NullExp final : public Expression class StringExp final : public Expression { public: + utf8_t postfix; // 'c', 'w', 'd' + OwnedBy ownedByCtfe; void *string; // char, wchar, or dchar data size_t len; // number of chars, wchars, or dchars unsigned char sz; // 1: char, 2: wchar, 4: dchar - unsigned char committed; // !=0 if type is committed - utf8_t postfix; // 'c', 'w', 'd' - OwnedBy ownedByCtfe; + bool committed; // if type is committed static StringExp *create(const Loc &loc, const char *s); static StringExp *create(const Loc &loc, const void *s, d_size_t len); @@ -448,10 +450,10 @@ class TupleExp final : public Expression class ArrayLiteralExp final : public Expression { public: + OwnedBy ownedByCtfe; + d_bool onstack; Expression *basis; Expressions *elements; - OwnedBy ownedByCtfe; - bool onstack; static ArrayLiteralExp *create(const Loc &loc, Expressions *elements); static void emplace(UnionExp *pue, const Loc &loc, Expressions *elements); @@ -468,9 +470,9 @@ class ArrayLiteralExp final : public Expression class AssocArrayLiteralExp final : public Expression { public: + OwnedBy ownedByCtfe; Expressions *keys; Expressions *values; - OwnedBy ownedByCtfe; bool equals(const RootObject * const o) const override; AssocArrayLiteralExp *syntaxCopy() override; @@ -486,15 +488,21 @@ class StructLiteralExp final : public Expression Expressions *elements; // parallels sd->fields[] with NULL entries for fields to skip Type *stype; // final type of result (can be different from sd's type) + union + { #if IN_LLVM - // With the introduction of pointers returned from CTFE, struct literals can - // now contain pointers to themselves. While in toElem, contains a pointer - // to the memory used to build the literal for resolving such references. - llvm::Value *inProgressMemory; + // With the introduction of pointers returned from CTFE, struct literals can + // now contain pointers to themselves. While in toElem, contains a pointer + // to the memory used to build the literal for resolving such references. + llvm::Value *inProgressMemory; #else - Symbol *sym; // back end symbol to initialize with literal + Symbol *sym; // back end symbol to initialize with literal #endif + // those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. + StructLiteralExp *inlinecopy; + }; + /** pointer to the origin instance of the expression. * once a new expression is created, origin is set to 'this'. * anytime when an expression copy is created, 'origin' pointer is set to @@ -502,18 +510,16 @@ class StructLiteralExp final : public Expression */ StructLiteralExp *origin; - // those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. - StructLiteralExp *inlinecopy; /** anytime when recursive function is calling, 'stageflags' marks with bit flag of * current stage and unmarks before return from this function. * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' * (with infinite recursion) of this expression. */ - int stageflags; + uint8_t stageflags; - bool useStaticInit; // if this is true, use the StructDeclaration's init symbol - bool isOriginal; // used when moving instances to indicate `this is this.origin` + d_bool useStaticInit; // if this is true, use the StructDeclaration's init symbol + d_bool isOriginal; // used when moving instances to indicate `this is this.origin` OwnedBy ownedByCtfe; static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL); @@ -573,8 +579,8 @@ class NewExp final : public Expression Expression *argprefix; // expression to be evaluated just before arguments[] CtorDeclaration *member; // constructor function - bool onstack; // allocate on stack - bool thrownew; // this NewExp is the expression of a ThrowStatement + d_bool onstack; // allocate on stack + d_bool thrownew; // this NewExp is the expression of a ThrowStatement Expression *lowering; // lowered druntime hook: `_d_newclass` @@ -602,7 +608,7 @@ class SymbolExp : public Expression public: Declaration *var; Dsymbol *originalScope; - bool hasOverloads; + d_bool hasOverloads; void accept(Visitor *v) override { v->visit(this); } }; @@ -624,7 +630,7 @@ class SymOffExp final : public SymbolExp class VarExp final : public SymbolExp { public: - bool delegateWasExtracted; + d_bool delegateWasExtracted; static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true); bool equals(const RootObject * const o) const override; bool isLvalue() override; @@ -729,7 +735,6 @@ class UnaExp : public Expression { public: Expression *e1; - Type *att1; // Save alias this type to detect recursion UnaExp *syntaxCopy() override; Expression *incompatibleTypes(); @@ -800,9 +805,9 @@ class DotIdExp final : public UnaExp { public: Identifier *ident; - bool noderef; // true if the result of the expression will never be dereferenced - bool wantsym; // do not replace Symbol with its initializer during semantic() - bool arrow; // ImportC: if -> instead of . + d_bool noderef; // true if the result of the expression will never be dereferenced + d_bool wantsym; // do not replace Symbol with its initializer during semantic() + d_bool arrow; // ImportC: if -> instead of . static DotIdExp *create(const Loc &loc, Expression *e, Identifier *ident); void accept(Visitor *v) override { v->visit(this); } @@ -822,7 +827,7 @@ class DotVarExp final : public UnaExp { public: Declaration *var; - bool hasOverloads; + d_bool hasOverloads; bool isLvalue() override; Expression *toLvalue(Scope *sc, Expression *e) override; @@ -846,7 +851,7 @@ class DelegateExp final : public UnaExp { public: FuncDeclaration *func; - bool hasOverloads; + d_bool hasOverloads; VarDeclaration *vthis2; // container for multi-context @@ -867,9 +872,9 @@ class CallExp final : public UnaExp Expressions *arguments; // function arguments Identifiers *names; FuncDeclaration *f; // symbol to call - bool directcall; // true if a virtual call is devirtualized - bool inDebugStatement; // true if this was in a debug statement - bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code) + d_bool directcall; // true if a virtual call is devirtualized + d_bool inDebugStatement; // true if this was in a debug statement + d_bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code) VarDeclaration *vthis2; // container for multi-context static CallExp *create(const Loc &loc, Expression *e, Expressions *exps); @@ -928,7 +933,7 @@ class NotExp final : public UnaExp class DeleteExp final : public UnaExp { public: - bool isRAII; + d_bool isRAII; void accept(Visitor *v) override { v->visit(this); } }; @@ -973,10 +978,17 @@ class SliceExp final : public UnaExp Expression *upr; // NULL if implicit 0 Expression *lwr; // NULL if implicit [length - 1] VarDeclaration *lengthVar; - bool upperIsInBounds; // true if upr <= e1.length - bool lowerIsLessThanUpper; // true if lwr <= upr - bool arrayop; // an array operation, rather than a slice + bool upperIsInBounds() const; // true if upr <= e1.length + bool upperIsInBounds(bool v); + bool lowerIsLessThanUpper() const; // true if lwr <= upr + bool lowerIsLessThanUpper(bool v); + bool arrayop() const; // an array operation, rather than a slice + bool arrayop(bool v); +private: + uint8_t bitFields; + +public: SliceExp *syntaxCopy() override; bool isLvalue() override; Expression *toLvalue(Scope *sc, Expression *e) override; @@ -1047,9 +1059,8 @@ class DotExp final : public BinExp class CommaExp final : public BinExp { public: - bool isGenerated; - bool allowCommaExp; - + d_bool isGenerated; + d_bool allowCommaExp; bool isLvalue() override; Expression *toLvalue(Scope *sc, Expression *e) override; Expression *modifiableLvalue(Scope *sc, Expression *e) override; @@ -1082,8 +1093,8 @@ class IndexExp final : public BinExp { public: VarDeclaration *lengthVar; - bool modifiable; - bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1 + d_bool modifiable; + d_bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1 IndexExp *syntaxCopy() override; bool isLvalue() override; @@ -1133,6 +1144,15 @@ class ConstructExp final : public AssignExp void accept(Visitor *v) override { v->visit(this); } }; +class LoweredAssignExp final : public AssignExp +{ +public: + Expression *lowering; + + const char *toChars() const override; + void accept(Visitor *v) override { v->visit(this); } +}; + class BlitExp final : public AssignExp { public: @@ -1244,6 +1264,8 @@ class MinExp final : public BinExp class CatExp final : public BinExp { public: + Expression *lowering; // call to druntime hook `_d_arraycatnTX` + void accept(Visitor *v) override { v->visit(this); } }; @@ -1429,7 +1451,7 @@ struct UnionExp UnionExp(Expression *e) { - memcpy(this, (void *)e, e->size); + memcpy(this, (void *)e, e->size()); } /* Extract pointer to Expression diff --git a/dmd/expressionsem.d b/dmd/expressionsem.d index f662f49c93f..9fc6c96007f 100644 --- a/dmd/expressionsem.d +++ b/dmd/expressionsem.d @@ -65,7 +65,6 @@ import dmd.parse; import dmd.printast; import dmd.root.array; import dmd.root.ctfloat; -import dmd.root.file; import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rootobject; @@ -635,7 +634,7 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) } else if (auto dti = ce.e1.isDotTemplateInstanceExp()) { - if (Expression ey = dti.dotTemplateSemanticProp(sc, 1)) + if (Expression ey = dti.dotTemplateSemanticProp(sc, DotExpFlag.gag)) { ce.e1 = ey; return null; @@ -1190,6 +1189,13 @@ L1: */ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc) { + // https://issues.dlang.org/show_bug.cgi?id=24013 + // traits(getOverloads) inserts an alias to select the overload. + // When searching for the right this we need to use the aliased + // overload/function, not the alias. + outerFunc = outerFunc.toAliasFunc(); + calledFunc = calledFunc.toAliasFunc(); + auto thisAd = outerFunc.isMemberLocal(); if (!thisAd) return false; @@ -1221,7 +1227,7 @@ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc) /*************************************** * Pull out any properties. */ -private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null) +private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null, BinExp saveAtts = null) { //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", EXPtoString(e1.op).ptr, e1.toChars(), e2 ? e2.toChars() : null); Loc loc = e1.loc; @@ -1295,7 +1301,14 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = { Expression e = new CallExp(loc, e1); if (e2) + { e = new AssignExp(loc, e, e2); + if (saveAtts) + { + (cast(BinExp)e).att1 = saveAtts.att1; + (cast(BinExp)e).att2 = saveAtts.att2; + } + } return e.expressionSemantic(sc); } } @@ -1413,7 +1426,14 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = } Expression e = new CallExp(loc, e1); if (e2) + { e = new AssignExp(loc, e, e2); + if (saveAtts) + { + (cast(BinExp)e).att1 = saveAtts.att1; + (cast(BinExp)e).att2 = saveAtts.att2; + } + } return e.expressionSemantic(sc); } } @@ -2177,7 +2197,11 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } // Allow 'lazy' to imply 'scope' - lazy parameters can be passed along // as lazy parameters to the next function, but that isn't escaping. - else if (!(pStc & STC.lazy_)) + // The arguments of `_d_arraycatnTX` are already handled in + // expressionsem.d, via `checkNewEscape`. Without `-dip1000`, the + // check does not return an error, so the lowering of `a ~ b` to + // `_d_arraycatnTX(a, b)` still occurs. + else if (!(pStc & STC.lazy_) && (!fd || fd.ident != Id._d_arraycatnTX)) { /* Argument value can escape from the called function. * Check arg to see if it matters. @@ -2208,6 +2232,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, // allocate the array literal as temporary static array on the stack ale.type = ale.type.nextOf().sarrayOf(ale.elements.length); auto tmp = copyToTemp(0, "__arrayliteral_on_stack", ale); + tmp.storage_class |= STC.exptemp; auto declareTmp = new DeclarationExp(ale.loc, tmp); auto castToSlice = new CastExp(ale.loc, new VarExp(ale.loc, tmp), p.type.substWildTo(MODFlags.mutable)); @@ -2270,7 +2295,8 @@ private bool functionParameters(const ref Loc loc, Scope* sc, default: break; } - if (tf.parameterList.varargs == VarArg.variadic) + if (tf.parameterList.varargs == VarArg.variadic || + tf.parameterList.varargs == VarArg.KRvariadic) { const(char)* p = tf.linkage == LINK.c ? "extern(C)" : "extern(C++)"; if (arg.type.ty == Tarray) @@ -2353,30 +2379,18 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } /* Remaining problems: - * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is - * implemented by calling a function) we'll defer this for now. - * 2. value structs (or static arrays of them) that need to be copy constructed - * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the + * 1. value structs (or static arrays of them) that need to be copy constructed + * 2. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the * function gets called. - * 4. value structs need to be destructed after the function call for platforms where the caller destroys the arguments. - * 2, 3 and 4 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned + * 3. value structs need to be destructed after the function call for platforms where the caller destroys the arguments. + * Those are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned * up properly. Pushing arguments on the stack then cannot fail. */ { - /* TODO: tackle problem 1) - */ - const bool leftToRight = true; // TODO: Any cases that need rightToLeft? - if (!leftToRight) - assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity - - /* Does Problem (4) apply? + /* Does Problem (3) apply? */ const bool callerDestroysArgs = !target.isCalleeDestroyingArgs(tf); - const ptrdiff_t start = (leftToRight ? 0 : cast(ptrdiff_t)nargs - 1); - const ptrdiff_t end = (leftToRight ? cast(ptrdiff_t)nargs : -1); - const ptrdiff_t step = (leftToRight ? 1 : -1); - /* Compute indices of last throwing argument and first arg needing destruction. * Used to not set up destructors unless an arg needs destruction on a throw * in a later argument. @@ -2384,7 +2398,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, ptrdiff_t lastthrow = -1; // last argument that may throw ptrdiff_t firstdtor = -1; // first argument that needs destruction ptrdiff_t lastdtor = -1; // last argument that needs destruction - for (ptrdiff_t i = start; i != end; i += step) + for (ptrdiff_t i = 0; i != nargs; i++) { Expression arg = (*arguments)[i]; if (canThrow(arg, sc.func, false)) @@ -2401,12 +2415,12 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } } - /* Do we need 'eprefix' for problems 3 or 4? + /* Do we need 'eprefix' for problems 2 or 3? */ const bool needsPrefix = callerDestroysArgs ? firstdtor >= 0 // true if any argument needs destruction : firstdtor >= 0 && lastthrow >= 0 && - (lastthrow - firstdtor) * step > 0; // last throw after first destruction + (lastthrow - firstdtor) > 0; // last throw after first destruction const ptrdiff_t lastPrefix = callerDestroysArgs ? lastdtor // up to last argument requiring destruction : lastthrow; // up to last potentially throwing argument @@ -2426,7 +2440,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, eprefix = ae.expressionSemantic(sc); } - for (ptrdiff_t i = start; i != end; i += step) + for (ptrdiff_t i = 0; i != nargs; i++) { Expression arg = (*arguments)[i]; //printf("arg[%d]: %s\n", cast(int)i, arg.toChars()); @@ -2442,12 +2456,12 @@ private bool functionParameters(const ref Loc loc, Scope* sc, /* Do we have 'eprefix' and aren't past 'lastPrefix' yet? * Then declare a temporary variable for this arg and append that declaration - * to 'eprefix', which will implicitly take care of potential problem 2) for + * to 'eprefix', which will implicitly take care of potential problem 1) for * this arg. * 'eprefix' will therefore finally contain all args up to and including 'lastPrefix', * excluding all lazy parameters. */ - if (needsPrefix && (lastPrefix - i) * step >= 0) + if (needsPrefix && (lastPrefix - i) >= 0) { const bool needsDtor = !isRef && arg.type.needsDestruction() && // Problem 3: last throwing arg doesn't require dtor patching @@ -2470,7 +2484,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } else { - /* Problem 3: Modify the destructor so it only runs if gate==false, + /* Problem 2: Modify the destructor so it only runs if gate==false, * i.e., only if there was a throw while constructing the args */ if (!needsDtor) @@ -2505,7 +2519,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, arg = arg.expressionSemantic(sc); } - /* Problem 3: Last throwing arg? + /* Problem 2: Last throwing arg? * Then finalize eprefix => (eprefix, gate = true), i.e., disable the * dtors right after constructing the last throwing arg. * From now on, the callee will take care of destructing the args because @@ -2519,7 +2533,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } else // not part of 'eprefix' { - /* Handle problem 2) by calling the copy constructor for value structs + /* Handle problem 1) by calling the copy constructor for value structs * (or static arrays of them) if appropriate. */ Type tv = arg.type.baseElemOf(); @@ -2676,6 +2690,34 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!e.type) e.type = Type.tfloat64; + else if (e.type.isimaginary && sc.flags & SCOPE.Cfile) + { + /* Convert to core.stdc.config.complex + */ + Type t = getComplexLibraryType(e.loc, sc, e.type.ty); + if (t.ty == Terror) + return setError(); + + Type tf; + switch (e.type.ty) + { + case Timaginary32: tf = Type.tfloat32; break; + case Timaginary64: tf = Type.tfloat64; break; + case Timaginary80: tf = Type.tfloat80; break; + default: + assert(0); + } + + /* Construct ts{re : 0.0, im : e} + */ + TypeStruct ts = t.isTypeStruct; + Expressions* elements = new Expressions(2); + (*elements)[0] = new RealExp(e.loc, CTFloat.zero, tf); + (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf); + Expression sle = new StructLiteralExp(e.loc, ts.sym, elements); + result = sle.expressionSemantic(sc); + return; + } else e.type = e.type.typeSemantic(e.loc, sc); result = e; @@ -2938,7 +2980,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!s) { e.error("`%s` is not in a class or struct scope", e.toChars()); - goto Lerr; + return setError(); } ClassDeclaration cd = s.isClassDeclaration(); if (cd) @@ -2957,7 +2999,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } if (!fd) - goto Lerr; + { + e.error("`this` is only defined in non-static member functions, not `%s`", sc.parent.toChars()); + return setError(); + } assert(fd.vthis); e.var = fd.vthis; @@ -2972,11 +3017,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); result = e; - return; - - Lerr: - e.error("`this` is only defined in non-static member functions, not `%s`", sc.parent.toChars()); - result = ErrorExp.get(); } override void visit(SuperExp e) @@ -3006,7 +3046,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!s) { e.error("`%s` is not in a class scope", e.toChars()); - goto Lerr; + return setError(); } cd = s.isClassDeclaration(); if (cd) @@ -3015,7 +3055,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!cd) { e.error("class `%s` has no `super`", s.toChars()); - goto Lerr; + return setError(); } e.type = cd.type; result = e; @@ -3114,7 +3154,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type = Type.tuns32.sarrayOf(e.len + 1); else e.type = Type.tdchar.immutableOf().arrayOf(); - e.committed = 1; + e.committed = true; break; case 'w': @@ -3139,11 +3179,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type = Type.tuns16.sarrayOf(e.len + 1); else e.type = Type.twchar.immutableOf().arrayOf(); - e.committed = 1; + e.committed = true; break; case 'c': - e.committed = 1; + e.committed = true; goto default; default: @@ -3865,9 +3905,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = id.expressionSemantic(sc); return; } - // LDC: not using the `_d_newclassT` lowering yet - else if (!IN_LLVM && !exp.onstack && !exp.type.isscope()) + else if (!IN_LLVM && // LDC: not using the `_d_newclassT` lowering yet + sc.needsCodegen() && // interpreter doesn't need this lowered + !exp.onstack && !exp.type.isscope()) // these won't use the GC { + /* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)` + * or `_d_newclassTTrace` + */ auto hook = global.params.tracegc ? Id._d_newclassTTrace : Id._d_newclassT; if (!verifyHookExist(exp.loc, *sc, hook, "new class")) return setError(); @@ -4539,6 +4583,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } + Type att = null; Lagain: //printf("Lagain: %s\n", toChars()); exp.f = null; @@ -4749,7 +4794,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // overload of opCall, therefore it's a call if (exp.e1.op != EXP.type) { - if (sd.aliasthis && !isRecursiveAliasThis(exp.att1, exp.e1.type)) + if (sd.aliasthis && !isRecursiveAliasThis(att, exp.e1.type)) { exp.e1 = resolveAliasThis(sc, exp.e1); goto Lagain; @@ -4838,10 +4883,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return null; if (f) { - /* Error if match in more than one overload set, + /* Match in more than one overload set, * even if one is a 'better' match than the other. */ - ScopeDsymbol.multiplyDefined(loc, f, f2); + if (f.isCsymbol() && f2.isCsymbol()) + { + /* C has global name space, so just pick one, such as f. + * If f and f2 are not compatible, that's how C rolls. + */ + } + else + ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error } else f = f2; @@ -5209,13 +5261,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else if (sc.func && sc.intypeof != 1 && !(sc.flags & (SCOPE.ctfe | SCOPE.debug_))) { bool err = false; - if (!tf.purity && sc.func.setImpure()) + if (!tf.purity && sc.func.setImpure(exp.loc, "`pure` %s `%s` cannot call impure `%s`", exp.e1)) { exp.error("`pure` %s `%s` cannot call impure %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); err = true; } - if (!tf.isnogc && sc.func.setGC()) + if (!tf.isnogc && sc.func.setGC(exp.loc, "`@nogc` %s `%s` cannot call non-@nogc `%s`", exp.e1)) { exp.error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); @@ -6172,7 +6224,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor uint errors = global.errors; const len = buf.length; const str = buf.extractChars()[0 .. len]; - scope p = new Parser!ASTCodegen(exp.loc, sc._module, str, false, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + 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.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); @@ -6301,19 +6356,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - auto readResult = File.read(resolvedNamez); - if (!readResult.success) - { - e.error("cannot read file `%s`", resolvedNamez.ptr); - return setError(); - } - else - { - // take ownership of buffer (probably leaking) - auto data = readResult.extractSlice(); - se = new StringExp(e.loc, data); - global.fileManager.add(fileName, data); - } + e.error("cannot read file `%s`", resolvedNamez.ptr); + return setError(); } } result = se.expressionSemantic(sc); @@ -6327,7 +6371,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("AssertExp::semantic('%s')\n", exp.toChars()); } - const generateMsg = !exp.msg && global.params.checkAction == CHECKACTION.context && global.params.useAssert == CHECKENABLE.on; + const generateMsg = !exp.msg && + sc.needsCodegen() && // let ctfe interpreter handle the error message + global.params.checkAction == CHECKACTION.context && + global.params.useAssert == CHECKENABLE.on; Expression temporariesPrefix; if (generateMsg) @@ -6640,7 +6687,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { import dmd.statementsem; - if (StatementSemanticVisitor.throwSemantic(te.loc, te.e1, sc)) + if (throwSemantic(te.loc, te.e1, sc)) result = te; else setError(); @@ -6933,7 +6980,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } // Indicate we need to resolve by UFCS. - Expression e = exp.dotTemplateSemanticProp(sc, 1); + Expression e = exp.dotTemplateSemanticProp(sc, DotExpFlag.gag); if (!e) e = resolveUFCSProperties(sc, exp); if (e is exp) @@ -7840,7 +7887,7 @@ version (IN_LLVM) } } - if(t1b.ty == Tarray && exp.e1.op != EXP.arrayLiteral && (sc.flags & SCOPE.ctfe) == 0) + if(t1b.ty == Tarray && exp.e1.op != EXP.arrayLiteral && sc.needsCodegen()) { auto tFrom = t1b.nextOf(); auto tTo = tob.nextOf(); @@ -8930,6 +8977,7 @@ version (IN_LLVM) assert((*ae.arguments)[0].op == EXP.interval); ie = cast(IntervalExp)(*ae.arguments)[0]; } + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -9004,7 +9052,7 @@ version (IN_LLVM) // No operator overloading member function found yet, but // there might be an alias this to try. - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { /* Rewrite (a[arguments] op e2) as: * a.aliasthis[arguments] op e2 @@ -9032,7 +9080,7 @@ version (IN_LLVM) */ if (auto dti = e1x.isDotTemplateInstanceExp()) { - Expression e = dti.dotTemplateSemanticProp(sc, 1); + Expression e = dti.dotTemplateSemanticProp(sc, DotExpFlag.gag); if (!e) { return setResult(resolveUFCSProperties(sc, e1x, exp.e2)); @@ -9086,7 +9134,7 @@ version (IN_LLVM) * or: * f() = value */ - if (Expression e = resolvePropertiesX(sc, e1x, exp.e2)) + if (Expression e = resolvePropertiesX(sc, e1x, exp.e2, exp)) return setResult(e); if (e1x.checkRightThis(sc)) @@ -9780,6 +9828,12 @@ version (IN_LLVM) return setResult(res); } + if (!sc.needsCodegen()) // if compile time creature only + { + exp.type = Type.tsize_t; + return setResult(exp); + } + // Lower to object._d_arraysetlengthTImpl!(typeof(e1))._d_arraysetlengthT{,Trace}(e1, e2) Expression id = new IdentifierExp(ale.loc, Id.empty); id = new DotIdExp(ale.loc, id, Id.object); @@ -9801,10 +9855,11 @@ version (IN_LLVM) arguments.push(ale.e1); arguments.push(exp.e2); - Expression ce = new CallExp(ale.loc, id, arguments); - auto res = ce.expressionSemantic(sc); + Expression ce = new CallExp(ale.loc, id, arguments).expressionSemantic(sc); + auto res = new LoweredAssignExp(exp, ce); // if (global.params.verbose) // message("lowered %s =>\n %s", exp.toChars(), res.toChars()); + res.type = Type.tsize_t; return setResult(res); } else if (auto se = exp.e1.isSliceExp()) @@ -10133,6 +10188,10 @@ version (IN_LLVM) } } } + + if (!sc.needsCodegen()) // interpreter can handle these + return setResult(res); + const lowerToArrayCtor = ( (rhsType.ty == Tarray && !rhs.isArrayLiteralExp) || (rhsType.ty == Tsarray && rhs.isLvalue) ) && @@ -10272,6 +10331,9 @@ version (IN_LLVM) if (global.params.verbose) message("lowered %s =>\n %s", ae.toChars(), res.toChars()); + res = new LoweredAssignExp(ae, res); + res.type = ae.type; + return res; } @@ -10521,7 +10583,7 @@ version (IN_LLVM) result = res; if ((exp.op == EXP.concatenateAssign || exp.op == EXP.concatenateElemAssign) && - !(sc.flags & (SCOPE.ctfe | SCOPE.compile))) + sc.needsCodegen()) { // if aa ordering is triggered, `res` will be a CommaExp // and `.e2` will be the rewritten original expression. @@ -10914,6 +10976,86 @@ version (IN_LLVM) return; } + /** + * If the given expression is a `CatExp`, the function tries to lower it to + * `_d_arraycatnTX`. + * + * Params: + * ee = the `CatExp` to lower + * Returns: + * `_d_arraycatnTX(e1, e2, ..., en)` if `ee` is `e1 ~ e2 ~ ... en` + * `ee` otherwise + */ + private Expression lowerToArrayCat(CatExp exp) + { + // String literals are concatenated by the compiler. No lowering is needed. + if ((exp.e1.isStringExp() && (exp.e2.isIntegerExp() || exp.e2.isStringExp())) || + (exp.e2.isStringExp() && (exp.e1.isIntegerExp() || exp.e1.isStringExp()))) + return exp; + + Identifier hook = global.params.tracegc ? Id._d_arraycatnTXTrace : Id._d_arraycatnTX; + if (!verifyHookExist(exp.loc, *sc, hook, "concatenating arrays")) + { + setError(); + return result; + } + + void handleCatArgument(Expressions *arguments, Expression e) + { + if (auto ce = e.isCatExp()) + { + Expression lowering = ce.lowering; + + /* Skip `file`, `line`, and `funcname` if the hook of the parent + * `CatExp` is `_d_arraycatnTXTrace`. + */ + if (auto callExp = isRuntimeHook(lowering, hook)) + { + if (hook == Id._d_arraycatnTX) + arguments.pushSlice((*callExp.arguments)[]); + else + arguments.pushSlice((*callExp.arguments)[3 .. $]); + } + } + else + arguments.push(e); + } + + auto arguments = new Expressions(); + if (global.params.tracegc) + { + auto funcname = (sc.callsc && sc.callsc.func) ? + sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); + arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); + arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); + arguments.push(new StringExp(exp.loc, funcname.toDString())); + } + + handleCatArgument(arguments, exp.e1); + handleCatArgument(arguments, exp.e2); + + Expression id = new IdentifierExp(exp.loc, Id.empty); + id = new DotIdExp(exp.loc, id, Id.object); + + auto tiargs = new Objects(); + tiargs.push(exp.type); + id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs); + id = new CallExp(exp.loc, id, arguments); + return id.expressionSemantic(sc); + } + + void trySetCatExpLowering(Expression exp) + { + /* `_d_arraycatnTX` canot be used with `-betterC`, but `CatExp`s may be + * used with `-betterC`, but only during CTFE. + */ + if (global.params.betterC || !sc.needsCodegen()) + return; + + if (auto ce = exp.isCatExp()) + ce.lowering = lowerToArrayCat(ce); + } + override void visit(CatExp exp) { // https://dlang.org/spec/expression.html#cat_expressions @@ -11001,14 +11143,10 @@ version (IN_LLVM) exp.e2 = exp.e2.implicitCastTo(sc, tb1next); exp.type = tb1next.arrayOf(); L2elem: - if (tb2.ty == Tarray || tb2.ty == Tsarray) - { - // Make e2 into [e2] - exp.e2 = new ArrayLiteralExp(exp.e2.loc, exp.type, exp.e2); - } - else if (checkNewEscape(sc, exp.e2, false)) + if (checkNewEscape(sc, exp.e2, false)) return setError(); result = exp.optimize(WANTvalue); + trySetCatExpLowering(result); return; } } @@ -11039,14 +11177,10 @@ version (IN_LLVM) exp.e1 = exp.e1.implicitCastTo(sc, tb2next); exp.type = tb2next.arrayOf(); L1elem: - if (tb1.ty == Tarray || tb1.ty == Tsarray) - { - // Make e1 into [e1] - exp.e1 = new ArrayLiteralExp(exp.e1.loc, exp.type, exp.e1); - } - else if (checkNewEscape(sc, exp.e1, false)) + if (checkNewEscape(sc, exp.e1, false)) return setError(); result = exp.optimize(WANTvalue); + trySetCatExpLowering(result); return; } } @@ -11069,6 +11203,7 @@ version (IN_LLVM) if (Expression ex = typeCombine(exp, sc)) { result = ex; + trySetCatExpLowering(result); return; } exp.type = exp.type.toHeadMutable(); @@ -11101,6 +11236,7 @@ version (IN_LLVM) } result = e; + trySetCatExpLowering(result); } override void visit(MulExp exp) @@ -11911,7 +12047,10 @@ version (IN_LLVM) exp.error("array comparison type mismatch, `%s` vs `%s`", t1next.toChars(), t2next.toChars()); return setError(); } - if ((t1.ty == Tarray || t1.ty == Tsarray) && (t2.ty == Tarray || t2.ty == Tsarray)) + + if (sc.needsCodegen() && + (t1.ty == Tarray || t1.ty == Tsarray) && + (t2.ty == Tarray || t2.ty == Tsarray)) { if (!verifyHookExist(exp.loc, *sc, Id.__cmp, "comparing arrays")) return setError(); @@ -12778,7 +12917,7 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) if (exp.e1.isVarExp() && exp.e1.type.toBasetype().isTypeSArray() && exp.ident == Id.length) { // bypass checkPurity - return exp.e1.type.dotExp(sc, exp.e1, exp.ident, exp.noderef ? DotExpFlag.noDeref : 0); + return exp.e1.type.dotExp(sc, exp.e1, exp.ident, cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref)); } if (!exp.e1.isDotExp()) @@ -12830,11 +12969,11 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) * Params: * exp = expression to resolve * sc = context - * flag = if 1 then do not emit error messages, just return null + * gag = do not emit error messages, just return `null` * Returns: * resolved expression, null if error */ -Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) +Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) { //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars()); @@ -13062,10 +13201,20 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) Expression se = new ScopeExp(exp.loc, imp.pkg); return se.expressionSemantic(sc); } + + if (auto attr = s.isAttribDeclaration()) + { + if (auto sm = ie.sds.search(exp.loc, exp.ident, flags)) + { + auto es = new DsymbolExp(exp.loc, sm); + return es; + } + } + // BUG: handle other cases like in IdentifierExp::semantic() debug { - printf("s = '%s', kind = '%s'\n", s.toChars(), s.kind()); + printf("s = %p '%s', kind = '%s'\n", s, s.toChars(), s.kind()); } assert(0); } @@ -13077,9 +13226,9 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) } if (ie.sds.isPackage() || ie.sds.isImport() || ie.sds.isModule()) { - flag = 0; + gag = false; } - if (flag) + if (gag) return null; s = ie.sds.search_correct(exp.ident); if (s && symbolIsVisible(sc, s)) @@ -13105,7 +13254,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) )) { Type t1bn = t1b.nextOf(); - if (flag) + if (gag) { if (AggregateDeclaration ad = isAggregate(t1bn)) { @@ -13119,11 +13268,12 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) * as: * (*p).ident */ - if (flag && t1bn.ty == Tvoid) + if (gag && t1bn.ty == Tvoid) return null; Expression e = new PtrExp(exp.loc, exp.e1); e = e.expressionSemantic(sc); - return e.type.dotExp(sc, e, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); + const newFlag = cast(DotExpFlag) (gag * DotExpFlag.gag | exp.noderef * DotExpFlag.noDeref); + return e.type.dotExp(sc, e, exp.ident, newFlag); } else if (exp.ident == Id.__xalignof && exp.e1.isVarExp() && @@ -13156,8 +13306,11 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) else { if (exp.e1.isTypeExp() || exp.e1.isTemplateExp()) - flag = 0; - Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); + gag = false; + + const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag); + + Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag); if (e) { e = e.expressionSemantic(sc); @@ -13166,9 +13319,16 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, int flag) } } -// Resolve e1.ident!tiargs without seeing UFCS. -// If flag == 1, stop "not a property" error and return NULL. -Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, int flag) +/** + * Resolve `e1.ident!tiargs` without seeing UFCS. + * Params: + * exp = the `DotTemplateInstanceExp` to resolve + * sc = the semantic scope + * gag = stop "not a property" error and return `null`. + * Returns: + * `null` if error or not found, or the resolved expression. + */ +Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool gag) { static if (LOGSEMANTIC) { @@ -13202,11 +13362,11 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, int fl /* No built-in type has templatized properties, so do shortcut. * It is necessary in: 1024.max!"a < b" */ - if (flag) + if (gag) return null; } - e = die.dotIdSemanticProp(sc, flag); - if (flag) + e = die.dotIdSemanticProp(sc, gag); + if (gag) { if (!e || isDotOpDispatch(e)) @@ -13927,6 +14087,7 @@ Expression toBoolean(Expression exp, Scope* sc) case EXP.assign: case EXP.construct: case EXP.blit: + case EXP.loweredAssignExp: if (sc.flags & SCOPE.Cfile) return exp; // Things like: diff --git a/dmd/foreachvar.d b/dmd/foreachvar.d index ba2825a3098..7a964695f45 100644 --- a/dmd/foreachvar.d +++ b/dmd/foreachvar.d @@ -299,7 +299,7 @@ void foreachExpAndVar(Statement s, case STMT.Conditional: case STMT.While: case STMT.Forwarding: - case STMT.Compile: + case STMT.Mixin: case STMT.Peel: case STMT.Synchronized: assert(0); // should have been rewritten diff --git a/dmd/frontend.h b/dmd/frontend.h index 480b91922c2..e8c2acc134a 100644 --- a/dmd/frontend.h +++ b/dmd/frontend.h @@ -100,7 +100,7 @@ class AttribDeclaration; class AnonDeclaration; class VisibilityDeclaration; class OverloadSet; -class CompileDeclaration; +class MixinDeclaration; class StaticAssert; class StaticIfDeclaration; class DsymbolTable; @@ -181,6 +181,7 @@ class IndexExp; class PostExp; class PreExp; class AssignExp; +class LoweredAssignExp; class ConstructExp; class BlitExp; class AddAssignExp; @@ -315,11 +316,18 @@ class StaticForeachStatement; class GotoDefaultStatement; class BreakStatement; class DtorExpStatement; -class CompileStatement; +class MixinStatement; class ForwardingStatement; class ContinueStatement; class ThrowStatement; class SwitchErrorStatement; +class CompoundAsmStatement; +class PragmaStatement; +class StaticAssertStatement; +class AsmStatement; +class InlineAsmStatement; +class GccAsmStatement; +class ImportStatement; struct Token; struct code; class Object; @@ -570,7 +578,7 @@ class Dsymbol : public ASTNode virtual CPPNamespaceDeclaration* isCPPNamespaceDeclaration(); virtual VisibilityDeclaration* isVisibilityDeclaration(); virtual OverloadSet* isOverloadSet(); - virtual CompileDeclaration* isCompileDeclaration(); + virtual MixinDeclaration* isMixinDeclaration(); virtual StaticAssert* isStaticAssert(); virtual StaticIfDeclaration* isStaticIfDeclaration(); }; @@ -694,31 +702,6 @@ enum class LINK : uint8_t system = 6u, }; -struct structalign_t final -{ -private: - uint16_t value; - bool pack; -public: - bool isDefault() const; - void setDefault(); - bool isUnknown() const; - void setUnknown(); - void set(uint32_t value); - uint32_t get() const; - bool isPack() const; - void setPack(bool pack); - structalign_t() : - value(0u), - pack() - { - } - structalign_t(uint16_t value, bool pack = false) : - value(value), - pack(pack) - {} -}; - enum class BUILTIN : uint8_t { unknown = 255u, @@ -821,136 +804,128 @@ enum class EXP : uint8_t cast_ = 2u, null_ = 3u, assert_ = 4u, - true_ = 5u, - false_ = 6u, - array = 7u, - call = 8u, - address = 9u, - type = 10u, - throw_ = 11u, - new_ = 12u, - delete_ = 13u, - star = 14u, - symbolOffset = 15u, - variable = 16u, - dotVariable = 17u, - dotIdentifier = 18u, - dotTemplateInstance = 19u, - dotType = 20u, - slice = 21u, - arrayLength = 22u, - version_ = 23u, - dollar = 24u, - template_ = 25u, - dotTemplateDeclaration = 26u, - declaration = 27u, - typeof_ = 28u, - pragma_ = 29u, - dSymbol = 30u, - typeid_ = 31u, - uadd = 32u, - remove = 33u, - newAnonymousClass = 34u, - arrayLiteral = 35u, - assocArrayLiteral = 36u, - structLiteral = 37u, - classReference = 38u, - thrownException = 39u, - delegatePointer = 40u, - delegateFunctionPointer = 41u, - lessThan = 42u, - greaterThan = 43u, - lessOrEqual = 44u, - greaterOrEqual = 45u, - equal = 46u, - notEqual = 47u, - identity = 48u, - notIdentity = 49u, - index = 50u, - is_ = 51u, - leftShift = 52u, - rightShift = 53u, - leftShiftAssign = 54u, - rightShiftAssign = 55u, - unsignedRightShift = 56u, - unsignedRightShiftAssign = 57u, - concatenate = 58u, - concatenateAssign = 59u, - concatenateElemAssign = 60u, - concatenateDcharAssign = 61u, - add = 62u, - min = 63u, - addAssign = 64u, - minAssign = 65u, - mul = 66u, - div = 67u, - mod = 68u, - mulAssign = 69u, - divAssign = 70u, - modAssign = 71u, - and_ = 72u, - or_ = 73u, - xor_ = 74u, - andAssign = 75u, - orAssign = 76u, - xorAssign = 77u, - assign = 78u, - not_ = 79u, - tilde = 80u, - plusPlus = 81u, - minusMinus = 82u, - construct = 83u, - blit = 84u, - dot = 85u, - comma = 86u, - question = 87u, - andAnd = 88u, - orOr = 89u, - prePlusPlus = 90u, - preMinusMinus = 91u, - identifier = 92u, - string_ = 93u, - this_ = 94u, - super_ = 95u, - halt = 96u, - tuple = 97u, - error = 98u, - void_ = 99u, - int64 = 100u, - float64 = 101u, - complex80 = 102u, - char_ = 103u, - import_ = 104u, - delegate_ = 105u, - function_ = 106u, - mixin_ = 107u, - in_ = 108u, - default_ = 109u, - break_ = 110u, - continue_ = 111u, - goto_ = 112u, - scope_ = 113u, - traits = 114u, - overloadSet = 115u, - line = 116u, - file = 117u, - fileFullPath = 118u, - moduleString = 119u, - functionString = 120u, - prettyFunction = 121u, - shared_ = 122u, - pow = 123u, - powAssign = 124u, - vector = 125u, - voidExpression = 126u, - cantExpression = 127u, - showCtfeContext = 128u, - objcClassReference = 129u, - vectorArray = 130u, - arrow = 131u, - compoundLiteral = 132u, - _Generic = 133u, - interval = 134u, + array = 5u, + call = 6u, + address = 7u, + type = 8u, + throw_ = 9u, + new_ = 10u, + delete_ = 11u, + star = 12u, + symbolOffset = 13u, + variable = 14u, + dotVariable = 15u, + dotIdentifier = 16u, + dotTemplateInstance = 17u, + dotType = 18u, + slice = 19u, + arrayLength = 20u, + dollar = 21u, + template_ = 22u, + dotTemplateDeclaration = 23u, + declaration = 24u, + dSymbol = 25u, + typeid_ = 26u, + uadd = 27u, + remove = 28u, + newAnonymousClass = 29u, + arrayLiteral = 30u, + assocArrayLiteral = 31u, + structLiteral = 32u, + classReference = 33u, + thrownException = 34u, + delegatePointer = 35u, + delegateFunctionPointer = 36u, + lessThan = 37u, + greaterThan = 38u, + lessOrEqual = 39u, + greaterOrEqual = 40u, + equal = 41u, + notEqual = 42u, + identity = 43u, + notIdentity = 44u, + index = 45u, + is_ = 46u, + leftShift = 47u, + rightShift = 48u, + leftShiftAssign = 49u, + rightShiftAssign = 50u, + unsignedRightShift = 51u, + unsignedRightShiftAssign = 52u, + concatenate = 53u, + concatenateAssign = 54u, + concatenateElemAssign = 55u, + concatenateDcharAssign = 56u, + add = 57u, + min = 58u, + addAssign = 59u, + minAssign = 60u, + mul = 61u, + div = 62u, + mod = 63u, + mulAssign = 64u, + divAssign = 65u, + modAssign = 66u, + and_ = 67u, + or_ = 68u, + xor_ = 69u, + andAssign = 70u, + orAssign = 71u, + xorAssign = 72u, + assign = 73u, + not_ = 74u, + tilde = 75u, + plusPlus = 76u, + minusMinus = 77u, + construct = 78u, + blit = 79u, + dot = 80u, + comma = 81u, + question = 82u, + andAnd = 83u, + orOr = 84u, + prePlusPlus = 85u, + preMinusMinus = 86u, + identifier = 87u, + string_ = 88u, + this_ = 89u, + super_ = 90u, + halt = 91u, + tuple = 92u, + error = 93u, + void_ = 94u, + int64 = 95u, + float64 = 96u, + complex80 = 97u, + import_ = 98u, + delegate_ = 99u, + function_ = 100u, + mixin_ = 101u, + in_ = 102u, + break_ = 103u, + continue_ = 104u, + goto_ = 105u, + scope_ = 106u, + traits = 107u, + overloadSet = 108u, + line = 109u, + file = 110u, + fileFullPath = 111u, + moduleString = 112u, + functionString = 113u, + prettyFunction = 114u, + pow = 115u, + powAssign = 116u, + vector = 117u, + voidExpression = 118u, + cantExpression = 119u, + showCtfeContext = 120u, + objcClassReference = 121u, + vectorArray = 122u, + compoundLiteral = 123u, + _Generic = 124u, + interval = 125u, + loweredAssignExp = 126u, }; typedef uint64_t dinteger_t; @@ -992,11 +967,11 @@ struct Optional final class Expression : public ASTNode { public: - const EXP op; - uint8_t size; - bool parens; Type* type; Loc loc; + const EXP op; + bool parens; + size_t size() const; static void _init(); static void deinitialize(); Expression* copy(); @@ -1096,6 +1071,7 @@ class Expression : public ASTNode PostExp* isPostExp(); PreExp* isPreExp(); AssignExp* isAssignExp(); + LoweredAssignExp* isLoweredAssignExp(); ConstructExp* isConstructExp(); BlitExp* isBlitExp(); AddAssignExp* isAddAssignExp(); @@ -1366,7 +1342,6 @@ enum class TY : uint8_t Tmixin = 45u, Tnoreturn = 46u, Ttag = 47u, - TMAX = 48u, }; enum class Covariant @@ -1835,9 +1810,10 @@ enum class TOK : uint8_t __cdecl_ = 217u, __declspec_ = 218u, __stdcall_ = 219u, - __pragma_ = 220u, - __int128_ = 221u, - __attribute___ = 222u, + __thread_ = 220u, + __pragma_ = 221u, + __int128_ = 222u, + __attribute___ = 223u, }; enum class MemorySet @@ -1889,7 +1865,7 @@ class ParseTimeVisitor virtual void visit(typename AST::StaticDtorDeclaration s); virtual void visit(typename AST::SharedStaticCtorDeclaration s); virtual void visit(typename AST::SharedStaticDtorDeclaration s); - virtual void visit(typename AST::CompileDeclaration s); + virtual void visit(typename AST::MixinDeclaration s); virtual void visit(typename AST::UserAttributeDeclaration s); virtual void visit(typename AST::LinkDeclaration s); virtual void visit(typename AST::AnonDeclaration s); @@ -1916,7 +1892,7 @@ class ParseTimeVisitor virtual void visit(typename AST::ReturnStatement s); virtual void visit(typename AST::LabelStatement s); virtual void visit(typename AST::StaticAssertStatement s); - virtual void visit(typename AST::CompileStatement s); + virtual void visit(typename AST::MixinStatement s); virtual void visit(typename AST::WhileStatement s); virtual void visit(typename AST::ForStatement s); virtual void visit(typename AST::DoStatement s); @@ -2429,6 +2405,7 @@ enum class VarArg : uint8_t none = 0u, variadic = 1u, typesafe = 2u, + KRvariadic = 3u, }; struct ParameterList final @@ -2499,6 +2476,9 @@ class FuncDeclaration : public Declaration Array siblingCallers; Array* inlinedNestedCallees; AttributeViolation* safetyViolation; + AttributeViolation* nogcViolation; + AttributeViolation* pureViolation; + AttributeViolation* nothrowViolation; bool purityInprocess() const; bool purityInprocess(bool v); bool safetyInprocess() const; @@ -2549,6 +2529,10 @@ class FuncDeclaration : public Declaration bool hasEscapingSiblings(bool v); bool computedEscapingSiblings() const; bool computedEscapingSiblings(bool v); + bool dllImport() const; + bool dllImport(bool v); + bool dllExport() const; + bool dllExport(bool v); private: uint32_t bitFields; public: @@ -2706,14 +2690,14 @@ class SemanticTimePermissiveVisitor : public Visitor { public: using Visitor::visit; - void visit(Dsymbol* _param_0) override; - void visit(Parameter* _param_0) override; - void visit(Statement* _param_0) override; - void visit(Type* _param_0) override; - void visit(Expression* _param_0) override; - void visit(TemplateParameter* _param_0) override; - void visit(Condition* _param_0) override; - void visit(Initializer* _param_0) override; + void visit(Dsymbol* __param_0_) override; + void visit(Parameter* __param_0_) override; + void visit(Statement* __param_0_) override; + void visit(Type* __param_0_) override; + void visit(Expression* __param_0_) override; + void visit(TemplateParameter* __param_0_) override; + void visit(Condition* __param_0_) override; + void visit(Initializer* __param_0_) override; }; class StatementRewriteWalker : public SemanticTimePermissiveVisitor @@ -2964,6 +2948,7 @@ extern Expression* initializerToExpression(Initializer* init, Type* itype = null enum class DotExpFlag { + none = 0, gag = 1, noDeref = 2, noAliasThis = 4, @@ -3103,7 +3088,7 @@ struct Param final FeatureState useDIP1000; bool ehnogc; bool useDIP1021; - bool fieldwise; + FeatureState fieldwise; bool fixAliasThis; FeatureState rvalueRefParam; FeatureState noSharedAccess; @@ -3147,6 +3132,7 @@ struct Param final bool run; Array runargs; Array cppswitches; + const char* cpp; Array objfiles; Array linkswitches; Array linkswitchIsForCC; @@ -3206,7 +3192,6 @@ struct Param final useDIP25((FeatureState)1), ehnogc(), useDIP1021(), - fieldwise(), fixAliasThis(), previewIn(), inclusiveInContracts(), @@ -3245,6 +3230,7 @@ struct Param final run(), runargs(), cppswitches(), + cpp(), objfiles(), linkswitches(), linkswitchIsForCC(), @@ -3256,7 +3242,7 @@ struct Param final mapfile() { } - Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool verbose = false, bool vcg_ast = false, bool showColumns = false, bool vtls = false, bool vtemplates = false, bool vtemplatesListInstances = false, bool vgc = false, bool vfield = false, bool vcomplex = true, bool vin = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool color = false, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = false, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, bool showGaggedErrors = false, bool printErrorContext = false, bool manual = false, bool usage = false, bool mcpuUsage = false, bool transitionUsage = false, bool checkUsage = false, bool checkActionUsage = false, bool revertUsage = false, bool previewUsage = false, bool externStdUsage = false, bool hcUsage = false, bool logo = false, FeatureState useDIP25 = (FeatureState)1, FeatureState useDIP1000 = (FeatureState)-1, bool ehnogc = false, bool useDIP1021 = false, bool fieldwise = false, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)-1, FeatureState noSharedAccess = (FeatureState)-1, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)-1, FeatureState systemVariables = (FeatureState)-1, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, uint32_t errorLimit = 20u, uint32_t errorSupplementLimit = 6u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array* imppath = nullptr, Array* fileImppath = nullptr, _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, Array* debugids = nullptr, uint32_t versionlevel = 0u, Array* versionids = nullptr, MessageStyle messageStyle = (MessageStyle)0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) : + Param(bool obj, bool multiobj = false, bool trace = false, bool tracegc = false, bool verbose = false, bool vcg_ast = false, bool showColumns = false, bool vtls = false, bool vtemplates = false, bool vtemplatesListInstances = false, bool vgc = false, bool vfield = false, bool vcomplex = true, bool vin = false, DiagnosticReporting useDeprecated = (DiagnosticReporting)1u, bool useUnitTests = false, bool useInline = false, bool release = false, bool preservePaths = false, DiagnosticReporting warnings = (DiagnosticReporting)2u, bool color = false, bool cov = false, uint8_t covPercent = 0u, bool ctfe_cov = false, bool ignoreUnsupportedPragmas = false, bool useModuleInfo = true, bool useTypeInfo = true, bool useExceptions = true, bool betterC = false, bool addMain = false, bool allInst = false, bool bitfields = false, CppStdRevision cplusplus = (CppStdRevision)201103u, bool showGaggedErrors = false, bool printErrorContext = false, bool manual = false, bool usage = false, bool mcpuUsage = false, bool transitionUsage = false, bool checkUsage = false, bool checkActionUsage = false, bool revertUsage = false, bool previewUsage = false, bool externStdUsage = false, bool hcUsage = false, bool logo = false, FeatureState useDIP25 = (FeatureState)1, FeatureState useDIP1000 = (FeatureState)-1, bool ehnogc = false, bool useDIP1021 = false, FeatureState fieldwise = (FeatureState)-1, bool fixAliasThis = false, FeatureState rvalueRefParam = (FeatureState)-1, FeatureState noSharedAccess = (FeatureState)-1, bool previewIn = false, bool inclusiveInContracts = false, bool shortenedMethods = true, bool fixImmutableConv = false, bool fix16997 = true, FeatureState dtorFields = (FeatureState)-1, FeatureState systemVariables = (FeatureState)-1, CHECKENABLE useInvariants = (CHECKENABLE)0u, CHECKENABLE useIn = (CHECKENABLE)0u, CHECKENABLE useOut = (CHECKENABLE)0u, CHECKENABLE useArrayBounds = (CHECKENABLE)0u, CHECKENABLE useAssert = (CHECKENABLE)0u, CHECKENABLE useSwitchError = (CHECKENABLE)0u, CHECKENABLE boundscheck = (CHECKENABLE)0u, CHECKACTION checkAction = (CHECKACTION)0u, uint32_t errorLimit = 20u, uint32_t errorSupplementLimit = 6u, _d_dynamicArray< const char > argv0 = {}, Array modFileAliasStrings = Array(), Array* imppath = nullptr, Array* fileImppath = nullptr, _d_dynamicArray< const char > objdir = {}, _d_dynamicArray< const char > objname = {}, _d_dynamicArray< const char > libname = {}, Output ddoc = Output(), Output dihdr = Output(), Output cxxhdr = Output(), Output json = Output(), JsonFieldFlags jsonFieldFlags = (JsonFieldFlags)0u, Output makeDeps = Output(), Output mixinOut = Output(), Output moduleDeps = Output(), uint32_t debuglevel = 0u, Array* debugids = nullptr, uint32_t versionlevel = 0u, Array* versionids = nullptr, MessageStyle messageStyle = (MessageStyle)0u, bool run = false, Array runargs = Array(), Array cppswitches = Array(), const char* cpp = nullptr, Array objfiles = Array(), Array linkswitches = Array(), Array linkswitchIsForCC = Array(), Array libfiles = Array(), Array dllfiles = Array(), _d_dynamicArray< const char > deffile = {}, _d_dynamicArray< const char > resfile = {}, _d_dynamicArray< const char > exefile = {}, _d_dynamicArray< const char > mapfile = {}) : obj(obj), multiobj(multiobj), trace(trace), @@ -3351,6 +3337,7 @@ struct Param final run(run), runargs(runargs), cppswitches(cppswitches), + cpp(cpp), objfiles(objfiles), linkswitches(linkswitches), linkswitchIsForCC(linkswitchIsForCC), @@ -3363,6 +3350,39 @@ struct Param final {} }; +struct CompileEnv final +{ + uint32_t versionNumber; + _d_dynamicArray< const char > date; + _d_dynamicArray< const char > time; + _d_dynamicArray< const char > vendor; + _d_dynamicArray< const char > timestamp; + bool previewIn; + bool ddocOutput; + bool shortenedMethods; + CompileEnv() : + versionNumber(), + date(), + time(), + vendor(), + timestamp(), + previewIn(), + ddocOutput(), + shortenedMethods(true) + { + } + CompileEnv(uint32_t versionNumber, _d_dynamicArray< const char > date = {}, _d_dynamicArray< const char > time = {}, _d_dynamicArray< const char > vendor = {}, _d_dynamicArray< const char > timestamp = {}, bool previewIn = false, bool ddocOutput = false, bool shortenedMethods = true) : + versionNumber(versionNumber), + date(date), + time(time), + vendor(vendor), + timestamp(timestamp), + previewIn(previewIn), + ddocOutput(ddocOutput), + shortenedMethods(shortenedMethods) + {} +}; + struct Global final { _d_dynamicArray< const char > inifilename; @@ -3370,7 +3390,7 @@ struct Global final _d_dynamicArray< const char > written; Array* path; Array* filePath; - _d_dynamicArray< const char > vendor; + CompileEnv compileEnv; Param params; uint32_t errors; uint32_t warnings; @@ -3399,7 +3419,7 @@ struct Global final written(24, "written by Walter Bright"), path(), filePath(), - vendor(), + compileEnv(), params(), errors(), warnings(), @@ -3416,13 +3436,13 @@ struct Global final preprocess() { } - Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array* path = nullptr, Array* filePath = nullptr, _d_dynamicArray< const char > vendor = {}, Param params = Param(), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array* versionids = nullptr, Array* debugids = nullptr, bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, FileName(*preprocess)(FileName , const Loc& , bool& , OutBuffer* ) = nullptr) : + Global(_d_dynamicArray< const char > inifilename, _d_dynamicArray< const char > copyright = { 73, "Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved" }, _d_dynamicArray< const char > written = { 24, "written by Walter Bright" }, Array* path = nullptr, Array* filePath = nullptr, CompileEnv compileEnv = CompileEnv(), Param params = Param(), uint32_t errors = 0u, uint32_t warnings = 0u, uint32_t gag = 0u, uint32_t gaggedErrors = 0u, uint32_t gaggedWarnings = 0u, void* console = nullptr, Array* versionids = nullptr, Array* debugids = nullptr, bool hasMainFunction = false, uint32_t varSequenceNumber = 1u, FileManager* fileManager = nullptr, ErrorSink* errorSink = nullptr, FileName(*preprocess)(FileName , const Loc& , bool& , OutBuffer* ) = nullptr) : inifilename(inifilename), copyright(copyright), written(written), path(path), filePath(filePath), - vendor(vendor), + compileEnv(compileEnv), params(params), errors(errors), warnings(warnings), @@ -3774,7 +3794,7 @@ class TypeFunction final : public TypeNext bool isDstyleVariadic() const; StorageClass parameterStorageClass(Type* tthis, Parameter* p); Type* addStorageClass(StorageClass stc) override; - Type* substWildTo(uint32_t _param_0) override; + Type* substWildTo(uint32_t __param_0_) override; MATCH constConv(Type* to) override; bool iswild() const; void accept(Visitor* v) override; @@ -3958,6 +3978,7 @@ class TypeTag final : public Type public: Loc loc; TOK tok; + structalign_t packalign; Identifier* id; Type* base; Array* members; @@ -4053,7 +4074,7 @@ enum class STMT : uint8_t Peel = 1u, Exp = 2u, DtorExp = 3u, - Compile = 4u, + Mixin = 4u, Compound = 5u, CompoundDeclaration = 6u, CompoundAsm = 7u, @@ -4117,6 +4138,7 @@ class Statement : public ASTNode void accept(Visitor* v) override; virtual ReturnStatement* endsWithReturnStatement(); ErrorStatement* isErrorStatement(); + PeelStatement* isPeelStatement(); ScopeStatement* isScopeStatement(); ExpStatement* isExpStatement(); CompoundStatement* isCompoundStatement(); @@ -4132,7 +4154,7 @@ class Statement : public ASTNode GotoCaseStatement* isGotoCaseStatement(); BreakStatement* isBreakStatement(); DtorExpStatement* isDtorExpStatement(); - CompileStatement* isCompileStatement(); + MixinStatement* isMixinStatement(); ForwardingStatement* isForwardingStatement(); DoStatement* isDoStatement(); WhileStatement* isWhileStatement(); @@ -4150,6 +4172,15 @@ class Statement : public ASTNode UnrolledLoopStatement* isUnrolledLoopStatement(); ForeachRangeStatement* isForeachRangeStatement(); CompoundDeclarationStatement* isCompoundDeclarationStatement(); + CompoundAsmStatement* isCompoundAsmStatement(); + PragmaStatement* isPragmaStatement(); + StaticAssertStatement* isStaticAssertStatement(); + CaseRangeStatement* isCaseRangeStatement(); + SynchronizedStatement* isSynchronizedStatement(); + AsmStatement* isAsmStatement(); + InlineAsmStatement* isInlineAsmStatement(); + GccAsmStatement* isGccAsmStatement(); + ImportStatement* isImportStatement(); }; class AsmStatement : public Statement @@ -4203,14 +4234,6 @@ class Catch final : public RootObject Catch* syntaxCopy(); }; -class CompileStatement final : public Statement -{ -public: - Array* exps; - CompileStatement* syntaxCopy() override; - void accept(Visitor* v) override; -}; - class CompoundStatement : public Statement { public: @@ -4477,6 +4500,14 @@ class LabelStatement final : public Statement void accept(Visitor* v) override; }; +class MixinStatement final : public Statement +{ +public: + Array* exps; + MixinStatement* syntaxCopy() override; + void accept(Visitor* v) override; +}; + class PeelStatement final : public Statement { public: @@ -4780,11 +4811,11 @@ struct ASTCodegen final using AttribDeclaration = ::AttribDeclaration; using CPPMangleDeclaration = ::CPPMangleDeclaration; using CPPNamespaceDeclaration = ::CPPNamespaceDeclaration; - using CompileDeclaration = ::CompileDeclaration; using ConditionalDeclaration = ::ConditionalDeclaration; using DeprecatedDeclaration = ::DeprecatedDeclaration; using ForwardingAttribDeclaration = ::ForwardingAttribDeclaration; using LinkDeclaration = ::LinkDeclaration; + using MixinDeclaration = ::MixinDeclaration; using PragmaDeclaration = ::PragmaDeclaration; using StaticForeachDeclaration = ::StaticForeachDeclaration; using StaticIfDeclaration = ::StaticIfDeclaration; @@ -4928,6 +4959,7 @@ struct ASTCodegen final using IsExp = ::IsExp; using LineInitExp = ::LineInitExp; using LogicalExp = ::LogicalExp; + using LoweredAssignExp = ::LoweredAssignExp; using MemorySet = ::MemorySet; using MinAssignExp = ::MinAssignExp; using MinExp = ::MinExp; @@ -5018,6 +5050,7 @@ struct ASTCodegen final using Initializer = ::Initializer; using NeedInterpret = ::NeedInterpret; using StructInitializer = ::StructInitializer; + using VisitInitializer = ::VisitInitializer; using VoidInitializer = ::VoidInitializer; using Covariant = ::Covariant; using DotExpFlag = ::DotExpFlag; @@ -5054,13 +5087,13 @@ struct ASTCodegen final using TypeTuple = ::TypeTuple; using TypeTypeof = ::TypeTypeof; using TypeVector = ::TypeVector; + using VisitType = ::VisitType; using Nspace = ::Nspace; using AsmStatement = ::AsmStatement; using BreakStatement = ::BreakStatement; using CaseRangeStatement = ::CaseRangeStatement; using CaseStatement = ::CaseStatement; using Catch = ::Catch; - using CompileStatement = ::CompileStatement; using CompoundAsmStatement = ::CompoundAsmStatement; using CompoundDeclarationStatement = ::CompoundDeclarationStatement; using CompoundStatement = ::CompoundStatement; @@ -5085,6 +5118,7 @@ struct ASTCodegen final using InlineAsmStatement = ::InlineAsmStatement; using LabelDsymbol = ::LabelDsymbol; using LabelStatement = ::LabelStatement; + using MixinStatement = ::MixinStatement; using PeelStatement = ::PeelStatement; using PragmaStatement = ::PragmaStatement; using ReturnStatement = ::ReturnStatement; @@ -5100,6 +5134,7 @@ struct ASTCodegen final using TryCatchStatement = ::TryCatchStatement; using TryFinallyStatement = ::TryFinallyStatement; using UnrolledLoopStatement = ::UnrolledLoopStatement; + using VisitStatement = ::VisitStatement; using WhileStatement = ::WhileStatement; using WithStatement = ::WithStatement; using StaticAssert = ::StaticAssert; @@ -5188,6 +5223,7 @@ class Visitor : public ParseTimeVisitor virtual void visit(ClassReferenceExp* e); virtual void visit(VoidInitExp* e); virtual void visit(ThrownExceptionExp* e); + virtual void visit(LoweredAssignExp* e); }; class StoppableVisitor : public Visitor @@ -5403,6 +5439,31 @@ extern TypeTuple* toArgTypes_aarch64(Type* t); extern bool isHFVA(Type* t, int32_t maxNumElements = 4, Type** rewriteType = nullptr); +struct structalign_t final +{ +private: + uint16_t value; + bool pack; +public: + bool isDefault() const; + void setDefault(); + bool isUnknown() const; + void setUnknown(); + void set(uint32_t value); + uint32_t get() const; + bool isPack() const; + void setPack(bool pack); + structalign_t() : + value(0u), + pack() + { + } + structalign_t(uint16_t value, bool pack = false) : + value(value), + pack(pack) + {} +}; + class AttribDeclaration : public Dsymbol { public: @@ -5489,7 +5550,7 @@ class VisibilityDeclaration final : public AttribDeclaration Scope* newScope(Scope* sc) override; void addMember(Scope* sc, ScopeDsymbol* sds) override; const char* kind() const override; - const char* toPrettyChars(bool _param_0) override; + const char* toPrettyChars(bool __param_0_) override; VisibilityDeclaration* isVisibilityDeclaration() override; void accept(Visitor* v) override; }; @@ -5591,17 +5652,17 @@ class ForwardingAttribDeclaration final : public AttribDeclaration void accept(Visitor* v) override; }; -class CompileDeclaration final : public AttribDeclaration +class MixinDeclaration final : public AttribDeclaration { public: Array* exps; ScopeDsymbol* scopesym; bool compiled; - CompileDeclaration* syntaxCopy(Dsymbol* s) override; + MixinDeclaration* syntaxCopy(Dsymbol* s) override; void addMember(Scope* sc, ScopeDsymbol* sds) override; void setScope(Scope* sc) override; const char* kind() const override; - CompileDeclaration* isCompileDeclaration() override; + MixinDeclaration* isMixinDeclaration() override; void accept(Visitor* v) override; }; @@ -5981,6 +6042,10 @@ class VarDeclaration : public Declaration bool isArgDtorVar(bool v); bool isCmacro() const; bool isCmacro(bool v); + bool dllImport() const; + bool dllImport(bool v); + bool dllExport() const; + bool dllExport(bool v); bool inClosure() const; bool inClosure(bool v); bool inAlignSection() const; @@ -6702,7 +6767,7 @@ class TemplateDeclaration final : public ScopeDsymbol Array lastConstraintNegs; Array* lastConstraintTiargs; public: - TemplateDeclaration* syntaxCopy(Dsymbol* _param_0) override; + TemplateDeclaration* syntaxCopy(Dsymbol* __param_0_) override; bool overloadInsert(Dsymbol* s) override; bool hasStaticCtorOrDtor() override; const char* kind() const override; @@ -6927,23 +6992,23 @@ struct UnionExp final private: union __AnonStruct__u { - char exp[40LLU]; + char exp[34LLU]; char integerexp[48LLU]; - char errorexp[40LLU]; + char errorexp[34LLU]; char realexp[64LLU]; char complexexp[80LLU]; char symoffexp[72LLU]; - char stringexp[60LLU]; - char arrayliteralexp[58LLU]; - char assocarrayliteralexp[57LLU]; - char structliteralexp[95LLU]; + char stringexp[58LLU]; + char arrayliteralexp[56LLU]; + char assocarrayliteralexp[56LLU]; + char structliteralexp[84LLU]; char compoundliteralexp[48LLU]; - char nullexp[40LLU]; - char dotvarexp[65LLU]; - char addrexp[56LLU]; + char nullexp[34LLU]; + char dotvarexp[57LLU]; + char addrexp[48LLU]; char indexexp[82LLU]; - char sliceexp[83LLU]; - char vectorexp[69LLU]; + char sliceexp[73LLU]; + char vectorexp[61LLU]; }; #pragma pack(pop) @@ -7082,20 +7147,20 @@ class NullExp final : public Expression class StringExp final : public Expression { +public: + char postfix; + OwnedBy ownedByCtfe; union { char* string; char16_t* wstring; char32_t* dstring; }; -public: size_t len; uint8_t sz; - uint8_t committed; + bool committed; enum : char { NoPostfix = 0u }; - char postfix; - OwnedBy ownedByCtfe; static StringExp* create(const Loc& loc, const char* s); static StringExp* create(const Loc& loc, const void* string, size_t len); static void emplace(UnionExp* pue, const Loc& loc, const char* s); @@ -7128,10 +7193,10 @@ class TupleExp final : public Expression class ArrayLiteralExp final : public Expression { public: - Expression* basis; - Array* elements; OwnedBy ownedByCtfe; bool onstack; + Expression* basis; + Array* elements; static ArrayLiteralExp* create(const Loc& loc, Array* elements); static void emplace(UnionExp* pue, const Loc& loc, Array* elements); ArrayLiteralExp* syntaxCopy() override; @@ -7146,9 +7211,9 @@ class ArrayLiteralExp final : public Expression class AssocArrayLiteralExp final : public Expression { public: + OwnedBy ownedByCtfe; Array* keys; Array* values; - OwnedBy ownedByCtfe; bool equals(const RootObject* const o) const override; AssocArrayLiteralExp* syntaxCopy() override; Optional toBool() override; @@ -7161,10 +7226,13 @@ class StructLiteralExp final : public Expression StructDeclaration* sd; Array* elements; Type* stype; - Symbol* sym; + union + { + Symbol* sym; + StructLiteralExp* inlinecopy; + }; StructLiteralExp* origin; - StructLiteralExp* inlinecopy; - int32_t stageflags; + uint8_t stageflags; bool useStaticInit; bool isOriginal; OwnedBy ownedByCtfe; @@ -7344,7 +7412,6 @@ class UnaExp : public Expression { public: Expression* e1; - Type* att1; UnaExp* syntaxCopy() override; Expression* incompatibleTypes(); void setNoderefOperand(); @@ -7568,9 +7635,15 @@ class SliceExp final : public UnaExp Expression* upr; Expression* lwr; VarDeclaration* lengthVar; - bool upperIsInBounds; - bool lowerIsLessThanUpper; - bool arrayop; + bool upperIsInBounds() const; + bool upperIsInBounds(bool v); + bool lowerIsLessThanUpper() const; + bool lowerIsLessThanUpper(bool v); + bool arrayop() const; + bool arrayop(bool v); +private: + uint8_t bitFields; +public: SliceExp* syntaxCopy() override; bool isLvalue() override; Expression* toLvalue(Scope* sc, Expression* e) override; @@ -7679,6 +7752,14 @@ class AssignExp : public BinExp void accept(Visitor* v) override; }; +class LoweredAssignExp final : public AssignExp +{ +public: + Expression* lowering; + const char* toChars() const override; + void accept(Visitor* v) override; +}; + class ConstructExp final : public AssignExp { public: @@ -7796,6 +7877,7 @@ class MinExp final : public BinExp class CatExp final : public BinExp { public: + Expression* lowering; Expression* resolveLoc(const Loc& loc, Scope* sc) override; void accept(Visitor* v) override; }; @@ -8209,7 +8291,7 @@ class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor public: using SemanticTimePermissiveVisitor::visit; void visit(ExpStatement* s) override; - void visit(CompileStatement* s) override; + void visit(MixinStatement* s) override; void visit(CompoundStatement* s) override; virtual void visitVarDecl(VarDeclaration* v); void visit(CompoundDeclarationStatement* s) override; @@ -8270,7 +8352,7 @@ class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor void visit(AnonDeclaration* d) override; void visit(PragmaDeclaration* d) override; void visit(ConditionalDeclaration* d) override; - void visit(CompileDeclaration* d) override; + void visit(MixinDeclaration* d) override; void visit(UserAttributeDeclaration* d) override; virtual void visitFuncBody(FuncDeclaration* f); virtual void visitBaseClasses(ClassDeclaration* d); @@ -8359,6 +8441,7 @@ class SemanticTimeTransitiveVisitor : public SemanticTimePermissiveVisitor void visit(DotExp* e) override; void visit(IndexExp* e) override; void visit(RemoveExp* e) override; + void visit(LoweredAssignExp* e) override; }; extern _d_real creall(complex_t x); @@ -8654,11 +8737,17 @@ struct Id final static Identifier* _d_arrayappendcTXImpl; static Identifier* _d_arrayappendcTX; static Identifier* _d_arrayappendcTXTrace; + static Identifier* _d_arraycatnTX; + static Identifier* _d_arraycatnTXTrace; static Identifier* stdc; static Identifier* stdarg; static Identifier* va_start; static Identifier* std; static Identifier* core; + static Identifier* config; + static Identifier* c_complex_float; + static Identifier* c_complex_double; + static Identifier* c_complex_real; static Identifier* etc; static Identifier* attribute; static Identifier* atomic; @@ -8789,8 +8878,16 @@ struct Id final static Identifier* ImportC; static Identifier* dllimport; static Identifier* dllexport; + static Identifier* naked; + static Identifier* thread; static Identifier* vector_size; + static Identifier* always_inline; + static Identifier* noinline; static Identifier* noreturn; + static Identifier* _nothrow; + static Identifier* _deprecated; + static Identifier* _align; + static Identifier* aligned; static Identifier* builtins; static Identifier* builtin_va_list; static Identifier* builtin_va_arg; @@ -8800,6 +8897,7 @@ struct Id final static Identifier* show; static Identifier* push; static Identifier* pop; + static Identifier* _pure; static Identifier* define; static Identifier* undef; static void initialize(); diff --git a/dmd/func.d b/dmd/func.d index 693005087ff..c3c360b0894 100644 --- a/dmd/func.d +++ b/dmd/func.d @@ -230,6 +230,7 @@ private struct FUNCFLAG bool hasCatches; /// function has try-catch statements bool skipCodegen; /// do not generate code for this function. bool printf; /// is a printf-like function + bool scanf; /// is a scanf-like function bool noreturn; /// the function does not return bool isNRVO = true; /// Support for named return value optimization @@ -240,11 +241,14 @@ private struct FUNCFLAG bool hasNoEH; /// No exception unwinding is needed bool inferRetType; /// Return type is to be inferred bool hasDualContext; /// has a dual-context 'this' parameter + bool hasAlwaysInlines; /// Contains references to functions that must be inlined bool isCrtCtor; /// Has attribute pragma(crt_constructor) bool isCrtDtor; /// Has attribute pragma(crt_destructor) bool hasEscapingSiblings;/// Has sibling functions that escape bool computedEscapingSiblings; /// `hasEscapingSiblings` has been computed + bool dllImport; /// __declspec(dllimport) + bool dllExport; /// __declspec(dllexport) } /*********************************************************** @@ -401,6 +405,9 @@ version (IN_LLVM) {} else /// In case of failed `@safe` inference, store the error that made the function `@system` for /// better diagnostics AttributeViolation* safetyViolation; + AttributeViolation* nogcViolation; + AttributeViolation* pureViolation; + AttributeViolation* nothrowViolation; /// See the `FUNCFLAG` struct import dmd.common.bitfields; @@ -415,8 +422,8 @@ version (IN_LLVM) {} else extern (D) this(const ref Loc loc, const ref Loc endloc, Identifier ident, StorageClass storage_class, Type type, bool noreturn = false) { super(loc, ident); - //printf("FuncDeclaration(id = '%s', type = %p)\n", id.toChars(), type); - //printf("storage_class = x%x\n", storage_class); + //.printf("FuncDeclaration(id = '%s', type = %s)\n", ident.toChars(), type.toChars()); + //.printf("storage_class = x%llx\n", storage_class); this.storage_class = storage_class; this.type = type; if (type) @@ -1343,14 +1350,14 @@ version (IN_LLVM) override final bool isExport() const { - return visibility.kind == Visibility.Kind.export_; + return visibility.kind == Visibility.Kind.export_ || dllExport; } override final bool isImportedSymbol() const { //printf("isImportedSymbol()\n"); //printf("protection = %d\n", visibility); - return (visibility.kind == Visibility.Kind.export_) && !fbody; + return (visibility.kind == Visibility.Kind.export_ || dllImport) && !fbody; } override final bool isCodeseg() const pure nothrow @nogc @safe @@ -1490,17 +1497,27 @@ version (IN_LLVM) } /************************************** - * The function is doing something impure, - * so mark it as impure. - * If there's a purity error, return true. + * The function is doing something impure, so mark it as impure. + * + * Params: + * loc = location of impure action + * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. + * arg0 = (optional) argument to format string + * + * Returns: `true` if there's a purity error */ - extern (D) final bool setImpure() + extern (D) final bool setImpure(Loc loc = Loc.init, const(char)* fmt = null, RootObject arg0 = null) { if (purityInprocess) { purityInprocess = false; + if (fmt) + pureViolation = new AttributeViolation(loc, fmt, this, arg0); // impure action + else if (arg0) + pureViolation = new AttributeViolation(loc, fmt, arg0); // call to impure function + if (fes) - fes.func.setImpure(); + fes.func.setImpure(loc, fmt, arg0); } else if (isPure()) return true; @@ -1589,7 +1606,7 @@ version (IN_LLVM) { //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess)); if (nogcInprocess) - setGC(); + setGC(loc, null); return type.toTypeFunction().isnogc; } @@ -1601,10 +1618,16 @@ version (IN_LLVM) /************************************** * The function is doing something that may allocate with the GC, * so mark it as not nogc (not no-how). + * + * Params: + * loc = location of impure action + * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. + * arg0 = (optional) argument to format string + * * Returns: * true if function is marked as @nogc, meaning a user error occurred */ - extern (D) final bool setGC() + extern (D) final bool setGC(Loc loc, const(char)* fmt, RootObject arg0 = null) { //printf("setGC() %s\n", toChars()); if (nogcInprocess && semanticRun < PASS.semantic3 && _scope) @@ -1616,15 +1639,59 @@ version (IN_LLVM) if (nogcInprocess) { nogcInprocess = false; + if (fmt) + nogcViolation = new AttributeViolation(loc, fmt, this, arg0); // action that requires GC + else if (arg0) + nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function + type.toTypeFunction().isnogc = false; if (fes) - fes.func.setGC(); + fes.func.setGC(Loc.init, null, null); } else if (isNogc()) return true; return false; } + /************************************** + * The function calls non-`@nogc` function f, mark it as not nogc. + * Params: + * f = function being called + * Returns: + * true if function is marked as @nogc, meaning a user error occurred + */ + extern (D) final bool setGCCall(FuncDeclaration f) + { + return setGC(loc, null, f); + } + + /************************************** + * The function is doing something that may throw an exception, register that in case nothrow is being inferred + * + * Params: + * loc = location of action + * fmt = format string for error message + * arg0 = (optional) argument to format string + */ + extern (D) final void setThrow(Loc loc, const(char)* fmt, RootObject arg0 = null) + { + if (nothrowInprocess && !nothrowViolation) + { + nothrowViolation = new AttributeViolation(loc, fmt, arg0); // action that requires GC + } + } + + /************************************** + * The function calls non-`nothrow` function f, register that in case nothrow is being inferred + * Params: + * loc = location of call + * f = function being called + */ + extern (D) final void setThrowCall(Loc loc, FuncDeclaration f) + { + return setThrow(loc, null, f); + } + extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn) { if (!global.params.vgc) @@ -1950,7 +2017,8 @@ version (IN_LLVM) overloadApply(cast() this, (Dsymbol s) { auto f = s.isFuncDeclaration(); - if (!f) + auto td = s.isTemplateDeclaration(); + if (!f && !td) return 0; if (result) { @@ -2168,7 +2236,7 @@ version (IN_LLVM) if (!needsClosure()) return false; - if (setGC()) + if (setGC(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", this)) { error("is `@nogc` yet allocates closure for `%s()` with the GC", toChars()); if (global.gag) // need not report supplemental errors @@ -4580,18 +4648,51 @@ struct AttributeViolation /// fd = function to check /// maxDepth = up to how many functions deep to report errors /// deprecation = print deprecations instead of errors -void errorSupplementalInferredSafety(FuncDeclaration fd, int maxDepth, bool deprecation) +/// stc = storage class of attribute to check +void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc) { auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental; - if (auto s = fd.safetyViolation) + + AttributeViolation* s; + const(char)* attr; + if (stc & STC.safe) + { + s = fd.safetyViolation; + attr = "@safe"; + } + else if (stc & STC.pure_) + { + s = fd.pureViolation; + attr = "pure"; + } + else if (stc & STC.nothrow_) + { + s = fd.nothrowViolation; + attr = "nothrow"; + } + else if (stc & STC.nogc) + { + s = fd.nogcViolation; + attr = "@nogc"; + } + + if (s) { if (s.fmtStr) { errorFunc(s.loc, deprecation ? - "which would be `@system` because of:" : - "which was inferred `@system` because of:"); - errorFunc(s.loc, s.fmtStr, - s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); + "which wouldn't be `%s` because of:" : + "which wasn't inferred `%s` because of:", attr); + if (stc == STC.nogc || stc == STC.pure_) + { + auto f = (cast(Dsymbol) s.arg0).isFuncDeclaration(); + errorFunc(s.loc, s.fmtStr, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : ""); + } + else + { + errorFunc(s.loc, s.fmtStr, + s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); + } } else if (s.arg0.dyncast() == DYNCAST.dsymbol) { @@ -4600,7 +4701,7 @@ void errorSupplementalInferredSafety(FuncDeclaration fd, int maxDepth, bool depr if (maxDepth > 0) { errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); - errorSupplementalInferredSafety(fd2, maxDepth - 1, deprecation); + errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc); } } } diff --git a/dmd/globals.d b/dmd/globals.d index 7c7a7d89d28..141ce324f92 100644 --- a/dmd/globals.d +++ b/dmd/globals.d @@ -11,7 +11,10 @@ module dmd.globals; +import core.stdc.stdio; import core.stdc.stdint; +import core.stdc.string; + import dmd.root.array; import dmd.root.filename; import dmd.common.outbuffer; @@ -20,6 +23,8 @@ import dmd.errors; import dmd.file_manager; import dmd.identifier; import dmd.location; +import dmd.lexer : CompileEnv; +import dmd.utils; version (IN_LLVM) { @@ -183,7 +188,7 @@ extern (C++) struct Param FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params bool ehnogc; // use @nogc exception handling bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md - bool fieldwise; // do struct equality testing field-wise rather than by memcmp() + FeatureState fieldwise; // do struct equality testing field-wise rather than by memcmp() bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters // https://dconf.org/2019/talks/alexandrescu.html @@ -243,6 +248,7 @@ extern (C++) struct Param bool run; // run resulting executable Strings runargs; // arguments for executable Array!(const(char)*) cppswitches; // C preprocessor switches + const(char)* cpp; // if not null, then this specifies the C preprocessor // Linker stuff Array!(const(char)*) objfiles; @@ -301,30 +307,6 @@ version (IN_LLVM) } // IN_LLVM } -extern (C++) struct structalign_t -{ - private: - ushort value = 0; // unknown - enum STRUCTALIGN_DEFAULT = 1234; // default = match whatever the corresponding C compiler does - bool pack; // use #pragma pack semantics - - public: - pure @safe @nogc nothrow: - bool isDefault() const { return value == STRUCTALIGN_DEFAULT; } - void setDefault() { value = STRUCTALIGN_DEFAULT; } - bool isUnknown() const { return value == 0; } // value is not set - void setUnknown() { value = 0; } - void set(uint value) { this.value = cast(ushort)value; } - uint get() const { return value; } - bool isPack() const { return pack; } - void setPack(bool pack) { this.pack = pack; } -} -//alias structalign_t = uint; - -// magic value means "match whatever the underlying C compiler does" -// other values are all powers of 2 -//enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0); - enum mars_ext = "d"; // for D source files enum doc_ext = "html"; // for Ddoc generated files enum ddoc_ext = "ddoc"; // for Ddoc macro include files @@ -363,10 +345,8 @@ extern (C++) struct Global version (IN_LLVM) {} else { private enum string _version = import("VERSION"); - private enum uint _versionNumber = parseVersionNumber(_version); } - - const(char)[] vendor; /// Compiler backend name + CompileEnv compileEnv; Param params; /// command line parameters uint errors; /// number of errors reported so far @@ -456,12 +436,12 @@ else extern (C++) void _init() { - global.errorSink = new ErrorSinkCompiler; + errorSink = new ErrorSinkCompiler; this.fileManager = new FileManager(); version (MARS) { - vendor = "Digital Mars D"; + compileEnv.vendor = "Digital Mars D"; // -color=auto is the default value import dmd.console : detectTerminal; @@ -469,15 +449,53 @@ else } else version (IN_GCC) { - vendor = "GNU D"; + compileEnv.vendor = "GNU D"; } else version (IN_LLVM) { - vendor = "LDC"; + compileEnv.vendor = "LDC"; import dmd.console : detectTerminal; params.color = detectTerminal(); } + +version (IN_LLVM) +{ + compileEnv.versionNumber = parseVersionNumber(versionString()); +} +else +{ + compileEnv.versionNumber = parseVersionNumber(_version); +} + + /* Initialize date, time, and timestamp + */ + import core.stdc.time; + import core.stdc.stdlib : getenv; + + time_t ct; + // https://issues.dlang.org/show_bug.cgi?id=20444 + if (auto p = getenv("SOURCE_DATE_EPOCH")) + { + if (!ct.parseDigits(p[0 .. strlen(p)])) + errorSink.error(Loc.initial, "value of environment variable `SOURCE_DATE_EPOCH` should be a valid UNIX timestamp, not: `%s`", p); + } + else + core.stdc.time.time(&ct); + const p = ctime(&ct); + assert(p); + + __gshared char[11 + 1] date = 0; // put in BSS segment + __gshared char[8 + 1] time = 0; + __gshared char[24 + 1] timestamp = 0; + + const dsz = snprintf(&date[0], date.length, "%.6s %.4s", p + 4, p + 20); + const tsz = snprintf(&time[0], time.length, "%.8s", p + 11); + const tssz = snprintf(×tamp[0], timestamp.length, "%.24s", p); + assert(dsz > 0 && tsz > 0 && tssz > 0); + compileEnv.time = time[0 .. tsz]; + compileEnv.date = date[0 .. dsz]; + compileEnv.timestamp = timestamp[0 .. tssz]; } /** @@ -528,14 +546,7 @@ else */ extern(C++) uint versionNumber() { -version (IN_LLVM) -{ - return parseVersionNumber(versionString()); -} -else -{ - return _versionNumber; -} + return compileEnv.versionNumber; } /** diff --git a/dmd/globals.h b/dmd/globals.h index 8019a59d769..03e157f2cb2 100644 --- a/dmd/globals.h +++ b/dmd/globals.h @@ -112,8 +112,8 @@ enum class DLLImport : char struct Output { /// Configuration for the compiler generator - bool doOutput; // Output is enabled - bool fullOutput; // Generate comments for hidden declarations (for -HC), + d_bool doOutput; // Output is enabled + d_bool fullOutput; // Generate comments for hidden declarations (for -HC), // and don't strip the bodies of plain (non-template) functions (for -H) DString dir; // write to directory 'dir' DString name; // write to file 'name' @@ -126,71 +126,71 @@ struct Output // Put command line switches in here struct Param { - bool obj; // write object file - bool multiobj; // break one object file into multiple ones - bool trace; // insert profiling hooks - bool tracegc; // instrument calls to 'new' - bool verbose; // verbose compile - bool vcg_ast; // write-out codegen-ast - bool showColumns; // print character (column) numbers in diagnostics - bool vtls; // identify thread local variables - bool vtemplates; // collect and list statistics on template instantiations - bool vtemplatesListInstances; // collect and list statistics on template instantiations origins - bool vgc; // identify gc usage - bool vfield; // identify non-mutable field variables - bool vcomplex; // identify complex/imaginary type usage - bool vin; // identify 'in' parameters + d_bool obj; // write object file + d_bool multiobj; // break one object file into multiple ones + d_bool trace; // insert profiling hooks + d_bool tracegc; // instrument calls to 'new' + d_bool verbose; // verbose compile + d_bool vcg_ast; // write-out codegen-ast + d_bool showColumns; // print character (column) numbers in diagnostics + d_bool vtls; // identify thread local variables + d_bool vtemplates; // collect and list statistics on template instantiations + d_bool vtemplatesListInstances; // collect and list statistics on template instantiations origins + d_bool vgc; // identify gc usage + d_bool vfield; // identify non-mutable field variables + d_bool vcomplex; // identify complex/imaginary type usage + d_bool vin; // identify 'in' parameters Diagnostic useDeprecated; - bool useUnitTests; // generate unittest code - bool useInline; // inline expand functions - bool release; // build release version - bool preservePaths; // true means don't strip path from source file + d_bool useUnitTests; // generate unittest code + d_bool useInline; // inline expand functions + d_bool release; // build release version + d_bool preservePaths; // true means don't strip path from source file Diagnostic warnings; - bool color; // use ANSI colors in console output - bool cov; // generate code coverage data + d_bool color; // use ANSI colors in console output + d_bool cov; // generate code coverage data unsigned char covPercent; // 0..100 code coverage percentage required - bool ctfe_cov; // generate coverage data for ctfe - bool ignoreUnsupportedPragmas; // rather than error on them - bool useModuleInfo; // generate runtime module information - bool useTypeInfo; // generate runtime type information - bool useExceptions; // support exception handling - bool betterC; // be a "better C" compiler; no dependency on D runtime - bool addMain; // add a default main() function - bool allInst; // generate code for all template instantiations - bool bitfields; // support C style bit fields + d_bool ctfe_cov; // generate coverage data for ctfe + d_bool ignoreUnsupportedPragmas; // rather than error on them + d_bool useModuleInfo; // generate runtime module information + d_bool useTypeInfo; // generate runtime type information + d_bool useExceptions; // support exception handling + d_bool betterC; // be a "better C" compiler; no dependency on D runtime + d_bool addMain; // add a default main() function + d_bool allInst; // generate code for all template instantiations + d_bool bitfields; // support C style bit fields CppStdRevision cplusplus; // version of C++ name mangling to support - bool showGaggedErrors; // print gagged errors anyway - bool printErrorContext; // print errors with the error context (the error line in the source file) - bool manual; // open browser on compiler manual - bool usage; // print usage and exit - bool mcpuUsage; // print help on -mcpu switch - bool transitionUsage; // print help on -transition switch - bool checkUsage; // print help on -check switch - bool checkActionUsage; // print help on -checkaction switch - bool revertUsage; // print help on -revert switch - bool previewUsage; // print help on -preview switch - bool externStdUsage; // print help on -extern-std switch - bool hcUsage; // print help on -HC switch - bool logo; // print logo; + d_bool showGaggedErrors; // print gagged errors anyway + d_bool printErrorContext; // print errors with the error context (the error line in the source file) + d_bool manual; // open browser on compiler manual + d_bool usage; // print usage and exit + d_bool mcpuUsage; // print help on -mcpu switch + d_bool transitionUsage; // print help on -transition switch + d_bool checkUsage; // print help on -check switch + d_bool checkActionUsage; // print help on -checkaction switch + d_bool revertUsage; // print help on -revert switch + d_bool previewUsage; // print help on -preview switch + d_bool externStdUsage; // print help on -extern-std switch + d_bool hcUsage; // print help on -HC switch + d_bool logo; // print logo; // Options for `-preview=/-revert=` FeatureState useDIP25; // implement https://wiki.dlang.org/DIP25 FeatureState useDIP1000; // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params - bool ehnogc; // use @nogc exception handling - bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md - bool fieldwise; // do struct equality testing field-wise rather than by memcmp() - bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes + d_bool ehnogc; // use @nogc exception handling + d_bool useDIP1021; // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md + FeatureState fieldwise; // do struct equality testing field-wise rather than by memcmp() + d_bool fixAliasThis; // if the current scope has an alias this, check it before searching upper scopes FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters // https://dconf.org/2019/talks/alexandrescu.html // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 FeatureState noSharedAccess; // read/write access to shared memory objects - bool previewIn; // `in` means `[ref] scope const`, accepts rvalues - bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract - bool shortenedMethods; // allow => in normal function declarations - bool fixImmutableConv; // error on unsound immutable conversion - https://github.com/dlang/dmd/pull/14070 - bool fix16997; // fix integral promotions for unary + - ~ operators + d_bool previewIn; // `in` means `[ref] scope const`, accepts rvalues + d_bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract + d_bool shortenedMethods; // allow => in normal function declarations + d_bool fixImmutableConv; // error on unsound immutable conversion - https://github.com/dlang/dmd/pull/14070 + d_bool fix16997; // fix integral promotions for unary + - ~ operators // https://issues.dlang.org/show_bug.cgi?id=16997 FeatureState dtorFields; // destruct fields of partially constructed objects // https://issues.dlang.org/show_bug.cgi?id=14246 @@ -235,10 +235,11 @@ struct Param MessageStyle messageStyle; // style of file/line annotations on messages - bool run; // run resulting executable + d_bool run; // run resulting executable Strings runargs; // arguments for executable Array cppswitches; // preprocessor switches + const char *cpp; // if not null, then this specifies the C preprocessor // Linker stuff Array objfiles; @@ -298,7 +299,7 @@ struct Param struct structalign_t { unsigned short value; - bool pack; + d_bool pack; bool isDefault() const; void setDefault(); @@ -330,6 +331,18 @@ const DString bc_ext = "bc"; const DString s_ext = "s"; #endif +struct CompileEnv +{ + uint32_t versionNumber; + DString date; + DString time; + DString vendor; + DString timestamp; + bool previewIn; + bool ddocOutput; + bool shortenedMethods; +}; + struct Global { DString inifilename; @@ -339,7 +352,7 @@ struct Global Array *path; // Array of char*'s which form the import lookup path Array *filePath; // Array of char*'s which form the file import lookup path - DString vendor; // Compiler backend name + CompileEnv compileEnv; Param params; unsigned errors; // number of errors reported so far @@ -353,7 +366,7 @@ struct Global Array* versionids; // command line versions and predefined versions Array* debugids; // command line debug versions and predefined versions - bool hasMainFunction; + d_bool hasMainFunction; unsigned varSequenceNumber; FileManager* fileManager; diff --git a/dmd/hdrgen.d b/dmd/hdrgen.d index d63b4ff3689..94e0eda4df1 100644 --- a/dmd/hdrgen.d +++ b/dmd/hdrgen.d @@ -139,37 +139,19 @@ void moduleToBuffer2(Module m, OutBuffer* buf, HdrGenState* hgs) private void statementToBuffer(Statement s, OutBuffer* buf, HdrGenState* hgs) { - scope v = new StatementPrettyPrintVisitor(buf, hgs); - s.accept(v); -} - -private extern (C++) final class StatementPrettyPrintVisitor : Visitor -{ - alias visit = Visitor.visit; -public: - OutBuffer* buf; - HdrGenState* hgs; - - extern (D) this(OutBuffer* buf, HdrGenState* hgs) scope + void visitDefaultCase(Statement s) { - this.buf = buf; - this.hgs = hgs; + printf("Statement::toCBuffer() %d\n", s.stmt); + assert(0, "unrecognized statement in statementToBuffer()"); } - override void visit(Statement s) - { - buf.writestring("Statement::toCBuffer()"); - buf.writenl(); - assert(0); - } - - override void visit(ErrorStatement s) + void visitError(ErrorStatement s) { buf.writestring("__error__"); buf.writenl(); } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { if (s.exp && s.exp.op == EXP.declaration && (cast(DeclarationExp)s.exp).declaration) @@ -185,7 +167,12 @@ public: buf.writenl(); } - override void visit(CompileStatement s) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitMixin(MixinStatement s) { buf.writestring("mixin("); argsToBuffer(s.exps, buf, hgs, null); @@ -194,25 +181,29 @@ public: buf.writenl(); } - override void visit(CompoundStatement s) + void visitCompound(CompoundStatement s) { foreach (sx; *s.statements) { if (sx) - sx.accept(this); + sx.statementToBuffer(buf, hgs); } } - override void visit(CompoundDeclarationStatement s) + void visitCompoundAsm(CompoundAsmStatement s) + { + visitCompound(s); + } + + void visitCompoundDeclaration(CompoundDeclarationStatement s) { bool anywritten = false; foreach (sx; *s.statements) { auto ds = sx ? sx.isExpStatement() : null; - if (ds && ds.exp.op == EXP.declaration) + if (ds && ds.exp.isDeclarationExp()) { - auto d = (cast(DeclarationExp)ds.exp).declaration; - assert(d.isDeclaration()); + auto d = ds.exp.isDeclarationExp().declaration; if (auto v = d.isVarDeclaration()) { scope ppv = new DsymbolPrettyPrintVisitor(buf, hgs); @@ -228,7 +219,7 @@ public: buf.writenl(); } - override void visit(UnrolledLoopStatement s) + void visitUnrolledLoop(UnrolledLoopStatement s) { buf.writestring("/*unrolled*/ {"); buf.writenl(); @@ -236,26 +227,26 @@ public: foreach (sx; *s.statements) { if (sx) - sx.accept(this); + sx.statementToBuffer(buf, hgs); } buf.level--; buf.writeByte('}'); buf.writenl(); } - override void visit(ScopeStatement s) + void visitScope(ScopeStatement s) { buf.writeByte('{'); buf.writenl(); buf.level++; if (s.statement) - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - override void visit(WhileStatement s) + void visitWhile(WhileStatement s) { buf.writestring("while ("); if (auto p = s.param) @@ -276,28 +267,28 @@ public: buf.writeByte(')'); buf.writenl(); if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } - override void visit(DoStatement s) + void visitDo(DoStatement s) { buf.writestring("do"); buf.writenl(); if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.writestring("while ("); s.condition.expressionToBuffer(buf, hgs); buf.writestring(");"); buf.writenl(); } - override void visit(ForStatement s) + void visitFor(ForStatement s) { buf.writestring("for ("); if (s._init) { hgs.forStmtInit++; - s._init.accept(this); + s._init.statementToBuffer(buf, hgs); hgs.forStmtInit--; } else @@ -319,13 +310,13 @@ public: buf.writenl(); buf.level++; if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - private void foreachWithoutBody(ForeachStatement s) + void foreachWithoutBody(ForeachStatement s) { buf.writestring(Token.toString(s.op)); buf.writestring(" ("); @@ -346,20 +337,20 @@ public: buf.writenl(); } - override void visit(ForeachStatement s) + void visitForeach(ForeachStatement s) { foreachWithoutBody(s); buf.writeByte('{'); buf.writenl(); buf.level++; if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - private void foreachRangeWithoutBody(ForeachRangeStatement s) + void foreachRangeWithoutBody(ForeachRangeStatement s) { buf.writestring(Token.toString(s.op)); buf.writestring(" ("); @@ -375,39 +366,39 @@ public: buf.writenl(); } - override void visit(ForeachRangeStatement s) + void visitForeachRange(ForeachRangeStatement s) { foreachRangeWithoutBody(s); buf.writeByte('{'); buf.writenl(); buf.level++; if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } - override void visit(StaticForeachStatement s) + void visitStaticForeach(StaticForeachStatement s) { buf.writestring("static "); if (s.sfe.aggrfe) { - visit(s.sfe.aggrfe); + visitForeach(s.sfe.aggrfe); } else { assert(s.sfe.rangefe); - visit(s.sfe.rangefe); + visitForeachRange(s.sfe.rangefe); } } - override void visit(ForwardingStatement s) + void visitForwarding(ForwardingStatement s) { - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(IfStatement s) + void visitIf(IfStatement s) { buf.writestring("if ("); if (Parameter p = s.prm) @@ -428,12 +419,12 @@ public: buf.writenl(); if (s.ifbody.isScopeStatement()) { - s.ifbody.accept(this); + s.ifbody.statementToBuffer(buf, hgs); } else { buf.level++; - s.ifbody.accept(this); + s.ifbody.statementToBuffer(buf, hgs); buf.level--; } if (s.elsebody) @@ -449,18 +440,18 @@ public: } if (s.elsebody.isScopeStatement() || s.elsebody.isIfStatement()) { - s.elsebody.accept(this); + s.elsebody.statementToBuffer(buf, hgs); } else { buf.level++; - s.elsebody.accept(this); + s.elsebody.statementToBuffer(buf, hgs); buf.level--; } } } - override void visit(ConditionalStatement s) + void visitConditional(ConditionalStatement s) { s.condition.conditionToBuffer(buf, hgs); buf.writenl(); @@ -468,7 +459,7 @@ public: buf.writenl(); buf.level++; if (s.ifbody) - s.ifbody.accept(this); + s.ifbody.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); @@ -479,14 +470,14 @@ public: buf.writeByte('{'); buf.level++; buf.writenl(); - s.elsebody.accept(this); + s.elsebody.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); } buf.writenl(); } - override void visit(PragmaStatement s) + void visitPragma(PragmaStatement s) { buf.writestring("pragma ("); buf.writestring(s.ident.toString()); @@ -502,7 +493,7 @@ public: buf.writeByte('{'); buf.writenl(); buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); @@ -514,12 +505,12 @@ public: } } - override void visit(StaticAssertStatement s) + void visitStaticAssert(StaticAssertStatement s) { s.sa.dsymbolToBuffer(buf, hgs); } - override void visit(SwitchStatement s) + void visitSwitch(SwitchStatement s) { buf.writestring(s.isFinal ? "final switch (" : "switch ("); s.condition.expressionToBuffer(buf, hgs); @@ -532,28 +523,28 @@ public: buf.writeByte('{'); buf.writenl(); buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); } else { - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } } } - override void visit(CaseStatement s) + void visitCase(CaseStatement s) { buf.writestring("case "); s.exp.expressionToBuffer(buf, hgs); buf.writeByte(':'); buf.writenl(); - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(CaseRangeStatement s) + void visitCaseRange(CaseRangeStatement s) { buf.writestring("case "); s.first.expressionToBuffer(buf, hgs); @@ -561,23 +552,23 @@ public: s.last.expressionToBuffer(buf, hgs); buf.writeByte(':'); buf.writenl(); - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(DefaultStatement s) + void visitDefault(DefaultStatement s) { buf.writestring("default:"); buf.writenl(); - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(GotoDefaultStatement s) + void visitGotoDefault(GotoDefaultStatement s) { buf.writestring("goto default;"); buf.writenl(); } - override void visit(GotoCaseStatement s) + void visitGotoCase(GotoCaseStatement s) { buf.writestring("goto case"); if (s.exp) @@ -589,13 +580,13 @@ public: buf.writenl(); } - override void visit(SwitchErrorStatement s) + void visitSwitchError(SwitchErrorStatement s) { buf.writestring("SwitchErrorStatement::toCBuffer()"); buf.writenl(); } - override void visit(ReturnStatement s) + void visitReturn(ReturnStatement s) { buf.writestring("return "); if (s.exp) @@ -604,7 +595,7 @@ public: buf.writenl(); } - override void visit(BreakStatement s) + void visitBreak(BreakStatement s) { buf.writestring("break"); if (s.ident) @@ -616,7 +607,7 @@ public: buf.writenl(); } - override void visit(ContinueStatement s) + void visitContinue(ContinueStatement s) { buf.writestring("continue"); if (s.ident) @@ -628,7 +619,7 @@ public: buf.writenl(); } - override void visit(SynchronizedStatement s) + void visitSynchronized(SynchronizedStatement s) { buf.writestring("synchronized"); if (s.exp) @@ -640,21 +631,21 @@ public: if (s._body) { buf.writeByte(' '); - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } } - override void visit(WithStatement s) + void visitWith(WithStatement s) { buf.writestring("with ("); s.exp.expressionToBuffer(buf, hgs); buf.writestring(")"); buf.writenl(); if (s._body) - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } - override void visit(TryCatchStatement s) + void visitTryCatch(TryCatchStatement s) { buf.writestring("try"); buf.writenl(); @@ -662,29 +653,44 @@ public: { if (s._body.isScopeStatement()) { - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); } else { buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; } } foreach (c; *s.catches) { - visit(c); + buf.writestring("catch"); + if (c.type) + { + buf.writeByte('('); + typeToBuffer(c.type, c.ident, buf, hgs); + buf.writeByte(')'); + } + buf.writenl(); + buf.writeByte('{'); + buf.writenl(); + buf.level++; + if (c.handler) + c.handler.statementToBuffer(buf, hgs); + buf.level--; + buf.writeByte('}'); + buf.writenl(); } } - override void visit(TryFinallyStatement s) + void visitTryFinally(TryFinallyStatement s) { buf.writestring("try"); buf.writenl(); buf.writeByte('{'); buf.writenl(); buf.level++; - s._body.accept(this); + s._body.statementToBuffer(buf, hgs); buf.level--; buf.writeByte('}'); buf.writenl(); @@ -692,25 +698,25 @@ public: buf.writenl(); if (s.finalbody.isScopeStatement()) { - s.finalbody.accept(this); + s.finalbody.statementToBuffer(buf, hgs); } else { buf.level++; - s.finalbody.accept(this); + s.finalbody.statementToBuffer(buf, hgs); buf.level--; } } - override void visit(ScopeGuardStatement s) + void visitScopeGuard(ScopeGuardStatement s) { buf.writestring(Token.toString(s.tok)); buf.writeByte(' '); if (s.statement) - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(ThrowStatement s) + void visitThrow(ThrowStatement s) { buf.writestring("throw "); s.exp.expressionToBuffer(buf, hgs); @@ -718,15 +724,15 @@ public: buf.writenl(); } - override void visit(DebugStatement s) + void visitDebug(DebugStatement s) { if (s.statement) { - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } } - override void visit(GotoStatement s) + void visitGoto(GotoStatement s) { buf.writestring("goto "); buf.writestring(s.ident.toString()); @@ -734,16 +740,16 @@ public: buf.writenl(); } - override void visit(LabelStatement s) + void visitLabel(LabelStatement s) { buf.writestring(s.ident.toString()); buf.writeByte(':'); buf.writenl(); if (s.statement) - s.statement.accept(this); + s.statement.statementToBuffer(buf, hgs); } - override void visit(AsmStatement s) + void visitAsm(AsmStatement s) { buf.writestring("asm { "); Token* t = s.tokens; @@ -769,33 +775,26 @@ public: buf.writenl(); } - override void visit(ImportStatement s) + void visitInlineAsm(InlineAsmStatement s) { - foreach (imp; *s.imports) - { - imp.dsymbolToBuffer(buf, hgs); - } + visitAsm(s); } - void visit(Catch c) + void visitGccAsm(GccAsmStatement s) { - buf.writestring("catch"); - if (c.type) + visitAsm(s); + } + + void visitImport(ImportStatement s) + { + foreach (imp; *s.imports) { - buf.writeByte('('); - typeToBuffer(c.type, c.ident, buf, hgs); - buf.writeByte(')'); + imp.dsymbolToBuffer(buf, hgs); } - buf.writenl(); - buf.writeByte('{'); - buf.writenl(); - buf.level++; - if (c.handler) - c.handler.accept(this); - buf.level--; - buf.writeByte('}'); - buf.writenl(); } + + mixin VisitStatement!void visit; + visit.VisitStatement(s); } private void dsymbolToBuffer(Dsymbol s, OutBuffer* buf, HdrGenState* hgs) @@ -1165,7 +1164,7 @@ public: } - override void visit(CompileDeclaration d) + override void visit(MixinDeclaration d) { buf.writestring("mixin("); argsToBuffer(d.exps, buf, hgs, null); @@ -2326,6 +2325,16 @@ private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hg expToBuffer(e.e1, precedence[e.op], buf, hgs); } + void visitLoweredAssignExp(LoweredAssignExp e) + { + if (global.params.vcg_ast) + { + expressionToBuffer(e.lowering, buf, hgs); + return; + } + + visit(cast(BinExp)e); + } void visitBin(BinExp e) { expToBuffer(e.e1, precedence[e.op], buf, hgs); @@ -2699,6 +2708,7 @@ private void expressionPrettyPrint(Expression e, OutBuffer* buf, HdrGenState* hg case EXP.remove: return visitRemove(e.isRemoveExp()); case EXP.question: return visitCond(e.isCondExp()); case EXP.classReference: return visitClassReference(e.isClassReferenceExp()); + case EXP.loweredAssignExp: return visitLoweredAssignExp(e.isLoweredAssignExp()); } } @@ -2887,8 +2897,7 @@ public: void toCBuffer(const Statement s, OutBuffer* buf, HdrGenState* hgs) { - scope v = new StatementPrettyPrintVisitor(buf, hgs); - (cast() s).accept(v); + (cast()s).statementToBuffer(buf, hgs); } void toCBuffer(const Type t, OutBuffer* buf, const Identifier ident, HdrGenState* hgs) @@ -3228,6 +3237,7 @@ private void parametersToBuffer(ParameterList pl, OutBuffer* buf, HdrGenState* h final switch (pl.varargs) { case VarArg.none: + case VarArg.KRvariadic: break; case VarArg.variadic: @@ -3822,15 +3832,8 @@ private void initializerToBuffer(Initializer inx, OutBuffer* buf, HdrGenState* h buf.writeByte('}'); } - final switch (inx.kind) - { - case InitKind.error: return visitError (inx.isErrorInitializer ()); - case InitKind.void_: return visitVoid (inx.isVoidInitializer ()); - case InitKind.struct_: return visitStruct(inx.isStructInitializer()); - case InitKind.array: return visitArray (inx.isArrayInitializer ()); - case InitKind.exp: return visitExp (inx.isExpInitializer ()); - case InitKind.C_: return visitC (inx.isCInitializer ()); - } + mixin VisitInitializer!void visit; + visit.VisitInitializer(inx); } @@ -4106,7 +4109,6 @@ string EXPtoString(EXP op) EXP.error : "error", EXP.objcClassReference : "class", - EXP.typeof_ : "typeof", EXP.mixin_ : "mixin", EXP.import_ : "import", @@ -4146,7 +4148,6 @@ string EXPtoString(EXP op) EXP.remove : "remove", EXP.tuple : "tuple", EXP.traits : "__traits", - EXP.default_ : "default", EXP.overloadSet : "__overloadset", EXP.void_ : "void", EXP.vectorArray : "vectorarray", @@ -4237,6 +4238,7 @@ string EXPtoString(EXP op) EXP.declaration : "declaration", EXP.interval : "interval", + EXP.loweredAssignExp : "=" ]; const p = strings[op]; if (!p) diff --git a/dmd/iasmgcc.d b/dmd/iasmgcc.d index f8c88ab536e..1d4dea47b81 100644 --- a/dmd/iasmgcc.d +++ b/dmd/iasmgcc.d @@ -302,7 +302,8 @@ Ldone: extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) { //printf("GccAsmStatement.semantic()\n"); - scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests); // Make a safe copy of the token list before parsing. Token *toklist = null; @@ -410,7 +411,8 @@ unittest { const errors = global.errors; scope gas = new GccAsmStatement(Loc.initial, tokens); - scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink); + const bool doUnittests = false; + scope p = new Parser!ASTCodegen(null, ";", false, global.errorSink, &global.compileEnv, doUnittests); p.token = *tokens; p.parseGccAsm(gas); return global.errors - errors; @@ -420,7 +422,8 @@ unittest static void parseAsm(string input, bool expectError) { // Generate tokens from input test. - scope p = new Parser!ASTCodegen(null, input, false, global.errorSink); + const bool doUnittests = false; + scope p = new Parser!ASTCodegen(null, input, false, global.errorSink, &global.compileEnv, doUnittests); p.nextToken(); Token* toklist = null; diff --git a/dmd/id.d b/dmd/id.d index fd60a569ac5..1933b7c7561 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -361,6 +361,8 @@ immutable Msgtable[] msgtable = { "_d_arrayappendcTXImpl" }, { "_d_arrayappendcTX" }, { "_d_arrayappendcTXTrace" }, + { "_d_arraycatnTX" }, + { "_d_arraycatnTXTrace" }, // varargs implementation { "stdc" }, @@ -370,6 +372,10 @@ immutable Msgtable[] msgtable = // Builtin functions { "std" }, { "core" }, + { "config" }, + { "c_complex_float" }, + { "c_complex_double" }, + { "c_complex_real" }, { "etc" }, { "attribute" }, { "atomic" }, @@ -519,9 +525,17 @@ immutable Msgtable[] msgtable = { "__tag" }, { "dllimport" }, { "dllexport" }, + { "naked" }, + { "thread" }, { "vector_size" }, { "__func__" }, + { "always_inline" }, + { "noinline" }, { "noreturn" }, + { "_nothrow", "nothrow" }, + { "_deprecated", "deprecated" }, + { "_align", "align" }, + { "aligned" }, { "__pragma", "pragma" }, { "builtins", "__builtins" }, { "builtin_va_list", "__builtin_va_list" }, @@ -532,6 +546,7 @@ immutable Msgtable[] msgtable = { "show" }, { "push" }, { "pop" }, + { "_pure", "pure" }, { "define" }, { "undef" }, diff --git a/dmd/identifier.h b/dmd/identifier.h index c12c3554c1b..e7b3ba60b0f 100644 --- a/dmd/identifier.h +++ b/dmd/identifier.h @@ -17,7 +17,7 @@ class Identifier final : public RootObject { private: int value; - bool isAnonymous_; + d_bool isAnonymous_; DString string; public: diff --git a/dmd/init.d b/dmd/init.d index f646d0382eb..6f20a3c7a45 100644 --- a/dmd/init.d +++ b/dmd/init.d @@ -269,7 +269,22 @@ extern (C++) final class CInitializer : Initializer */ Initializer syntaxCopy(Initializer inx) { - static Initializer copyStruct(StructInitializer vi) + static Initializer visitVoid(VoidInitializer vi) + { + return new VoidInitializer(vi.loc); + } + + static Initializer visitError(ErrorInitializer vi) + { + return vi; + } + + static Initializer visitExp(ExpInitializer vi) + { + return new ExpInitializer(vi.loc, vi.exp.syntaxCopy()); + } + + static Initializer visitStruct(StructInitializer vi) { auto si = new StructInitializer(vi.loc); assert(vi.field.length == vi.value.length); @@ -283,7 +298,7 @@ Initializer syntaxCopy(Initializer inx) return si; } - static Initializer copyArray(ArrayInitializer vi) + static Initializer visitArray(ArrayInitializer vi) { auto ai = new ArrayInitializer(vi.loc); assert(vi.index.length == vi.value.length); @@ -297,7 +312,7 @@ Initializer syntaxCopy(Initializer inx) return ai; } - static Initializer copyC(CInitializer vi) + static Initializer visitC(CInitializer vi) { auto ci = new CInitializer(vi.loc); ci.initializerList.setDim(vi.initializerList.length); @@ -322,13 +337,62 @@ Initializer syntaxCopy(Initializer inx) return ci; } - final switch (inx.kind) + mixin VisitInitializer!Initializer visit; + return visit.VisitInitializer(inx); +} + +/*********************************************************** + * Visit each Initializer in init. Call a function visit%s(init) for + * each node, where %s is the op of the node. Otherwise call visitDefault(init) + * for that node. If the visit function returns R.init, continue + * visiting each node, otherwise return the value of R. + * Params: + * Result = return type + * init = Initializer tree to traverse + * Returns: + * Result.init for continue, value of type Result for early exit + */ + +mixin template VisitInitializer(Result) +{ + Result VisitInitializer(Initializer init) + { + final switch (init.kind) + { + case InitKind.void_: mixin(visitCase("Void")); break; + case InitKind.error: mixin(visitCase("Error")); break; + case InitKind.struct_: mixin(visitCase("Struct")); break; + case InitKind.array: mixin(visitCase("Array")); break; + case InitKind.exp: mixin(visitCase("Exp")); break; + case InitKind.C_: mixin(visitCase("C")); break; + } + static if (is(Result == void)) { } else + return Result.init; + } +} + +/**************************************** + * CTFE-only helper function for VisitInitializer. + * Params: + * handler = string for the name of the visit handler + * Returns: boilerplate code for a case + */ +pure string visitCase(string handler) +{ + if (__ctfe) { - case InitKind.void_: return new VoidInitializer(inx.loc); - case InitKind.error: return inx; - case InitKind.struct_: return copyStruct(cast(StructInitializer)inx); - case InitKind.array: return copyArray(cast(ArrayInitializer)inx); - case InitKind.exp: return new ExpInitializer(inx.loc, (cast(ExpInitializer)inx).exp.syntaxCopy()); - case InitKind.C_: return copyC(cast(CInitializer)inx); + return + " + auto ix = init.is"~handler~"Initializer(); + static if (is(Result == void)) + visit"~handler~"(ix); + else + { + Result r = visit"~handler~"(ix); + if (r !is Result.init) + return r; + } + "; } + assert(0); } diff --git a/dmd/init.h b/dmd/init.h index 66b874c91b5..9a6a56b68bb 100644 --- a/dmd/init.h +++ b/dmd/init.h @@ -77,8 +77,8 @@ class ArrayInitializer final : public Initializer Initializers value; // of Initializer *'s unsigned dim; // length of array being initialized Type *type; // type that array will be used to initialize - bool sem; // true if semantic() is run - bool isCarray; // C array semantics + d_bool sem; // true if semantic() is run + d_bool isCarray; // C array semantics bool isAssociativeArray() const; Expression *toAssocArrayLiteral(); @@ -89,7 +89,7 @@ class ArrayInitializer final : public Initializer class ExpInitializer final : public Initializer { public: - bool expandTuples; + d_bool expandTuples; Expression *exp; void accept(Visitor *v) override { v->visit(this); } @@ -112,7 +112,7 @@ class CInitializer final : public Initializer public: DesigInits initializerList; Type *type; // type that array will be used to initialize - bool sem; // true if semantic() is run + d_bool sem; // true if semantic() is run void accept(Visitor *v) override { v->visit(this); } }; diff --git a/dmd/initsem.d b/dmd/initsem.d index 18b10b41a2d..054ca766868 100644 --- a/dmd/initsem.d +++ b/dmd/initsem.d @@ -582,7 +582,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ Initializer visitC(CInitializer ci) { - //printf("CInitializer::semantic() (%s) %s\n", t.toChars(), ci.toChars()); + //printf("CInitializer::semantic() tx: %s t: %s ci: %s\n", (tx ? tx.toChars() : "".ptr), t.toChars(), ci.toChars()); /* Rewrite CInitializer into ExpInitializer, ArrayInitializer, or StructInitializer */ t = t.toBasetype(); @@ -767,13 +767,15 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ return err(); } const nfields = sd.fields.length; - size_t fieldi = 0; + Loop1: for (size_t index = 0; index < ci.initializerList.length; ) { - auto di = ci.initializerList[index]; - auto dlist = di.designatorList; + CInitializer cprev; + L1: + DesigInit di = ci.initializerList[index]; + Designators* dlist = di.designatorList; if (dlist) { const length = (*dlist).length; @@ -796,14 +798,34 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ si.addInit(id, di.initializer); ++fieldi; ++index; - break; + continue Loop1; } } + if (cprev) + { + /* The peeling didn't work, so unpeel it + */ + ci = cprev; + di = ci.initializerList[index]; + goto L2; + } + error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), sd.toChars()); + return err(); } else { if (fieldi == nfields) break; + if (index == 0 && ci.initializerList.length == 1 && di.initializer.isCInitializer()) + { + /* Try peeling off this set of { } and see if it works + */ + cprev = ci; + ci = di.initializer.isCInitializer(); + goto L1; + } + + L2: VarDeclaration field; while (1) // skip field if it overlaps with previously seen fields { @@ -951,22 +973,19 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ return initializerSemantic(ai, sc, tx, needInterpret); } else if (ExpInitializer ei = isBraceExpression()) + { return visitExp(ei); + } else { - assert(0); + error(ci.loc, "unrecognized C initializer `%s`", ci.toChars()); + return err(); } } - final switch (init.kind) - { - case InitKind.void_: return visitVoid (init.isVoidInitializer()); - case InitKind.error: return visitError (init.isErrorInitializer()); - case InitKind.struct_: return visitStruct(init.isStructInitializer()); - case InitKind.array: return visitArray (init.isArrayInitializer()); - case InitKind.exp: return visitExp (init.isExpInitializer()); - case InitKind.C_: return visitC (init.isCInitializer()); - } + mixin VisitInitializer!Initializer visit; + auto result = visit.VisitInitializer(init); + return (result !is null) ? result : new ErrorInitializer(); } /*********************** @@ -1120,15 +1139,9 @@ Initializer inferType(Initializer init, Scope* sc) return new ErrorInitializer(); } - final switch (init.kind) - { - case InitKind.void_: return visitVoid (init.isVoidInitializer()); - case InitKind.error: return visitError (init.isErrorInitializer()); - case InitKind.struct_: return visitStruct(init.isStructInitializer()); - case InitKind.array: return visitArray (init.isArrayInitializer()); - case InitKind.exp: return visitExp (init.isExpInitializer()); - case InitKind.C_: return visitC (init.isCInitializer()); - } + mixin VisitInitializer!Initializer visit; + auto result = visit.VisitInitializer(init); + return (result !is null) ? result : new ErrorInitializer(); } /*********************** @@ -1333,15 +1346,8 @@ extern (C++) Expression initializerToExpression(Initializer init, Type itype = n return null; } - final switch (init.kind) - { - case InitKind.void_: return visitVoid (init.isVoidInitializer()); - case InitKind.error: return visitError (init.isErrorInitializer()); - case InitKind.struct_: return visitStruct(init.isStructInitializer()); - case InitKind.array: return visitArray (init.isArrayInitializer()); - case InitKind.exp: return visitExp (init.isExpInitializer()); - case InitKind.C_: return visitC (init.isCInitializer()); - } + mixin VisitInitializer!Expression visit; + return visit.VisitInitializer(init); } diff --git a/dmd/inline.d b/dmd/inline.d index 0a94d4669fe..4c5836e7226 100644 --- a/dmd/inline.d +++ b/dmd/inline.d @@ -69,12 +69,17 @@ public void inlineScanModule(Module m) Dsymbol s = (*m.members)[i]; //if (global.params.verbose) // message("inline scan symbol %s", s.toChars()); - scope InlineScanVisitor v = new InlineScanVisitor(); - s.accept(v); + inlineScanDsymbol(s); } m.semanticRun = PASS.inlinedone; } +private void inlineScanDsymbol(Dsymbol s) +{ + scope InlineScanVisitorDsymbol v = new InlineScanVisitorDsymbol(); + s.accept(v); +} + /*********************************************************** * Perform the "inline copying" of a default argument for a function parameter. * @@ -745,6 +750,21 @@ version (IN_LLVM) {} else result = ae; } + override void visit(CatExp e) + { + auto ce = e.copy().isCatExp(); + + if (auto lowering = ce.lowering) + ce.lowering = doInlineAs!Expression(lowering, ids); + else + { + ce.e1 = doInlineAs!Expression(e.e1, ids); + ce.e2 = doInlineAs!Expression(e.e2, ids); + } + + result = ce; + } + override void visit(BinExp e) { auto be = cast(BinExp)e.copy(); @@ -764,12 +784,11 @@ version (IN_LLVM) {} else override void visit(AssignExp e) { visit(cast(BinExp)e); + } - if (auto ale = e.e1.isArrayLengthExp()) - { - Type tn = ale.e1.type.toBasetype().nextOf(); - semanticTypeInfo(null, tn); - } + override void visit(LoweredAssignExp e) + { + result = doInlineAs!Expression(e.lowering, ids); } override void visit(EqualExp e) @@ -1225,7 +1244,7 @@ public: } else { - s.accept(this); + inlineScanDsymbol(s); } } @@ -1246,6 +1265,18 @@ public: inlineScan(e.msg); } + override void visit(CatExp e) + { + if (auto lowering = e.lowering) + { + inlineScan(lowering); + return; + } + + inlineScan(e.e1); + inlineScan(e.e2); + } + override void visit(BinExp e) { inlineScan(e.e1); @@ -1290,6 +1321,11 @@ public: visit(cast(BinExp)e); } + override void visit(LoweredAssignExp e) + { + inlineScan(e.lowering); + } + override void visit(CallExp e) { //printf("CallExp.inlineScan() %s\n", e.toChars()); @@ -1497,7 +1533,7 @@ public: //printf("StructLiteralExp.inlineScan()\n"); if (e.stageflags & stageInlineScan) return; - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageInlineScan; arrayInlineScan(e.elements); e.stageflags = old; @@ -1535,6 +1571,20 @@ public: eresult = null; } } +} + +/*********************************************************** + * Walk the trees, looking for functions to inline. + * Inline any that can be. + */ +private extern (C++) final class InlineScanVisitorDsymbol : Visitor +{ + alias visit = Visitor.visit; +public: + + extern (D) this() scope + { + } /************************************* * Look for function inlining possibilities. @@ -1556,20 +1606,20 @@ public: return; if (fd.fbody && !fd.isNaked()) { - auto againsave = again; - auto parentsave = parent; - parent = fd; - do + while (1) { - again = false; fd.inlineNest++; fd.inlineScanned = true; - inlineScan(fd.fbody); + + scope InlineScanVisitor v = new InlineScanVisitor(); + v.parent = fd; + v.inlineScan(fd.fbody); + bool again = v.again; + fd.inlineNest--; + if (!again) + break; } - while (again); - again = againsave; - parent = parentsave; } } @@ -1708,7 +1758,8 @@ private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool stat TypeFunction tf = fd.type.isTypeFunction(); // no variadic parameter lists - if (tf.parameterList.varargs == VarArg.variadic) + if (tf.parameterList.varargs == VarArg.variadic || + tf.parameterList.varargs == VarArg.KRvariadic) goto Lno; /* No lazy parameters when inlining by statement, as the inliner tries to @@ -1815,8 +1866,7 @@ private bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool stat else fd.inlineStatusExp = ILS.yes; - scope InlineScanVisitor v = new InlineScanVisitor(); - fd.accept(v); // Don't scan recursively for header content scan + inlineScanDsymbol(fd); // Don't scan recursively for header content scan if (fd.inlineStatusExp == ILS.uninitialized) { diff --git a/dmd/json.d b/dmd/json.d index 2af7faec354..dcf53b8f547 100644 --- a/dmd/json.d +++ b/dmd/json.d @@ -833,7 +833,7 @@ public: { import dmd.target : target; objectStart(); - requiredProperty("vendor", global.vendor); + requiredProperty("vendor", global.compileEnv.vendor); requiredProperty("version", global.versionString()); property("__VERSION__", global.versionNumber()); requiredProperty("interface", determineCompilerInterface()); @@ -1070,13 +1070,13 @@ Determines and returns the compiler interface which is one of `dmd`, `ldc`, */ private extern(D) string determineCompilerInterface() { - if (global.vendor == "Digital Mars D") + if (global.compileEnv.vendor == "Digital Mars D") return "dmd"; - if (global.vendor == "LDC") + if (global.compileEnv.vendor == "LDC") return "ldc"; - if (global.vendor == "GNU D") + if (global.compileEnv.vendor == "GNU D") return "gdc"; - if (global.vendor == "SDC") + if (global.compileEnv.vendor == "SDC") return "sdc"; return null; } diff --git a/dmd/ldcbindings.d b/dmd/ldcbindings.d index 018f9874304..e68aee435dd 100644 --- a/dmd/ldcbindings.d +++ b/dmd/ldcbindings.d @@ -80,6 +80,6 @@ Expression createExpressionForIntOp(const ref Loc loc, TOK op, Expression e1, Ex } } -Expression createExpression(const ref Loc loc, EXP op) { return new Expression(loc, op, __traits(classInstanceSize, Expression)); } +Expression createExpression(const ref Loc loc, EXP op) { return new Expression(loc, op); } DsymbolExp createDsymbolExp(const ref Loc loc, Dsymbol s) { return new DsymbolExp(loc, s, /*hasOverloads=*/false); } AddrExp createAddrExp(const ref Loc loc, Expression e) { return new AddrExp(loc, e); } diff --git a/dmd/lexer.d b/dmd/lexer.d index f0f7872c2b2..b5f6617b99a 100644 --- a/dmd/lexer.d +++ b/dmd/lexer.d @@ -14,12 +14,8 @@ module dmd.lexer; import core.stdc.ctype; -import core.stdc.errno; -import core.stdc.stdarg; import core.stdc.stdio; -import core.stdc.stdlib : getenv; import core.stdc.string; -import core.stdc.time; import dmd.entity; import dmd.errorsink; @@ -31,10 +27,8 @@ import dmd.root.ctfloat; import dmd.common.outbuffer; import dmd.root.port; import dmd.root.rmem; -import dmd.root.string; import dmd.root.utf; import dmd.tokens; -import dmd.utils; nothrow: @@ -43,6 +37,22 @@ version (DMDLIB) version = LocOffset; } +/*********************************************************** + * Values to use for various magic identifiers + */ +struct CompileEnv +{ + uint versionNumber; /// __VERSION__ + const(char)[] date; /// __DATE__ + const(char)[] time; /// __TIME__ + const(char)[] vendor; /// __VENDOR__ + const(char)[] timestamp; /// __TIMESTAMP__ + + bool previewIn; /// `in` means `[ref] scope const`, accepts rvalues + bool ddocOutput; /// collect embedded documentation comments + bool shortenedMethods = true; /// allow => in normal function declarations +} + /*********************************************************** */ class Lexer @@ -69,6 +79,7 @@ class Lexer ubyte wchar_tsize; /// size of C wchar_t, 2 or 4 ErrorSink eSink; /// send error messages through this interface + CompileEnv compileEnv; /// environment private { @@ -87,8 +98,6 @@ class Lexer int lastDocLine; // last line of previous doc comment Token* tokenFreelist; - uint versionNumber; - const(char)[] vendor; } nothrow: @@ -105,13 +114,12 @@ class Lexer * doDocComment = handle documentation comments * commentToken = comments become TOK.comment's * errorSink = where error messages go, must not be null - * vendor = name of the vendor - * versionNumber = version of the caller + * compileEnv = version, vendor, date, time, etc. */ this(const(char)* filename, const(char)* base, size_t begoffset, size_t endoffset, bool doDocComment, bool commentToken, ErrorSink errorSink, - const(char)[] vendor = "DLF", uint versionNumber = 1) pure scope + const CompileEnv* compileEnv) pure scope { scanloc = Loc(filename, 1, 1); // debug printf("Lexer::Lexer(%p)\n", base); @@ -128,8 +136,13 @@ class Lexer this.lastDocLine = 0; this.eSink = errorSink; assert(errorSink); - this.versionNumber = versionNumber; - this.vendor = vendor; + if (compileEnv) + this.compileEnv = *compileEnv; + else + { + this.compileEnv.versionNumber = 1; + this.compileEnv.vendor = "DLF"; + } //initKeywords(); /* If first line starts with '#!', ignore the line */ @@ -169,10 +182,10 @@ class Lexer */ this(const(char)* filename, const(char)* base, size_t begoffset, size_t endoffset, bool doDocComment, bool commentToken, bool whitespaceToken, - ErrorSink errorSink + ErrorSink errorSink, const CompileEnv* compileEnv = null ) { - this(filename, base, begoffset, endoffset, doDocComment, commentToken, errorSink); + this(filename, base, begoffset, endoffset, doDocComment, commentToken, errorSink, compileEnv); this.whitespaceToken = whitespaceToken; } @@ -571,36 +584,26 @@ class Lexer else if (*t.ptr == '_') // if special identifier token { - // Lazy initialization - TimeStampInfo.initialize(t.loc, eSink); - - if (id == Id.DATE) + void toToken(const(char)[] s) { - t.ustring = TimeStampInfo.date.ptr; - goto Lstr; + t.value = TOK.string_; + t.ustring = s.ptr; + t.len = cast(uint)s.length; + t.postfix = 0; } + + if (id == Id.DATE) + toToken(compileEnv.date); else if (id == Id.TIME) - { - t.ustring = TimeStampInfo.time.ptr; - goto Lstr; - } + toToken(compileEnv.time); else if (id == Id.VENDOR) - { - t.ustring = vendor.xarraydup.ptr; - goto Lstr; - } + toToken(compileEnv.vendor); else if (id == Id.TIMESTAMP) - { - t.ustring = TimeStampInfo.timestamp.ptr; - Lstr: - t.value = TOK.string_; - t.postfix = 0; - t.len = cast(uint)strlen(t.ustring); - } + toToken(compileEnv.timestamp); else if (id == Id.VERSIONX) { t.value = TOK.int64Literal; - t.unsvalue = versionNumber; + t.unsvalue = compileEnv.versionNumber; } else if (id == Id.EOFX) { @@ -2570,6 +2573,14 @@ class Lexer TOK result; bool isOutOfRange = false; t.floatvalue = (isWellformedString ? CTFloat.parse(sbufptr, isOutOfRange) : CTFloat.zero); + + bool imaginary = false; + if (*p == 'i' && Ccompile) + { + ++p; + imaginary = true; + } + switch (*p) { case 'F': @@ -2590,16 +2601,25 @@ class Lexer goto case 'L'; case 'L': ++p; +version (IN_LLVM) { /* *always* map C `long double` literals to D `real` ones */ } else +{ if (Ccompile && long_doublesize == 8) goto default; +} result = TOK.float80Literal; break; } + if ((*p == 'i' || *p == 'I') && !Ccompile) { if (*p == 'I') error("use 'i' suffix instead of 'I'"); p++; + imaginary = true; + } + + if (imaginary) + { switch (result) { case TOK.float32Literal: @@ -3033,7 +3053,10 @@ class Lexer auto dc = (lineComment && anyToken) ? &t.lineComment : &t.blockComment; // Combine with previous doc comment, if any if (*dc) - *dc = combineComments(*dc, buf[], newParagraph).toDString(); + { + auto p = combineComments(*dc, buf[], newParagraph); + *dc = p ? p[0 .. strlen(p)] : null; + } else *dc = buf.extractSlice(true); } @@ -3081,42 +3104,6 @@ class Lexer private: -/// Support for `__DATE__`, `__TIME__`, and `__TIMESTAMP__` -private struct TimeStampInfo -{ - private __gshared bool initdone = false; - - // Note: Those properties need to be guarded by a call to `init` - // The API isn't safe, and quite brittle, but it was left this way - // over performance concerns. - // This is currently only called once, from the lexer. - __gshared char[11 + 1] date; - __gshared char[8 + 1] time; - __gshared char[24 + 1] timestamp; - - public static void initialize(const ref Loc loc, ErrorSink eSink) nothrow - { - if (initdone) - return; - - initdone = true; - time_t ct; - // https://issues.dlang.org/show_bug.cgi?id=20444 - if (auto p = getenv("SOURCE_DATE_EPOCH")) - { - if (!ct.parseDigits(p.toDString())) - eSink.error(loc, "value of environment variable `SOURCE_DATE_EPOCH` should be a valid UNIX timestamp, not: `%s`", p); - } - else - .time(&ct); - const p = ctime(&ct); - assert(p); - snprintf(&date[0], date.length, "%.6s %.4s", p + 4, p + 20); - snprintf(&time[0], time.length, "%.8s", p + 11); - snprintf(×tamp[0], timestamp.length, "%.24s", p); - } -} - private enum LS = 0x2028; // UTF line separator private enum PS = 0x2029; // UTF paragraph separator @@ -3366,7 +3353,7 @@ unittest */ string text = "int"; // We rely on the implicit null-terminator ErrorSink errorSink = new ErrorSinkStderr; - scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, false, false, errorSink); + scope Lexer lex1 = new Lexer(null, text.ptr, 0, text.length, false, false, errorSink, null); TOK tok; tok = lex1.nextToken(); //printf("tok == %s, %d, %d\n", Token::toChars(tok), tok, TOK.int32); @@ -3402,7 +3389,7 @@ unittest foreach (testcase; testcases) { - scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, false, false, errorSink); + scope Lexer lex2 = new Lexer(null, testcase.ptr, 0, testcase.length-1, false, false, errorSink, null); TOK tok = lex2.nextToken(); size_t iterations = 1; while ((tok != TOK.endOfFile) && (iterations++ < testcase.length)) diff --git a/dmd/mars.d b/dmd/mars.d index 49561188675..dd34292d086 100644 --- a/dmd/mars.d +++ b/dmd/mars.d @@ -178,6 +178,10 @@ private int tryMain(size_t argc, const(char)** argv, ref Param params) if (parseCommandlineAndConfig(argc, argv, params, files)) return EXIT_FAILURE; + global.compileEnv.previewIn = global.params.previewIn; + global.compileEnv.ddocOutput = global.params.ddoc.doOutput; + global.compileEnv.shortenedMethods = global.params.shortenedMethods; + if (params.usage) { usage(); @@ -1338,187 +1342,6 @@ private void setDefaultLibrary(ref Param params, const ref Target target) driverParams.debuglibname = driverParams.defaultlibname; } -/** - * Add default `version` identifier for dmd, and set the - * target platform in `params`. - * https://dlang.org/spec/version.html#predefined-versions - * - * Needs to be run after all arguments parsing (command line, DFLAGS environment - * variable and config file) in order to add final flags (such as `X86_64` or - * the `CRuntime` used). - * - * Params: - * params = which target to compile for (set by `setTarget()`) - * tgt = target - */ -public -void addDefaultVersionIdentifiers(const ref Param params, const ref Target tgt) -{ - VersionCondition.addPredefinedGlobalIdent("DigitalMars"); - VersionCondition.addPredefinedGlobalIdent("LittleEndian"); - VersionCondition.addPredefinedGlobalIdent("D_Version2"); - VersionCondition.addPredefinedGlobalIdent("all"); - - addPredefinedGlobalIdentifiers(tgt); - - if (params.ddoc.doOutput) - VersionCondition.addPredefinedGlobalIdent("D_Ddoc"); - if (params.cov) - VersionCondition.addPredefinedGlobalIdent("D_Coverage"); - if (driverParams.pic != PIC.fixed) - VersionCondition.addPredefinedGlobalIdent(driverParams.pic == PIC.pic ? "D_PIC" : "D_PIE"); - if (params.useUnitTests) - VersionCondition.addPredefinedGlobalIdent("unittest"); - if (params.useAssert == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("assert"); - if (params.useIn == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("D_PreConditions"); - if (params.useOut == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("D_PostConditions"); - if (params.useInvariants == CHECKENABLE.on) - VersionCondition.addPredefinedGlobalIdent("D_Invariants"); - if (params.useArrayBounds == CHECKENABLE.off) - VersionCondition.addPredefinedGlobalIdent("D_NoBoundsChecks"); - if (params.betterC) - { - VersionCondition.addPredefinedGlobalIdent("D_BetterC"); - } - else - { - VersionCondition.addPredefinedGlobalIdent("D_ModuleInfo"); - VersionCondition.addPredefinedGlobalIdent("D_Exceptions"); - VersionCondition.addPredefinedGlobalIdent("D_TypeInfo"); - } - - VersionCondition.addPredefinedGlobalIdent("D_HardFloat"); - - if (params.tracegc) - VersionCondition.addPredefinedGlobalIdent("D_ProfileGC"); - - if (driverParams.optimize) - VersionCondition.addPredefinedGlobalIdent("D_Optimized"); -} - -/** - * Add predefined global identifiers that are determied by the target - */ -private -void addPredefinedGlobalIdentifiers(const ref Target tgt) -{ - import dmd.cond : VersionCondition; - - alias predef = VersionCondition.addPredefinedGlobalIdent; - if (tgt.cpu >= CPU.sse2) - { - predef("D_SIMD"); - if (tgt.cpu >= CPU.avx) - predef("D_AVX"); - if (tgt.cpu >= CPU.avx2) - predef("D_AVX2"); - } - - with (Target) - { - if (tgt.os & OS.Posix) - predef("Posix"); - if (tgt.os & (OS.linux | OS.FreeBSD | OS.OpenBSD | OS.DragonFlyBSD | OS.Solaris)) - predef("ELFv1"); - switch (tgt.os) - { - case OS.none: { predef("FreeStanding"); break; } - case OS.linux: { predef("linux"); break; } - case OS.OpenBSD: { predef("OpenBSD"); break; } - case OS.DragonFlyBSD: { predef("DragonFlyBSD"); break; } - case OS.Solaris: { predef("Solaris"); break; } - case OS.Windows: - { - predef("Windows"); - VersionCondition.addPredefinedGlobalIdent(tgt.is64bit ? "Win64" : "Win32"); - break; - } - case OS.OSX: - { - predef("OSX"); - // For legacy compatibility - predef("darwin"); - break; - } - case OS.FreeBSD: - { - predef("FreeBSD"); - switch (tgt.osMajor) - { - case 10: predef("FreeBSD_10"); break; - case 11: predef("FreeBSD_11"); break; - case 12: predef("FreeBSD_12"); break; - case 13: predef("FreeBSD_13"); break; - default: predef("FreeBSD_11"); break; - } - break; - } - default: assert(0); - } - } - - addCRuntimePredefinedGlobalIdent(tgt.c); - addCppRuntimePredefinedGlobalIdent(tgt.cpp); - - if (tgt.is64bit) - { - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); - VersionCondition.addPredefinedGlobalIdent("X86_64"); - } - else - { - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy - VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); - VersionCondition.addPredefinedGlobalIdent("X86"); - } - if (tgt.isLP64) - VersionCondition.addPredefinedGlobalIdent("D_LP64"); - else if (tgt.is64bit) - VersionCondition.addPredefinedGlobalIdent("X32"); -} - -private -void addCRuntimePredefinedGlobalIdent(const ref TargetC c) -{ - import dmd.cond : VersionCondition; - - alias predef = VersionCondition.addPredefinedGlobalIdent; - with (TargetC.Runtime) switch (c.runtime) - { - default: - case Unspecified: return; - case Bionic: return predef("CRuntime_Bionic"); - case DigitalMars: return predef("CRuntime_DigitalMars"); - case Glibc: return predef("CRuntime_Glibc"); - case Microsoft: return predef("CRuntime_Microsoft"); - case Musl: return predef("CRuntime_Musl"); - case Newlib: return predef("CRuntime_Newlib"); - case UClibc: return predef("CRuntime_UClibc"); - case WASI: return predef("CRuntime_WASI"); - } -} - -private -void addCppRuntimePredefinedGlobalIdent(const ref TargetCPP cpp) -{ - import dmd.cond : VersionCondition; - - alias predef = VersionCondition.addPredefinedGlobalIdent; - with (TargetCPP.Runtime) switch (cpp.runtime) - { - default: - case Unspecified: return; - case Clang: return predef("CppRuntime_Clang"); - case DigitalMars: return predef("CppRuntime_DigitalMars"); - case Gcc: return predef("CppRuntime_Gcc"); - case Microsoft: return predef("CppRuntime_Microsoft"); - case Sun: return predef("CppRuntime_Sun"); - } -} - } // !IN_LLVM private void printPredefinedVersions(FILE* stream) @@ -1857,6 +1680,18 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, ref Param if (arg == "-allinst") // https://dlang.org/dmd.html#switch-allinst params.allInst = true; + else if (startsWith(p + 1, "cpp=")) // https://dlang.org/dmd.html#switch-cpp + { + if (p[5]) + { + params.cpp = p + 5; + } + else + { + errorInvalidSwitch(p, "it must be followed by the filename of the desired C preprocessor"); + return false; + } + } else if (arg == "-de") // https://dlang.org/dmd.html#switch-de params.useDeprecated = DiagnosticReporting.error; else if (arg == "-d") // https://dlang.org/dmd.html#switch-d diff --git a/dmd/module.h b/dmd/module.h index 6dbab15933f..874a1994d91 100644 --- a/dmd/module.h +++ b/dmd/module.h @@ -82,8 +82,8 @@ class Module final : public Package unsigned errors; // if any errors in file unsigned numlines; // number of lines in source file FileType filetype; // source file type - bool hasAlwaysInlines; // contains references to functions that must be inlined - bool isPackageFile; // if it is a package.d + d_bool hasAlwaysInlines; // contains references to functions that must be inlined + d_bool isPackageFile; // if it is a package.d Package *pkg; // if isPackageFile is true, the Package that contains this package.d Strings contentImportedFiles; // array of files whose content was imported int needmoduleinfo; @@ -98,7 +98,7 @@ class Module final : public Package Identifier *searchCacheIdent; Dsymbol *searchCacheSymbol; // cached value of search int searchCacheFlags; // cached flags - bool insearch; + d_bool insearch; // module from command line we're imported from, // i.e. a module that will be taken all the @@ -189,7 +189,7 @@ struct ModuleDeclaration Loc loc; Identifier *id; DArray packages; // array of Identifier's representing packages - bool isdeprecated; // if it is a deprecated module + d_bool isdeprecated; // if it is a deprecated module Expression *msg; const char *toChars() const; diff --git a/dmd/mtype.d b/dmd/mtype.d index 1858553645c..9b210528f7d 100644 --- a/dmd/mtype.d +++ b/dmd/mtype.d @@ -313,6 +313,7 @@ int mutabilityOfType(bool isref, Type t) */ enum DotExpFlag { + none = 0, gag = 1, // don't report "not a property" error and just return null noDeref = 2, // the use of the expression will not attempt a dereference noAliasThis = 4, // don't do 'alias this' resolution @@ -2048,9 +2049,11 @@ version (IN_LLVM) t = t.addMod(this.mod); return t; } - if (auto fd = s.isFuncDeclaration()) + Dsymbol callable = s.isFuncDeclaration(); + callable = callable ? callable : s.isTemplateDeclaration(); + if (callable) { - fd = resolveFuncCall(Loc.initial, null, fd, null, this, ArgumentList(), FuncResolveFlag.quiet); + auto fd = resolveFuncCall(Loc.initial, null, callable, null, this, ArgumentList(), FuncResolveFlag.quiet); if (!fd || fd.errors || !fd.functionSemantic()) return Type.terror; @@ -2069,24 +2072,17 @@ version (IN_LLVM) { return ed.type; } - if (auto td = s.isTemplateDeclaration()) - { - assert(td._scope); - auto fd = resolveFuncCall(Loc.initial, null, td, null, this, ArgumentList(), FuncResolveFlag.quiet); - if (!fd || fd.errors || !fd.functionSemantic()) - return Type.terror; - - auto t = fd.type.nextOf(); - if (!t) - return Type.terror; - t = t.substWildTo(mod == 0 ? MODFlags.mutable : mod); - return t; - } //printf("%s\n", s.kind()); return null; } + /** + * Check whether this type has endless `alias this` recursion. + * Returns: + * `true` if this type has an `alias this` that can be implicitly + * converted back to this type itself. + */ extern (D) final bool checkAliasThisRec() { Type tb = toBasetype(); @@ -2669,6 +2665,8 @@ version (IN_LLVM) if (t.isimaginary() || t.iscomplex()) { + if (sc.flags & SCOPE.Cfile) + return true; // complex/imaginary not deprecated in C code Type rt; switch (t.ty) { @@ -4294,7 +4292,7 @@ extern (C++) final class TypeFunction : TypeNext super(Tfunction, treturn); //if (!treturn) *(char*)0=0; // assert(treturn); - assert(VarArg.none <= pl.varargs && pl.varargs <= VarArg.typesafe); + assert(VarArg.none <= pl.varargs && pl.varargs <= VarArg.max); this.parameterList = pl; this.linkage = linkage; @@ -6537,6 +6535,7 @@ extern (C++) final class TypeTag : Type { Loc loc; /// location of declaration TOK tok; /// TOK.struct_, TOK.union_, TOK.enum_ + structalign_t packalign; /// alignment of struct/union fields Identifier id; /// tag name identifier Type base; /// base type for enums otherwise null Dsymbols* members; /// members of struct, null if none @@ -6546,13 +6545,14 @@ extern (C++) final class TypeTag : Type /// struct S { int a; } s1, *s2; MOD mod; /// modifiers to apply after type is resolved (only MODFlags.const_ at the moment) - extern (D) this(const ref Loc loc, TOK tok, Identifier id, Type base, Dsymbols* members) + extern (D) this(const ref Loc loc, TOK tok, Identifier id, structalign_t packalign, Type base, Dsymbols* members) { //printf("TypeTag ctor %s %p\n", id ? id.toChars() : "null".ptr, this); super(Ttag); this.loc = loc; this.tok = tok; this.id = id; + this.packalign = packalign; this.base = base; this.members = members; this.mod = 0; @@ -7276,7 +7276,7 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, s ~= "pure "; if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) s ~= "@safe "; - if (!f.isNogc && sc.func.setGC()) + if (!f.isNogc && sc.func.setGC(arg.loc, null)) s ~= "nogc "; if (s) { @@ -7603,3 +7603,113 @@ TypeVector toBooleanVector(TypeVector tv) return new TypeVector(new TypeSArray(telem, tsa.dim)); } + +/************************************************* + * Dispatch to function based on static type of Type. + */ +mixin template VisitType(Result) +{ + Result VisitType(Type t) + { + final switch (t.ty) + { + case TY.Tvoid: + case TY.Tint8: + case TY.Tuns8: + case TY.Tint16: + case TY.Tuns16: + case TY.Tint32: + case TY.Tuns32: + case TY.Tint64: + case TY.Tuns64: + case TY.Tfloat32: + case TY.Tfloat64: + case TY.Tfloat80: + case TY.Timaginary32: + case TY.Timaginary64: + case TY.Timaginary80: + case TY.Tcomplex32: + case TY.Tcomplex64: + case TY.Tcomplex80: + case TY.Tbool: + case TY.Tchar: + case TY.Twchar: + case TY.Tdchar: + case TY.Tint128: + case TY.Tuns128: mixin(visitTYCase("Basic")); + case TY.Tarray: mixin(visitTYCase("DArray")); + case TY.Tsarray: mixin(visitTYCase("SArray")); + case TY.Taarray: mixin(visitTYCase("AArray")); + case TY.Tpointer: mixin(visitTYCase("Pointer")); + case TY.Treference: mixin(visitTYCase("Reference")); + case TY.Tfunction: mixin(visitTYCase("Function")); + case TY.Tident: mixin(visitTYCase("Identifier")); + case TY.Tclass: mixin(visitTYCase("Class")); + case TY.Tstruct: mixin(visitTYCase("Struct")); + case TY.Tenum: mixin(visitTYCase("Enum")); + case TY.Tdelegate: mixin(visitTYCase("Delegate")); + case TY.Terror: mixin(visitTYCase("Error")); + case TY.Tinstance: mixin(visitTYCase("Instance")); + case TY.Ttypeof: mixin(visitTYCase("Typeof")); + case TY.Ttuple: mixin(visitTYCase("Tuple")); + case TY.Tslice: mixin(visitTYCase("Slice")); + case TY.Treturn: mixin(visitTYCase("Return")); + case TY.Tnull: mixin(visitTYCase("Null")); + case TY.Tvector: mixin(visitTYCase("Vector")); + case TY.Ttraits: mixin(visitTYCase("Traits")); + case TY.Tmixin: mixin(visitTYCase("Mixin")); + case TY.Tnoreturn: mixin(visitTYCase("Noreturn")); + case TY.Ttag: mixin(visitTYCase("Tag")); + case TY.Tnone: assert(0); + } + } +} + +/**************************************** + * CTFE-only helper function for VisitInitializer. + * Params: + * handler = string for the name of the visit handler + * Returns: boilerplate code for a case + */ +pure string visitTYCase(string handler) +{ + if (__ctfe) + { + return + " + enum isVoid = is(Result == void); + auto tx = t.isType"~handler~"(); + static if (__traits(compiles, visit"~handler~"(tx))) + { + static if (isVoid) + { + visit"~handler~"(tx); + return; + } + else + { + if (Result r = visit"~handler~"(tx)) + return r; + return Result.init; + } + } + else static if (__traits(compiles, visitDefaultCase(t))) + { + static if (isVoid) + { + visitDefaultCase(tx); + return; + } + else + { + if (Result r = visitDefaultCase(t)) + return r; + return Result.init; + } + } + else + static assert(0, "~handler~"); + "; + } + assert(0); +} diff --git a/dmd/mtype.h b/dmd/mtype.h index 5d22bf64b4d..d48e7388231 100644 --- a/dmd/mtype.h +++ b/dmd/mtype.h @@ -125,8 +125,9 @@ enum VarArgValues { VARARGnone = 0, /// fixed number of arguments VARARGvariadic = 1, /// T t, ...) can be C-style (core.stdc.stdarg) or D-style (core.vararg) - VARARGtypesafe = 2 /// T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions + VARARGtypesafe = 2, /// T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions /// or https://dlang.org/spec/function.html#typesafe_variadic_functions + VARARGKRvariadic = 3 /// K+R C style variadics (no function prototype) }; typedef unsigned char VarArg; @@ -594,7 +595,7 @@ struct ParameterList Parameters* parameters; StorageClass stc; VarArg varargs; - bool hasIdentifierList; // true if C identifier-list style + d_bool hasIdentifierList; // true if C identifier-list style size_t length(); Parameter *operator[](size_t i) { return Parameter::getNth(parameters, i); } @@ -784,7 +785,7 @@ class TypeStruct final : public Type public: StructDeclaration *sym; AliasThisRec att; - bool inuse; + d_bool inuse; static TypeStruct *create(StructDeclaration *sym); const char *kind() override; diff --git a/dmd/nogc.d b/dmd/nogc.d index 201f168527c..a0f3e60861b 100644 --- a/dmd/nogc.d +++ b/dmd/nogc.d @@ -83,7 +83,7 @@ public: err = true; return true; } - if (f.setGC()) + if (f.setGC(e.loc, format)) { e.error(format, f.kind(), f.toPrettyChars()); err = true; @@ -135,7 +135,7 @@ public: override void visit(NewExp e) { - if (e.member && !e.member.isNogc() && f.setGC()) + if (e.member && !e.member.isNogc() && f.setGC(e.loc, null)) { // @nogc-ness is already checked in NewExp::semantic return; @@ -195,7 +195,7 @@ public: err = true; return; } - if (f.setGC()) + if (f.setGC(e.loc, null)) { err = true; return; diff --git a/dmd/ob.d b/dmd/ob.d index 9cff76b84aa..89728b64486 100644 --- a/dmd/ob.d +++ b/dmd/ob.d @@ -844,7 +844,7 @@ void toObNodes(ref ObNodes obnodes, Statement s) case STMT.Conditional: case STMT.While: case STMT.Forwarding: - case STMT.Compile: + case STMT.Mixin: case STMT.Peel: case STMT.Synchronized: debug printf("s: %s\n", s.toChars()); diff --git a/dmd/objc.h b/dmd/objc.h index 305ce812487..a5cc6f1b089 100644 --- a/dmd/objc.h +++ b/dmd/objc.h @@ -37,8 +37,8 @@ struct ObjcSelector struct ObjcClassDeclaration { - bool isMeta; - bool isExtern; + d_bool isMeta; + d_bool isExtern; Identifier* identifier; ClassDeclaration* classDeclaration; @@ -52,7 +52,7 @@ struct ObjcFuncDeclaration { ObjcSelector* selector; VarDeclaration* selectorParameter; - bool isOptional; + d_bool isOptional; }; class Objc diff --git a/dmd/opover.d b/dmd/opover.d index 3c80e5e1d0e..771287691ad 100644 --- a/dmd/opover.d +++ b/dmd/opover.d @@ -293,6 +293,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { ie = (*ae.arguments)[0].isIntervalExp(); } + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -354,7 +355,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { /* Rewrite op(a[arguments]) as: * op(a.aliasthis[arguments]) @@ -370,13 +371,18 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) } e.e1 = e.e1.expressionSemantic(sc); e.e1 = resolveProperties(sc, e.e1); - if (e.e1.op == EXP.error) - { - return e.e1; - } - AggregateDeclaration ad = isAggregate(e.e1.type); - if (ad) + Type att = null; // first cyclic `alias this` type + while (1) { + if (e.e1.op == EXP.error) + { + return e.e1; + } + + AggregateDeclaration ad = isAggregate(e.e1.type); + if (!ad) + break; + Dsymbol fd = null; /* Rewrite as: * e1.opUnary!(op)() @@ -404,18 +410,20 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) } } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) { /* Rewrite op(e1) as: * op(e1.aliasthis) */ //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); - Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident); - UnaExp ue = cast(UnaExp)e.copy(); - ue.e1 = e1; - result = ue.trySemantic(sc); - return result; + if (auto e1 = resolveAliasThis(sc, e.e1, true)) + { + e.e1 = e1; + continue; + } + break; } + break; } return result; } @@ -433,6 +441,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) ie = (*ae.arguments)[0].isIntervalExp(); } Expression result; + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -526,7 +535,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { //printf("att arr e1 = %s\n", this.e1.type.toChars()); /* Rewrite op(a[arguments]) as: @@ -547,7 +556,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) * This is mostly the same as UnaryExp::op_overload(), but has * a different rewrite. */ - Expression visitCast(CastExp e) + Expression visitCast(CastExp e, Type att = null) { //printf("CastExp::op_overload() (%s)\n", e.toChars()); Expression result; @@ -578,7 +587,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(e.att1, e.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) { /* Rewrite op(e1) as: * op(e1.aliasthis) @@ -587,7 +596,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { result = e.copy(); (cast(UnaExp)result).e1 = e1; - result = result.op_overload(sc); + result = visitCast(result.isCastExp(), att); return result; } } @@ -997,7 +1006,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return null; import dmd.clone : needOpEquals; - if (!global.params.fieldwise && !needOpEquals(sd)) + if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd)) { // Use bitwise equality. auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; @@ -1016,12 +1025,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) * also compare the parent class's equality. Otherwise, compares * the identity of parent context through void*. */ - if (e.att1 && t1.equivalent(e.att1)) return null; - if (e.att2 && t2.equivalent(e.att2)) return null; - e = e.copy().isEqualExp(); - if (!e.att1) e.att1 = t1; - if (!e.att2) e.att2 = t2; e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); @@ -1029,18 +1033,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) sc2.flags |= SCOPE.noaccesscheck; Expression r = e.expressionSemantic(sc2); sc2.pop(); - - /* https://issues.dlang.org/show_bug.cgi?id=15292 - * if the rewrite result is same with the original, - * the equality is unresolvable because it has recursive definition. - */ - if (r.op == e.op && - r.isEqualExp().e1.type.toBasetype() == t1) - { - e.error("cannot compare `%s` because its auto generated member-wise equality has recursive definition", - t1.toChars()); - return ErrorExp.get(); - } return r; } @@ -1071,8 +1063,6 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) auto ex1 = (*tup1.exps)[i]; auto ex2 = (*tup2.exps)[i]; auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); - eeq.att1 = e.att1; - eeq.att2 = e.att2; if (!result) result = eeq; @@ -1114,6 +1104,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) { ie = (*ae.arguments)[0].isIntervalExp(); } + Type att = null; // first cyclic `alias this` type while (true) { if (ae.e1.op == EXP.error) @@ -1185,7 +1176,7 @@ Expression op_overload(Expression e, Scope* sc, EXP* pop = null) return result; } // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(ae.att1, ae.e1.type)) + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { /* Rewrite (a[arguments] op= e2) as: * a.aliasthis[arguments] op= e2 diff --git a/dmd/optimize.d b/dmd/optimize.d index b5d32b2932d..61c385fc061 100644 --- a/dmd/optimize.d +++ b/dmd/optimize.d @@ -371,7 +371,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) { if (e.stageflags & stageOptimize) return; - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageOptimize; if (e.elements) { diff --git a/dmd/parse.d b/dmd/parse.d index 36a76f50da2..68a25064fc0 100644 --- a/dmd/parse.d +++ b/dmd/parse.d @@ -15,14 +15,13 @@ module dmd.parse; import core.stdc.stdio; import core.stdc.string; + import dmd.astenums; import dmd.errorsink; -import dmd.globals; import dmd.id; import dmd.identifier; import dmd.lexer; import dmd.location; -import dmd.errors; import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; @@ -30,6 +29,8 @@ import dmd.root.rootobject; import dmd.root.string; import dmd.tokens; +alias CompileEnv = dmd.lexer.CompileEnv; + /*********************************************************** */ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer @@ -45,49 +46,38 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer Loc endloc; // set to location of last right curly int inBrackets; // inside [] of array index or slice Loc lookingForElse; // location of lonely if looking for an else + bool doUnittests; // parse unittest blocks } + bool transitionIn = false; /// `-transition=in` is active, `in` parameters are listed + /********************* * Use this constructor for string mixins. * Input: - * loc location in source file of mixin + * loc = location in source file of mixin */ extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment, - ErrorSink errorSink) scope + ErrorSink errorSink, const CompileEnv* compileEnv, const bool doUnittests) scope { - super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, - errorSink, - global.vendor, global.versionNumber()); - - //printf("Parser::Parser()\n"); + //printf("Parser::Parser()1 %d\n", doUnittests); + this(_module, input, doDocComment, errorSink, compileEnv, doUnittests); scanloc = loc; - - if (!writeMixin(input, scanloc) && loc.filename) - { - /* Create a pseudo-filename for the mixin string, as it may not even exist - * in the source file. - */ - auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1; - char* filename = cast(char*)mem.xmalloc(len); - snprintf(filename, len, "%s-mixin-%d", loc.filename, cast(int)loc.linnum); - scanloc.filename = filename; - } - - mod = _module; - linkage = LINK.d; - //nextToken(); // start up the scanner } - extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink) scope + /************************************************** + * Main Parser constructor. + */ + extern (D) this(AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, + const CompileEnv* compileEnv, const bool doUnittests) scope { super(_module ? _module.srcfile.toChars() : null, input.ptr, 0, input.length, doDocComment, false, errorSink, - global.vendor, global.versionNumber()); + compileEnv); - //printf("Parser::Parser()\n"); - mod = _module; - linkage = LINK.d; - //nextToken(); // start up the scanner + //printf("Parser::Parser()2 %d\n", doUnittests); + this.mod = _module; + this.linkage = LINK.d; + this.doUnittests = doUnittests; } /++ @@ -335,6 +325,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer linkage = linksave; Loc startloc; + Loc scdLoc; switch (token.value) { @@ -381,7 +372,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); auto exps = parseArguments(); check(TOK.semicolon); - s = new AST.CompileDeclaration(loc, exps); + s = new AST.MixinDeclaration(loc, exps); break; } case TOK.template_: @@ -497,7 +488,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * template instantiations in these unittests as candidates for * further codegen culling. */ - if (mod.isRoot() && (global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput)) + // The isRoot check is here because it can change after parsing begins (see dmodule.d) + if (doUnittests && mod.isRoot()) { linkage = LINK.d; // unittests have D linkage s = parseUnitTest(pAttrs); @@ -696,6 +688,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } Lstc: pAttrs.storageClass = appendStorageClass(pAttrs.storageClass, stc); + scdLoc = token.loc; nextToken(); Lautodecl: @@ -748,7 +741,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto stc2 = getStorageClass!AST(pAttrs); if (stc2 != STC.undefined_) { - s = new AST.StorageClassDeclaration(stc2, a); + s = new AST.StorageClassDeclaration(scdLoc, stc2, a); } if (pAttrs.udas) { @@ -1219,19 +1212,20 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return orig | added; } - const Redundant = (STC.const_ | STC.scope_ | - (global.params.previewIn ? STC.ref_ : 0)); + const Redundant = (STC.const_ | STC.scope_ | STC.ref_); orig |= added; if ((orig & STC.in_) && (added & Redundant)) { if (added & STC.const_) error("attribute `const` is redundant with previously-applied `in`"); - else if (global.params.previewIn) + else if (compileEnv.previewIn) { error("attribute `%s` is redundant with previously-applied `in`", (orig & STC.scope_) ? "scope".ptr : "ref".ptr); } + else if (added & STC.ref_) + deprecation("using `in ref` is deprecated, use `-preview=in` and `in` instead"); else error("attribute `scope` cannot be applied with `in`, use `-preview=in` instead"); return orig; @@ -1241,13 +1235,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { if (orig & STC.const_) error("attribute `in` cannot be added after `const`: remove `const`"); - else if (global.params.previewIn) + else if (compileEnv.previewIn) { // Windows `printf` does not support `%1$s` const(char*) stc_str = (orig & STC.scope_) ? "scope".ptr : "ref".ptr; error(token.loc, "attribute `in` cannot be added after `%s`: remove `%s`", stc_str, stc_str); } + else if (orig & STC.ref_) + deprecation("using `ref in` is deprecated, use `-preview=in` and `in` instead"); else error("attribute `in` cannot be added after `scope`: remove `scope` and use `-preview=in`"); return orig; @@ -1302,12 +1298,38 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return 0; } + AST.Expression templateArgToExp(RootObject o, const ref Loc loc) + { + switch (o.dyncast) + { + case DYNCAST.expression: + return cast(AST.Expression) o; + case DYNCAST.type: + return new AST.TypeExp(loc, cast(AST.Type)o); + default: + assert(0); + } + } + if (token.value == TOK.leftParenthesis) { // Multi-UDAs ( `@( ArgumentList )`) form, concatenate with existing if (peekNext() == TOK.rightParenthesis) error("empty attribute list is not allowed"); - udas = AST.UserAttributeDeclaration.concat(udas, parseArguments()); + + if (udas is null) + udas = new AST.Expressions(); + auto args = parseTemplateArgumentList(); + foreach (arg; *args) + udas.push(templateArgToExp(arg, token.loc)); + return 0; + } + + if (auto o = parseTemplateSingleArgument()) + { + if (udas is null) + udas = new AST.Expressions(); + udas.push(templateArgToExp(o, token.loc)); return 0; } @@ -1764,7 +1786,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer else { // ident!template_argument - tiargs = parseTemplateSingleArgument(); + RootObject o = parseTemplateSingleArgument(); + if (!o) + { + error("template argument expected following `!`"); + } + else + { + tiargs = new AST.Objects(); + tiargs.push(o); + } } if (token.value == TOK.not) { @@ -1830,11 +1861,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * foo!arg * Input: * current token is the arg + * Returns: An AST.Type, AST.Expression, or `null` on error */ - private AST.Objects* parseTemplateSingleArgument() + private RootObject parseTemplateSingleArgument() { //printf("parseTemplateSingleArgument()\n"); - auto tiargs = new AST.Objects(); AST.Type ta; switch (token.value) { @@ -1942,9 +1973,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer ta = AST.Type.tdchar; goto LabelX; LabelX: - tiargs.push(ta); nextToken(); - break; + return ta; case TOK.int32Literal: case TOK.uns32Literal: @@ -1974,15 +2004,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.this_: { // Template argument is an expression - AST.Expression ea = parsePrimaryExp(); - tiargs.push(ea); - break; + return parsePrimaryExp(); } default: - error("template argument expected following `!`"); - break; + return null; } - return tiargs; } /********************************** @@ -2694,7 +2720,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer /** Extract unittest body as a string. Must be done eagerly since memory will be released by the lexer before doc gen. */ char* docline = null; - if (global.params.ddoc.doOutput && endPtr > begPtr) + if (compileEnv.ddocOutput && endPtr > begPtr) { /* Remove trailing whitespaces */ for (const(char)* p = endPtr - 1; begPtr <= p && (*p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'); --p) @@ -2849,8 +2875,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // Don't call nextToken again. } case TOK.in_: - if (global.params.vin) - message(scanloc, "Usage of 'in' on parameter"); + if (transitionIn) + eSink.message(scanloc, "Usage of 'in' on parameter"); stc = STC.in_; goto L2; @@ -4610,8 +4636,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Dsymbol s; if (width) { - if (!global.params.bitfields) - error("use -preview=bitfields for bitfield support"); if (_init) error("initializer not allowed for bit-field declaration"); if (storage_class) @@ -5144,7 +5168,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.goesTo: if (requireDo) error("missing `do { ... }` after `in` or `out`"); - if (!global.params.shortenedMethods) + if (!compileEnv.shortenedMethods) error("=> shortened method not enabled, compile with compiler switch `-preview=shortenedMethods`"); const returnloc = token.loc; nextToken(); @@ -5156,7 +5180,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.leftCurly: if (requireDo) error("missing `do { ... }` after `in` or `out`"); - f.fbody = parseStatement(ParseStatementFlags.semi); + f.fbody = parseStatement(0); f.endloc = endloc; break; @@ -5801,7 +5825,11 @@ LagainStc: if (token.value != TOK.semicolon && peek(&token).value == TOK.semicolon) error("found `%s` when expecting `;` following statement", token.toChars()); else - check(TOK.semicolon, "statement"); + { + if (token.value != TOK.semicolon) + error("found `%s` when expecting `;` following statement `%s` on line %s", token.toChars(), exp.toChars(), exp.loc.toChars()); + nextToken(); + } } s = new AST.ExpStatement(loc, exp); break; @@ -5959,7 +5987,7 @@ LagainStc: if (e.op == EXP.mixin_) { AST.MixinExp cpe = cast(AST.MixinExp)e; - s = new AST.CompileStatement(loc, cpe.exps); + s = new AST.MixinStatement(loc, cpe.exps); } else { @@ -5992,7 +6020,7 @@ LagainStc: auto statements = new AST.Statements(); while (token.value != TOK.rightCurly && token.value != TOK.endOfFile) { - statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(parseStatement(ParseStatementFlags.curlyScope)); } if (endPtr) *endPtr = token.ptr; @@ -6025,10 +6053,7 @@ LagainStc: case TOK.semicolon: if (!(flags & ParseStatementFlags.semiOk)) { - if (flags & ParseStatementFlags.semi) - deprecation("use `{ }` for an empty statement, not `;`"); - else - error("use `{ }` for an empty statement, not `;`"); + error("use `{ }` for an empty statement, not `;`"); } nextToken(); s = new AST.ExpStatement(loc, cast(AST.Expression)null); @@ -6245,7 +6270,7 @@ LagainStc: _body = null; } else - _body = parseStatement(ParseStatementFlags.semi); + _body = parseStatement(0); s = new AST.PragmaStatement(loc, ident, args, _body); break; } @@ -6298,7 +6323,7 @@ LagainStc: auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - auto cur = parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope); + auto cur = parseStatement(ParseStatementFlags.curlyScope); statements.push(cur); // https://issues.dlang.org/show_bug.cgi?id=21739 @@ -6313,7 +6338,7 @@ LagainStc: } else { - s = parseStatement(ParseStatementFlags.semi); + s = parseStatement(0); } s = new AST.ScopeStatement(loc, s, token.loc); @@ -6342,12 +6367,12 @@ LagainStc: auto statements = new AST.Statements(); while (token.value != TOK.case_ && token.value != TOK.default_ && token.value != TOK.endOfFile && token.value != TOK.rightCurly) { - statements.push(parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope)); + statements.push(parseStatement(ParseStatementFlags.curlyScope)); } s = new AST.CompoundStatement(loc, statements); } else - s = parseStatement(ParseStatementFlags.semi); + s = parseStatement(0); s = new AST.ScopeStatement(loc, s, token.loc); s = new AST.DefaultStatement(loc, s); break; @@ -6891,6 +6916,15 @@ LagainStc: /******************** * Parse inline assembler block. + * Enters with token on the `asm`. + * https://dlang.org/spec/iasm.html + * + * AsmStatement: + * asm FunctionAttributes(opt) { AsmInstructionListopt } + * AsmInstructionList: + * AsmInstruction ; + * AsmInstruction ; AsmInstruction + * * Returns: * inline assembler block as a Statement */ @@ -6905,7 +6939,7 @@ LagainStc: Loc labelloc; nextToken(); - StorageClass stc = parsePostfix(STC.undefined_, null); + StorageClass stc = parsePostfix(STC.undefined_, null); // optional FunctionAttributes if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild)) error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks"); @@ -9181,7 +9215,7 @@ LagainStc: void checkRequiredParens() { if (e.op == EXP.question && !e.parens) - dmd.errors.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", + eSink.error(e.loc, "`%s` must be surrounded by parentheses when next to operator `%s`", e.toChars(), Token.toChars(token.value)); } @@ -9498,7 +9532,6 @@ immutable PREC[EXP.max + 1] precedence = EXP.error : PREC.expr, EXP.objcClassReference : PREC.expr, // Objective-C class reference, same as EXP.type - EXP.typeof_ : PREC.primary, EXP.mixin_ : PREC.primary, EXP.import_ : PREC.primary, @@ -9538,7 +9571,6 @@ immutable PREC[EXP.max + 1] precedence = EXP.remove : PREC.primary, EXP.tuple : PREC.primary, EXP.traits : PREC.primary, - EXP.default_ : PREC.primary, EXP.overloadSet : PREC.primary, EXP.void_ : PREC.primary, EXP.vectorArray : PREC.primary, @@ -9637,7 +9669,6 @@ immutable PREC[EXP.max + 1] precedence = enum ParseStatementFlags : int { - semi = 1, // empty ';' statements are allowed, but deprecated scope_ = 2, // start a new scope curly = 4, // { } statement is required curlyScope = 8, // { } starts a new scope @@ -9708,52 +9739,3 @@ private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs) } return stc; } - -/************************************** - * dump mixin expansion to file for better debugging - */ -private bool writeMixin(const(char)[] s, ref Loc loc) -{ - if (!global.params.mixinOut.doOutput) - return false; - - OutBuffer* ob = global.params.mixinOut.buffer; - - ob.writestring("// expansion at "); - ob.writestring(loc.toChars()); - ob.writenl(); - - global.params.mixinOut.bufferLines++; - - loc = Loc(global.params.mixinOut.name.ptr, global.params.mixinOut.bufferLines + 1, loc.charnum); - - // write by line to create consistent line endings - size_t lastpos = 0; - for (size_t i = 0; i < s.length; ++i) - { - // detect LF and CRLF - const c = s[i]; - if (c == '\n' || (c == '\r' && i+1 < s.length && s[i+1] == '\n')) - { - ob.writestring(s[lastpos .. i]); - ob.writenl(); - global.params.mixinOut.bufferLines++; - if (c == '\r') - ++i; - lastpos = i + 1; - } - } - - if(lastpos < s.length) - ob.writestring(s[lastpos .. $]); - - if (s.length == 0 || s[$-1] != '\n') - { - ob.writenl(); // ensure empty line after expansion - global.params.mixinOut.bufferLines++; - } - ob.writenl(); - global.params.mixinOut.bufferLines++; - - return true; -} diff --git a/dmd/parsetimevisitor.d b/dmd/parsetimevisitor.d index 387b28c1532..a4a9434334e 100644 --- a/dmd/parsetimevisitor.d +++ b/dmd/parsetimevisitor.d @@ -66,7 +66,7 @@ public: void visit(AST.SharedStaticDtorDeclaration s) { visit(cast(AST.StaticDtorDeclaration)s); } // AttribDeclarations - void visit(AST.CompileDeclaration s) { visit(cast(AST.AttribDeclaration)s); } + void visit(AST.MixinDeclaration s) { visit(cast(AST.AttribDeclaration)s); } void visit(AST.UserAttributeDeclaration s) { visit(cast(AST.AttribDeclaration)s); } void visit(AST.LinkDeclaration s) { visit(cast(AST.AttribDeclaration)s); } void visit(AST.AnonDeclaration s) { visit(cast(AST.AttribDeclaration)s); } @@ -99,7 +99,7 @@ public: void visit(AST.ReturnStatement s) { visit(cast(AST.Statement)s); } void visit(AST.LabelStatement s) { visit(cast(AST.Statement)s); } void visit(AST.StaticAssertStatement s) { visit(cast(AST.Statement)s); } - void visit(AST.CompileStatement s) { visit(cast(AST.Statement)s); } + void visit(AST.MixinStatement s) { visit(cast(AST.Statement)s); } void visit(AST.WhileStatement s) { visit(cast(AST.Statement)s); } void visit(AST.ForStatement s) { visit(cast(AST.Statement)s); } void visit(AST.DoStatement s) { visit(cast(AST.Statement)s); } diff --git a/dmd/printast.d b/dmd/printast.d index d85105d6f20..8c0109524f7 100644 --- a/dmd/printast.d +++ b/dmd/printast.d @@ -219,6 +219,25 @@ extern (C++) final class PrintASTVisitor : Visitor printAST(e.value, indent + 2); } + override void visit(ArrayLiteralExp e) + { + visit(cast(Expression)e); + printIndent(indent + 2); + printf(".basis : %s\n", e.basis ? e.basis.toChars() : ""); + if (e.elements) + { + printIndent(indent + 2); + printf("["); + foreach (i, element; (*e.elements)[]) + { + if (i) + printf(", "); + printf("%s", element.toChars()); + } + printf("]\n"); + } + } + static void printIndent(int indent) { foreach (i; 0 .. indent) diff --git a/dmd/root/dcompat.h b/dmd/root/dcompat.h index 552922020b0..fa51370e3fb 100644 --- a/dmd/root/dcompat.h +++ b/dmd/root/dcompat.h @@ -36,7 +36,7 @@ struct DString : public DArray }; /// Corresponding C++ type that maps to D size_t -#if __APPLE__ && __i386__ +#if __APPLE__ && (__i386__ || __ppc__) // size_t is 'unsigned long', which makes it mangle differently than D's 'uint' typedef unsigned d_size_t; #elif MARS && DMD_VERSION >= 2079 && DMD_VERSION <= 2081 && \ @@ -51,3 +51,11 @@ typedef unsigned d_size_t; #else typedef size_t d_size_t; #endif + +/// Corresponding C++ type that maps to D bool +#if __APPLE__ && __ppc__ +// bool is defined as an 'int', which does not match same size as D +typedef uint8_t d_bool; +#else +typedef bool d_bool; +#endif diff --git a/dmd/root/optional.h b/dmd/root/optional.h index cc2ee79edeb..353332c2199 100644 --- a/dmd/root/optional.h +++ b/dmd/root/optional.h @@ -11,6 +11,8 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/optional.h */ +#include "dcompat.h" // for d_bool + /// Optional type that is either `empty` or contains a value of type `T` template struct Optional final @@ -20,7 +22,7 @@ struct Optional final T value; /** whether `value` is set **/ - bool present; + d_bool present; public: /** Creates an `Optional` with the given value **/ diff --git a/dmd/root/port.d b/dmd/root/port.d index 9d16e3db1af..a80d751aee2 100644 --- a/dmd/root/port.d +++ b/dmd/root/port.d @@ -70,7 +70,21 @@ extern (C++) struct Port return t; } - + private extern (D) static bool resultOutOfRange(FloatingType)(const FloatingType x, const int errnoValue) + { + import core.stdc.math : HUGE_VAL, HUGE_VALF; + static if (is(FloatingType == double)) + const FloatingType hugeVal = HUGE_VAL; + else static if (is(FloatingType == float)) + const FloatingType hugeVal = HUGE_VALF; + else static assert(0, "This function does not support " ~ FloatingType); + + if (errnoValue == ERANGE) + { + return x == hugeVal || x == 0.0f; + } + return false; + } static bool isFloat32LiteralOutOfRange(scope const(char)* s) { version (IN_LLVM) @@ -85,6 +99,8 @@ extern (C++) struct Port { auto save = __locale_decpoint; __locale_decpoint = "."; + scope(exit) + __locale_decpoint = save; } version (CRuntime_Microsoft) { @@ -92,13 +108,13 @@ extern (C++) struct Port int res = _atoflt(&r, s); if (res == _UNDERFLOW || res == _OVERFLOW) errno = ERANGE; + return errno == ERANGE; } else { - strtof(s, null); + const result = strtof(s, null); + return resultOutOfRange(result, errno); } - version (CRuntime_DigitalMars) __locale_decpoint = save; - return errno == ERANGE; } } @@ -116,6 +132,8 @@ extern (C++) struct Port { auto save = __locale_decpoint; __locale_decpoint = "."; + scope(exit) + __locale_decpoint = save; } version (CRuntime_Microsoft) { @@ -123,13 +141,13 @@ extern (C++) struct Port int res = _atodbl(&r, s); if (res == _UNDERFLOW || res == _OVERFLOW) errno = ERANGE; + return errno == ERANGE; } else { - strtod(s, null); + const result = strtod(s, null); + return resultOutOfRange(result, errno); } - version (CRuntime_DigitalMars) __locale_decpoint = save; - return errno == ERANGE; } } diff --git a/dmd/scope.h b/dmd/scope.h index 1b6ee3584ee..a04239b89db 100644 --- a/dmd/scope.h +++ b/dmd/scope.h @@ -81,8 +81,8 @@ struct Scope ForeachStatement *fes; // if nested function for ForeachStatement, this is it Scope *callsc; // used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__ Dsymbol *inunion; // !=null if processing members of a union - bool nofree; // true if shouldn't free it - bool inLoop; // true if inside a loop (where constructor calls aren't allowed) + d_bool nofree; // true if shouldn't free it + d_bool inLoop; // true if inside a loop (where constructor calls aren't allowed) int intypeof; // in typeof(exp) VarDeclaration *lastVar; // Previous symbol used to prevent goto-skips-init diff --git a/dmd/semantic2.d b/dmd/semantic2.d index c55674b3d73..7994048856c 100644 --- a/dmd/semantic2.d +++ b/dmd/semantic2.d @@ -669,6 +669,13 @@ private extern(C++) final class Semantic2Visitor : Visitor { foreach (base; cd.interfaces) { + // https://issues.dlang.org/show_bug.cgi?id=22729 + // interfaces that have errors or that + // inherit from interfaces that have errors + // might have an uninitialized vtable + if (!base.sym.vtbl.length) + continue; + // first entry is ClassInfo reference auto methods = base.sym.vtbl[base.sym.vtblOffset .. $]; diff --git a/dmd/semantic3.d b/dmd/semantic3.d index 19d53507db2..2025c9c7ab6 100644 --- a/dmd/semantic3.d +++ b/dmd/semantic3.d @@ -477,7 +477,7 @@ private extern(C++) final class Semantic3Visitor : Visitor /* Generate identifier for un-named parameter, * because we need it later on. */ - fparam.ident = id = Identifier.generateId("_param_", i); + fparam.ident = id = Identifier.generateId("__param_", i); stc |= STC.temp; } Type vtype = fparam.type; diff --git a/dmd/sideeffect.d b/dmd/sideeffect.d index 60a74cc2812..3f3e7e6377a 100644 --- a/dmd/sideeffect.d +++ b/dmd/sideeffect.d @@ -185,6 +185,7 @@ private bool lambdaHasSideEffect(Expression e, bool assumeImpureCalls = false) case EXP.delete_: case EXP.new_: case EXP.newAnonymousClass: + case EXP.loweredAssignExp: return true; case EXP.call: { diff --git a/dmd/statement.d b/dmd/statement.d index ba0959692e1..50f1a9a745f 100644 --- a/dmd/statement.d +++ b/dmd/statement.d @@ -379,11 +379,10 @@ version (IN_LLVM) * the downcast statement if it can be downcasted, otherwise `null` */ inout(ErrorStatement) isErrorStatement() { return stmt == STMT.Error ? cast(typeof(return))this : null; } + inout(PeelStatement) isPeelStatement() { return stmt == STMT.Peel ? cast(typeof(return))this : null; } inout(ScopeStatement) isScopeStatement() { return stmt == STMT.Scope ? cast(typeof(return))this : null; } inout(ExpStatement) isExpStatement() { return stmt == STMT.Exp ? cast(typeof(return))this : null; } inout(CompoundStatement) isCompoundStatement() { return stmt == STMT.Compound ? cast(typeof(return))this : null; } - version (IN_LLVM) - inout(CompoundAsmStatement) isCompoundAsmStatement() { return stmt == STMT.CompoundAsm ? cast(typeof(return))this : null; } inout(ReturnStatement) isReturnStatement() { return stmt == STMT.Return ? cast(typeof(return))this : null; } inout(IfStatement) isIfStatement() { return stmt == STMT.If ? cast(typeof(return))this : null; } inout(ConditionalStatement) isConditionalStatement() { return stmt == STMT.Conditional ? cast(typeof(return))this : null; } @@ -396,7 +395,7 @@ version (IN_LLVM) inout(GotoCaseStatement) isGotoCaseStatement() { return stmt == STMT.GotoCase ? cast(typeof(return))this : null; } inout(BreakStatement) isBreakStatement() { return stmt == STMT.Break ? cast(typeof(return))this : null; } inout(DtorExpStatement) isDtorExpStatement() { return stmt == STMT.DtorExp ? cast(typeof(return))this : null; } - inout(CompileStatement) isCompileStatement() { return stmt == STMT.Compile ? cast(typeof(return))this : null; } + inout(MixinStatement) isMixinStatement() { return stmt == STMT.Mixin ? cast(typeof(return))this : null; } inout(ForwardingStatement) isForwardingStatement() { return stmt == STMT.Forwarding ? cast(typeof(return))this : null; } inout(DoStatement) isDoStatement() { return stmt == STMT.Do ? cast(typeof(return))this : null; } inout(WhileStatement) isWhileStatement() { return stmt == STMT.While ? cast(typeof(return))this : null; } @@ -414,6 +413,15 @@ version (IN_LLVM) inout(UnrolledLoopStatement) isUnrolledLoopStatement() { return stmt == STMT.UnrolledLoop ? cast(typeof(return))this : null; } inout(ForeachRangeStatement) isForeachRangeStatement() { return stmt == STMT.ForeachRange ? cast(typeof(return))this : null; } inout(CompoundDeclarationStatement) isCompoundDeclarationStatement() { return stmt == STMT.CompoundDeclaration ? cast(typeof(return))this : null; } + inout(CompoundAsmStatement) isCompoundAsmStatement() { return stmt == STMT.CompoundAsm ? cast(typeof(return))this : null; } + inout(PragmaStatement) isPragmaStatement() { return stmt == STMT.Pragma ? cast(typeof(return))this : null; } + inout(StaticAssertStatement) isStaticAssertStatement() { return stmt == STMT.StaticAssert ? cast(typeof(return))this : null; } + inout(CaseRangeStatement) isCaseRangeStatement() { return stmt == STMT.CaseRange ? cast(typeof(return))this : null; } + inout(SynchronizedStatement) isSynchronizedStatement() { return stmt == STMT.Synchronized ? cast(typeof(return))this : null; } + inout(AsmStatement) isAsmStatement() { return stmt == STMT.Asm ? cast(typeof(return))this : null; } + inout(InlineAsmStatement) isInlineAsmStatement() { return stmt == STMT.InlineAsm ? cast(typeof(return))this : null; } + inout(GccAsmStatement) isGccAsmStatement() { return stmt == STMT.GccAsm ? cast(typeof(return))this : null; } + inout(ImportStatement) isImportStatement() { return stmt == STMT.Import ? cast(typeof(return))this : null; } } /*********************************************************** @@ -526,7 +534,8 @@ extern (C++) final class DtorExpStatement : ExpStatement /*********************************************************** * https://dlang.org/spec/statement.html#mixin-statement */ -extern (C++) final class CompileStatement : Statement +// Note: was called CompileStatement +extern (C++) final class MixinStatement : Statement { Expressions* exps; @@ -539,13 +548,13 @@ extern (C++) final class CompileStatement : Statement extern (D) this(const ref Loc loc, Expressions* exps) { - super(loc, STMT.Compile); + super(loc, STMT.Mixin); this.exps = exps; } - override CompileStatement syntaxCopy() + override MixinStatement syntaxCopy() { - return new CompileStatement(loc, Expression.arraySyntaxCopy(exps)); + return new MixinStatement(loc, Expression.arraySyntaxCopy(exps)); } override void accept(Visitor v) @@ -2168,3 +2177,107 @@ extern (C++) final class ImportStatement : Statement v.visit(this); } } + + +mixin template VisitStatement(Result) +{ + Result VisitStatement(Statement s) + { + final switch (s.stmt) + { + case STMT.Error: mixin(visitStmtCase("Error")); + case STMT.Scope: mixin(visitStmtCase("Scope")); + case STMT.Exp: mixin(visitStmtCase("Exp")); + case STMT.Compound: mixin(visitStmtCase("Compound")); + case STMT.Return: mixin(visitStmtCase("Return")); + case STMT.If: mixin(visitStmtCase("If")); + case STMT.Conditional: mixin(visitStmtCase("Conditional")); + case STMT.StaticForeach: mixin(visitStmtCase("StaticForeach")); + case STMT.Case: mixin(visitStmtCase("Case")); + case STMT.Default: mixin(visitStmtCase("Default")); + case STMT.Label: mixin(visitStmtCase("Label")); + case STMT.Goto: mixin(visitStmtCase("Goto")); + case STMT.GotoDefault: mixin(visitStmtCase("GotoDefault")); + case STMT.GotoCase: mixin(visitStmtCase("GotoCase")); + case STMT.Break: mixin(visitStmtCase("Break")); + case STMT.DtorExp: mixin(visitStmtCase("DtorExp")); + case STMT.Mixin: mixin(visitStmtCase("Mixin")); + case STMT.Forwarding: mixin(visitStmtCase("Forwarding")); + case STMT.Do: mixin(visitStmtCase("Do")); + case STMT.While: mixin(visitStmtCase("While")); + case STMT.For: mixin(visitStmtCase("For")); + case STMT.Foreach: mixin(visitStmtCase("Foreach")); + case STMT.Switch: mixin(visitStmtCase("Switch")); + case STMT.Continue: mixin(visitStmtCase("Continue")); + case STMT.With: mixin(visitStmtCase("With")); + case STMT.TryCatch: mixin(visitStmtCase("TryCatch")); + case STMT.Throw: mixin(visitStmtCase("Throw")); + case STMT.Debug: mixin(visitStmtCase("Debug")); + case STMT.TryFinally: mixin(visitStmtCase("TryFinally")); + case STMT.ScopeGuard: mixin(visitStmtCase("ScopeGuard")); + case STMT.SwitchError: mixin(visitStmtCase("SwitchError")); + case STMT.UnrolledLoop: mixin(visitStmtCase("UnrolledLoop")); + case STMT.ForeachRange: mixin(visitStmtCase("ForeachRange")); + case STMT.CompoundDeclaration: mixin(visitStmtCase("CompoundDeclaration")); + case STMT.Peel: mixin(visitStmtCase("Peel")); + case STMT.CompoundAsm: mixin(visitStmtCase("CompoundAsm")); + case STMT.Pragma: mixin(visitStmtCase("Pragma")); + case STMT.StaticAssert: mixin(visitStmtCase("StaticAssert")); + case STMT.CaseRange: mixin(visitStmtCase("CaseRange")); + case STMT.Synchronized: mixin(visitStmtCase("Synchronized")); + case STMT.Asm: mixin(visitStmtCase("Asm")); + case STMT.InlineAsm: mixin(visitStmtCase("InlineAsm")); + case STMT.GccAsm: mixin(visitStmtCase("GccAsm")); + case STMT.Import: mixin(visitStmtCase("Import")); + } + } +} + +/**************************************** + * CTFE-only helper function for VisitInitializer. + * Params: + * handler = string for the name of the visit handler + * Returns: boilerplate code for a case + */ +pure string visitStmtCase(string handler) +{ + if (__ctfe) + { + return + " + enum isVoid = is(Result == void); + auto sx = s.is"~handler~"Statement(); + static if (__traits(compiles, visit"~handler~"(sx))) + { + static if (isVoid) + { + visit"~handler~"(sx); + return; + } + else + { + if (Result r = visit"~handler~"(sx)) + return r; + return Result.init; + } + } + else static if (__traits(compiles, visitDefaultCase(s))) + { + static if (isVoid) + { + visitDefaultCase(sx); + return; + } + else + { + if (Result r = visitDefaultCase(s)) + return r; + return Result.init; + } + } + else + static assert(0, "~handler~"); + "; + } + assert(0); +} diff --git a/dmd/statement.h b/dmd/statement.h index e844636f9ab..1c96fb5aefd 100644 --- a/dmd/statement.h +++ b/dmd/statement.h @@ -70,7 +70,7 @@ enum STMTerror, STMTpeel, STMTexp, STMTdtorExp, - STMTcompile, + STMTmixin, STMTcompound, STMTcompoundDeclaration, STMTcompoundAsm, STMTunrolledLoop, STMTscope, @@ -156,7 +156,7 @@ class Statement : public ASTNode GotoCaseStatement *isGotoCaseStatement() { return stmt == STMTgotoCase ? (GotoCaseStatement*)this : NULL; } BreakStatement *isBreakStatement() { return stmt == STMTbreak ? (BreakStatement*)this : NULL; } DtorExpStatement *isDtorExpStatement() { return stmt == STMTdtorExp ? (DtorExpStatement*)this : NULL; } - CompileStatement *isCompileStatement() { return stmt == STMTcompile ? (CompileStatement*)this : NULL; } + MixinStatement *isMixinStatement() { return stmt == STMTmixin ? (MixinStatement*)this : NULL; } ForwardingStatement *isForwardingStatement() { return stmt == STMTforwarding ? (ForwardingStatement*)this : NULL; } DoStatement *isDoStatement() { return stmt == STMTdo ? (DoStatement*)this : NULL; } ForStatement *isForStatement() { return stmt == STMTfor ? (ForStatement*)this : NULL; } @@ -219,12 +219,12 @@ class DtorExpStatement final : public ExpStatement void accept(Visitor *v) override { v->visit(this); } }; -class CompileStatement final : public Statement +class MixinStatement final : public Statement { public: Expressions *exps; - CompileStatement *syntaxCopy() override; + MixinStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -450,7 +450,7 @@ class SwitchStatement final : public Statement public: Expression *condition; Statement *_body; - bool isFinal; + d_bool isFinal; DefaultStatement *sdefault; Statement *tryBody; // set to TryCatchStatement or TryFinallyStatement if in _body portion @@ -643,11 +643,11 @@ class Catch final : public RootObject VarDeclaration *var; // set if semantic processing errors - bool errors; + d_bool errors; // was generated by the compiler, // wasn't present in source code - bool internalCatch; + d_bool internalCatch; Catch *syntaxCopy(); }; @@ -659,7 +659,7 @@ class TryFinallyStatement final : public Statement Statement *finalbody; Statement *tryBody; // set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion - bool bodyFallsThru; // true if _body falls through to finally + d_bool bodyFallsThru; // true if _body falls through to finally static TryFinallyStatement *create(const Loc &loc, Statement *body, Statement *finalbody); TryFinallyStatement *syntaxCopy() override; @@ -686,7 +686,7 @@ class ThrowStatement final : public Statement Expression *exp; // was generated by the compiler, // wasn't present in source code - bool internalThrow; + d_bool internalThrow; ThrowStatement *syntaxCopy() override; @@ -711,7 +711,7 @@ class GotoStatement final : public Statement TryFinallyStatement *tf; ScopeGuardStatement *os; VarDeclaration *lastVar; - bool inCtfeBlock; + d_bool inCtfeBlock; GotoStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -728,8 +728,8 @@ class LabelStatement final : public Statement VarDeclaration *lastVar; Statement *gotoTarget; // interpret void* extra; // used by Statement_toIR() - bool breaks; // someone did a 'break ident' - bool inCtfeBlock; + d_bool breaks; // someone did a 'break ident' + d_bool inCtfeBlock; LabelStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -740,8 +740,8 @@ class LabelDsymbol final : public Dsymbol public: LabelStatement *statement; - bool deleted; // set if rewritten to return in foreach delegate - bool iasm; // set if used by inline assembler + d_bool deleted; // set if rewritten to return in foreach delegate + d_bool iasm; // set if used by inline assembler static LabelDsymbol *create(Identifier *ident); LabelDsymbol *isLabel() override; @@ -765,8 +765,8 @@ class InlineAsmStatement final : public AsmStatement code *asmcode; unsigned asmalign; // alignment of this statement unsigned regs; // mask of registers modified (must match regm_t in back end) - bool refparam; // true if function parameter is referenced - bool naked; // true if function is to be naked + d_bool refparam; // true if function parameter is referenced + d_bool naked; // true if function is to be naked #if IN_LLVM // non-zero if this is a branch, contains the target label diff --git a/dmd/statementsem.d b/dmd/statementsem.d index 300b26948cf..ff3500e84f1 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -145,43 +145,35 @@ extern(C++) Statement statementSemantic(Statement s, Scope* sc) version (CallbackAPI) Compiler.onStatementSemanticStart(s, sc); - scope v = new StatementSemanticVisitor(sc); - s.accept(v); + Statement result = statementSemanticVisit(s, sc); version (CallbackAPI) Compiler.onStatementSemanticDone(s, sc); - return v.result; + return result; } -package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor +package (dmd) +Statement statementSemanticVisit(Statement s, Scope* sc) { - alias visit = Visitor.visit; - Statement result; - Scope* sc; - - this(Scope* sc) scope - { - this.sc = sc; - } - private void setError() + void setError() { result = new ErrorStatement(); } - override void visit(Statement s) + void visitDefaultCase(Statement s) { result = s; } - override void visit(ErrorStatement s) + void visitError(ErrorStatement s) { result = s; } - override void visit(PeelStatement s) + void visitPeel(PeelStatement s) { /* "peel" off this wrapper, and don't run semantic() * on the result. @@ -189,7 +181,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s.s; } - override void visit(ExpStatement s) + void visitExp(ExpStatement s) { /* https://dlang.org/spec/statement.html#expression-statement */ @@ -226,12 +218,17 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s; } - override void visit(CompileStatement cs) + void visitDtorExp(DtorExpStatement s) + { + visitExp(s); + } + + void visitMixin(MixinStatement cs) { /* https://dlang.org/spec/statement.html#mixin-statement */ - //printf("CompileStatement::semantic() %s\n", exp.toChars()); + //printf("MixinStatement::semantic() %s\n", exp.toChars()); Statements* a = cs.flatten(sc); if (!a) return; @@ -239,7 +236,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s.statementSemantic(sc); } - override void visit(CompoundStatement cs) + void visitCompound(CompoundStatement cs) { //printf("CompoundStatement::semantic(this = %p, sc = %p)\n", cs, sc); version (none) @@ -431,7 +428,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = cs; } - override void visit(UnrolledLoopStatement uls) + void visitUnrolledLoop(UnrolledLoopStatement uls) { //printf("UnrolledLoopStatement::semantic(this = %p, sc = %p)\n", uls, sc); Scope* scd = sc.push(); @@ -454,7 +451,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = serror ? serror : uls; } - override void visit(ScopeStatement ss) + void visitScope(ScopeStatement ss) { //printf("ScopeStatement::semantic(sc = %p)\n", sc); if (!ss.statement) @@ -501,7 +498,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ss; } - override void visit(ForwardingStatement ss) + void visitForwarding(ForwardingStatement ss) { assert(ss.sym); for (Scope* csc = sc; !ss.sym.parent; csc = csc.enclosing) @@ -517,7 +514,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ss.statement; } - override void visit(WhileStatement ws) + void visitWhile(WhileStatement ws) { /* Rewrite as a for(;condition;) loop * https://dlang.org/spec/statement.html#while-statement @@ -544,7 +541,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = s; } - override void visit(DoStatement ds) + void visitDo(DoStatement ds) { /* https://dlang.org/spec/statement.html#do-statement */ @@ -580,7 +577,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = ds; } - override void visit(ForStatement fs) + void visitFor(ForStatement fs) { /* https://dlang.org/spec/statement.html#for-statement */ @@ -674,7 +671,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor result = fs; } - override void visit(ForeachStatement fs) + void visitForeach(ForeachStatement fs) { /* https://dlang.org/spec/statement.html#foreach-statement */ @@ -1350,7 +1347,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor auto exp = (*exps)[i]; version (none) { - printf("[%d] p = %s %s, exp = %s %s\n", i, + printf("[%lu] p = %s %s, exp = %s %s\n", i, p.type ? p.type.toChars() : "?", p.ident.toChars(), exp.type.toChars(), exp.toChars()); } @@ -1361,7 +1358,11 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor if (ignoreRef) sc &= ~STC.ref_; p.type = p.type.addStorageClass(sc).typeSemantic(loc, sc2); if (!exp.implicitConvTo(p.type)) - return rangeError(); + { + fs.error("cannot implicilty convert range element of type `%s` to variable `%s` of type `%s`", + exp.type.toChars(), p.toChars(), p.type.toChars()); + return retError(); + } auto var = new VarDeclaration(loc, p.type, p.ident, new ExpInitializer(loc, exp)); var.storage_class |= STC.ctfe | STC.ref_ | STC.foreach_; @@ -1396,342 +1397,7 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor } } - private static extern(D) Expression applyOpApply(ForeachStatement fs, Expression flde, - Type tab, Scope* sc2, Dsymbol sapply) - { - version (none) - { - if (global.params.useDIP1000 == FeatureState.enabled) - { - message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`"); - } - (cast(FuncExp)flde).fd.tookAddressOf = 1; - } - else - { - if (global.params.useDIP1000 == FeatureState.enabled) - ++(cast(FuncExp)flde).fd.tookAddressOf; // allocate a closure unless the opApply() uses 'scope' - } - assert(tab.ty == Tstruct || tab.ty == Tclass); - assert(sapply); - /* Call: - * aggr.apply(flde) - */ - Expression ec; - ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident); - ec = new CallExp(fs.loc, ec, flde); - ec = ec.expressionSemantic(sc2); - if (ec.op == EXP.error) - return null; - if (ec.type != Type.tint32) - { - fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); - return null; - } - return ec; - } - - private static extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde, - Type tab, Scope* sc2) - { - Expression ec; - /* Call: - * aggr(flde) - */ - if (fs.aggr.op == EXP.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() && - !(cast(DelegateExp)fs.aggr).func.needThis()) - { - // https://issues.dlang.org/show_bug.cgi?id=3560 - fs.aggr = (cast(DelegateExp)fs.aggr).e1; - } - ec = new CallExp(fs.loc, fs.aggr, flde); - ec = ec.expressionSemantic(sc2); - if (ec.op == EXP.error) - return null; - if (ec.type != Type.tint32) - { - fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); - return null; - } - return ec; - } - - private static extern(D) Expression applyArray(ForeachStatement fs, Expression flde, - Type tab, Scope* sc2, Type tn, Type tnv) - { - Expression ec; - const dim = fs.parameters.length; - const loc = fs.loc; - /* Call: - * _aApply(aggr, flde) - */ - static immutable fntab = - [ - "cc", "cw", "cd", - "wc", "cc", "wd", - "dc", "dw", "dd" - ]; - - const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1; - char[BUFFER_LEN] fdname; - int flag; - - switch (tn.ty) - { - case Tchar: flag = 0; break; - case Twchar: flag = 3; break; - case Tdchar: flag = 6; break; - default: - assert(0); - } - switch (tnv.ty) - { - case Tchar: flag += 0; break; - case Twchar: flag += 1; break; - case Tdchar: flag += 2; break; - default: - assert(0); - } - const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : ""; - int j = snprintf(fdname.ptr, BUFFER_LEN, "_aApply%s%.*s%llu", r, 2, fntab[flag].ptr, cast(ulong)dim); - assert(j < BUFFER_LEN); - - FuncDeclaration fdapply; - TypeDelegate dgty; - auto params = new Parameters(); - params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null)); - auto dgparams = new Parameters(); - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - if (dim == 2) - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); - params.push(new Parameter(0, dgty, null, null, null)); - fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr); - - if (tab.isTypeSArray()) - fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf()); - // paint delegate argument to the type runtime expects - Expression fexp = flde; - if (!dgty.equals(flde.type)) - { - fexp = new CastExp(loc, flde, flde.type); - fexp.type = dgty; - } - ec = new VarExp(Loc.initial, fdapply, false); - ec = new CallExp(loc, ec, fs.aggr, fexp); - ec.type = Type.tint32; // don't run semantic() on ec - return ec; - } - - private static extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab) - { - auto taa = tab.isTypeAArray(); - Expression ec; - const dim = fs.parameters.length; - // Check types - Parameter p = (*fs.parameters)[0]; - bool isRef = (p.storageClass & STC.ref_) != 0; - Type ta = p.type; - if (dim == 2) - { - Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index); - if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta)) - { - fs.error("`foreach`: index must be type `%s`, not `%s`", - ti.toChars(), ta.toChars()); - return null; - } - p = (*fs.parameters)[1]; - isRef = (p.storageClass & STC.ref_) != 0; - ta = p.type; - } - Type taav = taa.nextOf(); - if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta)) - { - fs.error("`foreach`: value must be type `%s`, not `%s`", - taav.toChars(), ta.toChars()); - return null; - } - - /* Call: - * extern(C) int _aaApply(void*, in size_t, int delegate(void*)) - * _aaApply(aggr, keysize, flde) - * - * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*)) - * _aaApply2(aggr, keysize, flde) - */ - __gshared FuncDeclaration* fdapply = [null, null]; - __gshared TypeDelegate* fldeTy = [null, null]; - ubyte i = (dim == 2 ? 1 : 0); - if (!fdapply[i]) - { - auto params = new Parameters(); - params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null)); - params.push(new Parameter(STC.const_, Type.tsize_t, null, null, null)); - auto dgparams = new Parameters(); - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - if (dim == 2) - dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); - fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); - params.push(new Parameter(0, fldeTy[i], null, null, null)); - fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply); - } - - auto exps = new Expressions(); - exps.push(fs.aggr); - auto keysize = taa.index.size(); - if (keysize == SIZE_INVALID) - return null; - assert(keysize < keysize.max - target.ptrsize); - keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1); - // paint delegate argument to the type runtime expects - Expression fexp = flde; - if (!fldeTy[i].equals(flde.type)) - { - fexp = new CastExp(fs.loc, flde, flde.type); - fexp.type = fldeTy[i]; - } - exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t)); - exps.push(fexp); - ec = new VarExp(Loc.initial, fdapply[i], false); - ec = new CallExp(fs.loc, ec, exps); - ec.type = Type.tint32; // don't run semantic() on ec - return ec; - } - - private static extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc) - { - if (!cases.length) - { - // Easy case, a clean exit from the loop - e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899 - return new ExpStatement(loc, e); - } - // Construct a switch statement around the return value - // of the apply function. - Statement s; - auto a = new Statements(); - - // default: break; takes care of cases 0 and 1 - s = new BreakStatement(Loc.initial, null); - s = new DefaultStatement(Loc.initial, s); - a.push(s); - - // cases 2... - foreach (i, c; *cases) - { - s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c); - a.push(s); - } - - s = new CompoundStatement(loc, a); - return new SwitchStatement(loc, e, s, false); - } - /************************************* - * Turn foreach body into the function literal: - * int delegate(ref T param) { body } - * Params: - * sc = context - * fs = ForeachStatement - * tfld = type of function literal to be created (type of opApply() function if any), can be null - * Returns: - * Function literal created, as an expression - * null if error. - */ - static FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld, - /*IN_LLVM*/ bool enforceSizeTIndex) - { - auto params = new Parameters(); - foreach (i, p; *fs.parameters) - { - StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_); - Identifier id; - - p.type = p.type.typeSemantic(fs.loc, sc); - p.type = p.type.addStorageClass(p.storageClass); -version (IN_LLVM) -{ - // Type of parameter may be different; see below - auto para_type = p.type; -} - if (tfld) - { - Parameter prm = tfld.parameterList[i]; - //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars()); - stc = (prm.storageClass & STC.ref_) | (p.storageClass & STC.scope_); - if ((p.storageClass & STC.ref_) != (prm.storageClass & STC.ref_)) - { - if (!(prm.storageClass & STC.ref_)) - { - fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars()); - return null; - } - goto LcopyArg; - } - id = p.ident; // argument copy is not need. - } - else if (p.storageClass & STC.ref_) - { - // default delegate parameters are marked as ref, then - // argument copy is not need. - id = p.ident; - } - else - { - // Make a copy of the ref argument so it isn't - // a reference. - LcopyArg: - id = Identifier.generateId("__applyArg", cast(int)i); - -version (IN_LLVM) -{ - // In case of a foreach loop on an array the index passed - // to the delegate is always of type size_t. The type of - // the parameter must be changed to size_t and a cast to - // the type used must be inserted. Otherwise the index is - // always 0 on a big endian architecture. This fixes - // issue #326. - Initializer ie; - if (fs.parameters.length == 2 && i == 0 && enforceSizeTIndex) - { - para_type = Type.tsize_t; - ie = new ExpInitializer(fs.loc, - new CastExp(fs.loc, - new IdentifierExp(fs.loc, id), p.type)); - } - else - { - ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); - } -} -else -{ - Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); -} - auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie); - v.storage_class |= STC.temp | (stc & STC.scope_); - Statement s = new ExpStatement(fs.loc, v); - fs._body = new CompoundStatement(fs.loc, s, fs._body); - } - params.push(new Parameter(stc, IN_LLVM ? para_type : p.type, id, null, null)); - } - // https://issues.dlang.org/show_bug.cgi?id=13840 - // Throwable nested function inside nothrow function is acceptable. - StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func); - auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc); - fs.cases = new Statements(); - fs.gotos = new ScopeStatements(); - auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs); - fld.fbody = fs._body; - Expression flde = new FuncExp(fs.loc, fld); - flde = flde.expressionSemantic(sc); - fld.tookAddressOf = 0; - if (flde.op == EXP.error) - return null; - return cast(FuncExp)flde; - } - - override void visit(ForeachRangeStatement fs) + void visitForeachRange(ForeachRangeStatement fs) { /* https://dlang.org/spec/statement.html#foreach-range-statement */ @@ -1917,7 +1583,7 @@ else result = s.statementSemantic(sc); } - override void visit(IfStatement ifs) + void visitIf(IfStatement ifs) { /* https://dlang.org/spec/statement.html#IfStatement */ @@ -1990,6 +1656,20 @@ else // Save 'root' of two branches (then and else) at the point where it forks CtorFlow ctorflow_root = scd.ctorflow.clone(); + /* Rewrite `if (!__ctfe) A else B` as `if (__ctfe) B else A` + */ + NotExp notExp; + if (ifs.elsebody && + (notExp = ifs.condition.isNotExp()) !is null && + notExp.e1.isVarExp() && + notExp.e1.isVarExp().var.ident == Id.ctfe) + { + ifs.condition = notExp.e1; + auto sbody = ifs.ifbody; + ifs.ifbody = ifs.elsebody; + ifs.elsebody = sbody; + } + /* Detect `if (__ctfe)` */ if (ifs.isIfCtfeBlock()) @@ -2022,7 +1702,7 @@ else result = ifs; } - override void visit(ConditionalStatement cs) + void visitConditional(ConditionalStatement cs) { //printf("ConditionalStatement::semantic()\n"); @@ -2051,7 +1731,7 @@ else } } - override void visit(PragmaStatement ps) + void visitPragma(PragmaStatement ps) { /* https://dlang.org/spec/statement.html#pragma-statement */ @@ -2167,14 +1847,14 @@ else result = ps._body; } - override void visit(StaticAssertStatement s) + void visitStaticAssert(StaticAssertStatement s) { s.sa.semantic2(sc); if (s.sa.errors) return setError(); } - override void visit(SwitchStatement ss) + void visitSwitch(SwitchStatement ss) { /* https://dlang.org/spec/statement.html#switch-statement */ @@ -2355,6 +2035,11 @@ version (IN_LLVM) { s = new BreakStatement(ss.loc, null); // default for C is `default: break;` } + else if (!sc.needsCodegen()) + { + // something for the interpreter to deal with + s = new ExpStatement(ss.loc, new AssertExp(ss.loc, IntegerExp.literal!0)); + } else if (global.params.useSwitchError == CHECKENABLE.on && global.params.checkAction != CHECKACTION.halt) { @@ -2416,7 +2101,7 @@ version (IN_LLVM) } } - if (!ss.condition.type.isString()) + if (!(ss.condition.type.isString() && sc.needsCodegen())) { sc.pop(); result = ss; @@ -2499,7 +2184,7 @@ version (IN_LLVM) result = ss; } - override void visit(CaseStatement cs) + void visitCase(CaseStatement cs) { SwitchStatement sw = sc.sw; bool errors = false; @@ -2645,7 +2330,7 @@ version (IN_LLVM) result = cs; } - override void visit(CaseRangeStatement crs) + void visitCaseRange(CaseRangeStatement crs) { SwitchStatement sw = sc.sw; if (sw is null) @@ -2728,7 +2413,7 @@ version (IN_LLVM) result = s; } - override void visit(DefaultStatement ds) + void visitDefault(DefaultStatement ds) { //printf("DefaultStatement::semantic()\n"); bool errors = false; @@ -2772,7 +2457,7 @@ version (IN_LLVM) result = ds; } - override void visit(GotoDefaultStatement gds) + void visitGotoDefault(GotoDefaultStatement gds) { /* https://dlang.org/spec/statement.html#goto-statement */ @@ -2797,7 +2482,7 @@ version (IN_LLVM) result = gds; } - override void visit(GotoCaseStatement gcs) + void visitGotoCase(GotoCaseStatement gcs) { /* https://dlang.org/spec/statement.html#goto-statement */ @@ -2826,7 +2511,7 @@ version (IN_LLVM) result = gcs; } - override void visit(ReturnStatement rs) + void visitReturn(ReturnStatement rs) { /* https://dlang.org/spec/statement.html#return-statement */ @@ -3219,7 +2904,7 @@ version (IN_LLVM) result = rs; } - override void visit(BreakStatement bs) + void visitBreak(BreakStatement bs) { /* https://dlang.org/spec/statement.html#break-statement */ @@ -3301,7 +2986,7 @@ version (IN_LLVM) result = bs; } - override void visit(ContinueStatement cs) + void visitContinue(ContinueStatement cs) { /* https://dlang.org/spec/statement.html#continue-statement */ @@ -3392,7 +3077,7 @@ version (IN_LLVM) result = cs; } - override void visit(SynchronizedStatement ss) + void visitSynchronized(SynchronizedStatement ss) { /* https://dlang.org/spec/statement.html#synchronized-statement */ @@ -3514,7 +3199,7 @@ version (IN_LLVM) } } - override void visit(WithStatement ws) + void visitWith(WithStatement ws) { /* https://dlang.org/spec/statement.html#with-statement */ @@ -3549,14 +3234,16 @@ version (IN_LLVM) } else { - Type t = ws.exp.type.toBasetype(); + Type texp = ws.exp.type; + Type t = texp.toBasetype(); Expression olde = ws.exp; if (t.ty == Tpointer) { ws.exp = new PtrExp(ws.loc, ws.exp); ws.exp = ws.exp.expressionSemantic(sc); - t = ws.exp.type.toBasetype(); + texp = ws.exp.type; + t = texp.toBasetype(); } assert(t); @@ -3604,9 +3291,16 @@ version (IN_LLVM) sym.parent = sc.scopesym; sym.endlinnum = ws.endloc.linnum; } + else if (auto tenum = texp.isTypeEnum()) + { + ws.exp = new TypeExp(ws.exp.loc, tenum); + sym = new WithScopeSymbol(ws); + sym.parent = sc.scopesym; + sym.endlinnum = ws.endloc.linnum; + } else { - ws.error("`with` expressions must be aggregate types or pointers to them, not `%s`", olde.type.toChars()); + ws.error("`with` expression types must be enums or aggregates or pointers to them, not `%s`", olde.type.toChars()); return setError(); } } @@ -3629,7 +3323,7 @@ version (IN_LLVM) } // https://dlang.org/spec/statement.html#TryStatement - override void visit(TryCatchStatement tcs) + void visitTryCatch(TryCatchStatement tcs) { //printf("TryCatchStatement.semantic()\n"); @@ -3733,7 +3427,7 @@ version (IN_LLVM) result = tcs; } - override void visit(TryFinallyStatement tfs) + void visitTryFinally(TryFinallyStatement tfs) { //printf("TryFinallyStatement::semantic()\n"); tfs.tryBody = sc.tryBody; // chain on in-flight tryBody @@ -3773,7 +3467,7 @@ version (IN_LLVM) result = tfs; } - override void visit(ScopeGuardStatement oss) + void visitScopeGuard(ScopeGuardStatement oss) { /* https://dlang.org/spec/statement.html#scope-guard-statement */ @@ -3823,7 +3517,7 @@ version (IN_LLVM) result = oss; } - override void visit(ThrowStatement ts) + void visitThrow(ThrowStatement ts) { /* https://dlang.org/spec/statement.html#throw-statement */ @@ -3836,57 +3530,7 @@ version (IN_LLVM) } - /** - * Run semantic on `throw `. - * - * Params: - * loc = location of the `throw` - * exp = value to be thrown - * sc = enclosing scope - * - * Returns: true if the `throw` is valid, or false if an error was found - */ - extern(D) static bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) - { - if (!global.params.useExceptions) - { - loc.error("cannot use `throw` statements with -betterC"); - return false; - } - - if (!ClassDeclaration.throwable) - { - loc.error("cannot use `throw` statements because `object.Throwable` was not declared"); - return false; - } - - if (FuncDeclaration fd = sc.parent.isFuncDeclaration()) - fd.hasReturnExp |= 2; - - if (exp.op == EXP.new_) - { - NewExp ne = cast(NewExp) exp; - ne.thrownew = true; - } - - exp = exp.expressionSemantic(sc); - exp = resolveProperties(sc, exp); - exp = checkGC(sc, exp); - if (exp.op == EXP.error) - return false; - - checkThrowEscape(sc, exp, false); - - ClassDeclaration cd = exp.type.toBasetype().isClassHandle(); - if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null))) - { - loc.error("can only throw class objects derived from `Throwable`, not type `%s`", exp.type.toChars()); - return false; - } - return true; - } - - override void visit(DebugStatement ds) + void visitDebug(DebugStatement ds) { if (ds.statement) { @@ -3898,7 +3542,7 @@ version (IN_LLVM) result = ds.statement; } - override void visit(GotoStatement gs) + void visitGoto(GotoStatement gs) { /* https://dlang.org/spec/statement.html#goto-statement */ @@ -3942,7 +3586,7 @@ version (IN_LLVM) result = gs; } - override void visit(LabelStatement ls) + void visitLabel(LabelStatement ls) { //printf("LabelStatement::semantic()\n"); FuncDeclaration fd = sc.parent.isFuncDeclaration(); @@ -3976,7 +3620,7 @@ version (IN_LLVM) result = ls; } - override void visit(AsmStatement s) + void visitAsm(AsmStatement s) { /* https://dlang.org/spec/statement.html#asm */ @@ -3985,7 +3629,7 @@ version (IN_LLVM) result = asmSemantic(s, sc); } - override void visit(CompoundAsmStatement cas) + void visitCompoundAsm(CompoundAsmStatement cas) { //printf("CompoundAsmStatement()::semantic()\n"); // Apply postfix attributes of the asm block to each statement. @@ -4013,9 +3657,9 @@ version (IN_LLVM) } assert(sc.func); - if (!(cas.stc & STC.pure_) && sc.func.setImpure()) + if (!(cas.stc & STC.pure_) && sc.func.setImpure(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not")) cas.error("`asm` statement is assumed to be impure - mark it with `pure` if it is not"); - if (!(cas.stc & STC.nogc) && sc.func.setGC()) + if (!(cas.stc & STC.nogc) && sc.func.setGC(cas.loc, "`asm` statement in %s `%s` is assumed to use the GC - mark it with `@nogc` if it does not")) cas.error("`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not"); if (!(cas.stc & (STC.trusted | STC.safe))) { @@ -4026,7 +3670,7 @@ version (IN_LLVM) result = cas; } - override void visit(ImportStatement imps) + void visitImport(ImportStatement imps) { /* https://dlang.org/spec/module.html#ImportDeclaration */ @@ -4065,8 +3709,405 @@ version (IN_LLVM) } result = imps; } + + mixin VisitStatement!void visit; + visit.VisitStatement(s); + return result; +} + +/** + * Run semantic on `throw `. + * + * Params: + * loc = location of the `throw` + * exp = value to be thrown + * sc = enclosing scope + * + * Returns: true if the `throw` is valid, or false if an error was found + */ +public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) +{ + if (!global.params.useExceptions) + { + loc.error("cannot use `throw` statements with -betterC"); + return false; + } + + if (!ClassDeclaration.throwable) + { + loc.error("cannot use `throw` statements because `object.Throwable` was not declared"); + return false; + } + + if (FuncDeclaration fd = sc.parent.isFuncDeclaration()) + fd.hasReturnExp |= 2; + + if (exp.op == EXP.new_) + { + NewExp ne = cast(NewExp) exp; + ne.thrownew = true; + } + + exp = exp.expressionSemantic(sc); + exp = resolveProperties(sc, exp); + exp = checkGC(sc, exp); + if (exp.op == EXP.error) + return false; + if (!exp.type.isNaked()) + { + // @@@DEPRECATED_2.112@@@ + // Deprecated in 2.102, change into an error & return false in 2.112 + exp.loc.deprecation("cannot throw object of qualified type `%s`", exp.type.toChars()); + //return false; + } + checkThrowEscape(sc, exp, false); + + ClassDeclaration cd = exp.type.toBasetype().isClassHandle(); + if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null))) + { + loc.error("can only throw class objects derived from `Throwable`, not type `%s`", exp.type.toChars()); + return false; + } + return true; +} + +private extern(D) Expression applyOpApply(ForeachStatement fs, Expression flde, + Type tab, Scope* sc2, Dsymbol sapply) +{ + version (none) + { + if (global.params.useDIP1000 == FeatureState.enabled) + { + message(loc, "To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`"); + } + (cast(FuncExp)flde).fd.tookAddressOf = 1; + } + else + { + if (global.params.useDIP1000 == FeatureState.enabled) + ++(cast(FuncExp)flde).fd.tookAddressOf; // allocate a closure unless the opApply() uses 'scope' + } + assert(tab.ty == Tstruct || tab.ty == Tclass); + assert(sapply); + /* Call: + * aggr.apply(flde) + */ + Expression ec; + ec = new DotIdExp(fs.loc, fs.aggr, sapply.ident); + ec = new CallExp(fs.loc, ec, flde); + ec = ec.expressionSemantic(sc2); + if (ec.op == EXP.error) + return null; + if (ec.type != Type.tint32) + { + fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); + return null; + } + return ec; } +private extern(D) Expression applyDelegate(ForeachStatement fs, Expression flde, + Type tab, Scope* sc2) +{ + Expression ec; + /* Call: + * aggr(flde) + */ + if (fs.aggr.op == EXP.delegate_ && (cast(DelegateExp)fs.aggr).func.isNested() && + !(cast(DelegateExp)fs.aggr).func.needThis()) + { + // https://issues.dlang.org/show_bug.cgi?id=3560 + fs.aggr = (cast(DelegateExp)fs.aggr).e1; + } + ec = new CallExp(fs.loc, fs.aggr, flde); + ec = ec.expressionSemantic(sc2); + if (ec.op == EXP.error) + return null; + if (ec.type != Type.tint32) + { + fs.error("`opApply()` function for `%s` must return an `int`", tab.toChars()); + return null; + } + return ec; +} + +private extern(D) Expression applyArray(ForeachStatement fs, Expression flde, + Type tab, Scope* sc2, Type tn, Type tnv) +{ + Expression ec; + const dim = fs.parameters.length; + const loc = fs.loc; + /* Call: + * _aApply(aggr, flde) + */ + static immutable fntab = + [ + "cc", "cw", "cd", + "wc", "cc", "wd", + "dc", "dw", "dd" + ]; + + const(size_t) BUFFER_LEN = 7 + 1 + 2 + dim.sizeof * 3 + 1; + char[BUFFER_LEN] fdname; + int flag; + + switch (tn.ty) + { + case Tchar: flag = 0; break; + case Twchar: flag = 3; break; + case Tdchar: flag = 6; break; + default: + assert(0); + } + switch (tnv.ty) + { + case Tchar: flag += 0; break; + case Twchar: flag += 1; break; + case Tdchar: flag += 2; break; + default: + assert(0); + } + const(char)* r = (fs.op == TOK.foreach_reverse_) ? "R" : ""; + int j = snprintf(fdname.ptr, BUFFER_LEN, "_aApply%s%.*s%llu", r, 2, fntab[flag].ptr, cast(ulong)dim); + assert(j < BUFFER_LEN); + + FuncDeclaration fdapply; + TypeDelegate dgty; + auto params = new Parameters(); + params.push(new Parameter(STC.in_, tn.arrayOf(), null, null, null)); + auto dgparams = new Parameters(); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + if (dim == 2) + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); + params.push(new Parameter(0, dgty, null, null, null)); + fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr); + + if (tab.isTypeSArray()) + fs.aggr = fs.aggr.castTo(sc2, tn.arrayOf()); + // paint delegate argument to the type runtime expects + Expression fexp = flde; + if (!dgty.equals(flde.type)) + { + fexp = new CastExp(loc, flde, flde.type); + fexp.type = dgty; + } + ec = new VarExp(Loc.initial, fdapply, false); + ec = new CallExp(loc, ec, fs.aggr, fexp); + ec.type = Type.tint32; // don't run semantic() on ec + return ec; +} + +private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression flde, Type tab) +{ + auto taa = tab.isTypeAArray(); + Expression ec; + const dim = fs.parameters.length; + // Check types + Parameter p = (*fs.parameters)[0]; + bool isRef = (p.storageClass & STC.ref_) != 0; + Type ta = p.type; + if (dim == 2) + { + Type ti = (isRef ? taa.index.addMod(MODFlags.const_) : taa.index); + if (isRef ? !ti.constConv(ta) : !ti.implicitConvTo(ta)) + { + fs.error("`foreach`: index must be type `%s`, not `%s`", + ti.toChars(), ta.toChars()); + return null; + } + p = (*fs.parameters)[1]; + isRef = (p.storageClass & STC.ref_) != 0; + ta = p.type; + } + Type taav = taa.nextOf(); + if (isRef ? !taav.constConv(ta) : !taav.implicitConvTo(ta)) + { + fs.error("`foreach`: value must be type `%s`, not `%s`", + taav.toChars(), ta.toChars()); + return null; + } + + /* Call: + * extern(C) int _aaApply(void*, in size_t, int delegate(void*)) + * _aaApply(aggr, keysize, flde) + * + * extern(C) int _aaApply2(void*, in size_t, int delegate(void*, void*)) + * _aaApply2(aggr, keysize, flde) + */ + __gshared FuncDeclaration* fdapply = [null, null]; + __gshared TypeDelegate* fldeTy = [null, null]; + ubyte i = (dim == 2 ? 1 : 0); + if (!fdapply[i]) + { + auto params = new Parameters(); + params.push(new Parameter(0, Type.tvoid.pointerTo(), null, null, null)); + params.push(new Parameter(STC.const_, Type.tsize_t, null, null, null)); + auto dgparams = new Parameters(); + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + if (dim == 2) + dgparams.push(new Parameter(0, Type.tvoidptr, null, null, null)); + fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); + params.push(new Parameter(0, fldeTy[i], null, null, null)); + fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply); + } + + auto exps = new Expressions(); + exps.push(fs.aggr); + auto keysize = taa.index.size(); + if (keysize == SIZE_INVALID) + return null; + assert(keysize < keysize.max - target.ptrsize); + keysize = (keysize + (target.ptrsize - 1)) & ~(target.ptrsize - 1); + // paint delegate argument to the type runtime expects + Expression fexp = flde; + if (!fldeTy[i].equals(flde.type)) + { + fexp = new CastExp(fs.loc, flde, flde.type); + fexp.type = fldeTy[i]; + } + exps.push(new IntegerExp(Loc.initial, keysize, Type.tsize_t)); + exps.push(fexp); + ec = new VarExp(Loc.initial, fdapply[i], false); + ec = new CallExp(fs.loc, ec, exps); + ec.type = Type.tint32; // don't run semantic() on ec + return ec; +} + +private extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc) +{ + if (!cases.length) + { + // Easy case, a clean exit from the loop + e = new CastExp(loc, e, Type.tvoid); // https://issues.dlang.org/show_bug.cgi?id=13899 + return new ExpStatement(loc, e); + } + // Construct a switch statement around the return value + // of the apply function. + Statement s; + auto a = new Statements(); + + // default: break; takes care of cases 0 and 1 + s = new BreakStatement(Loc.initial, null); + s = new DefaultStatement(Loc.initial, s); + a.push(s); + + // cases 2... + foreach (i, c; *cases) + { + s = new CaseStatement(Loc.initial, new IntegerExp(i + 2), c); + a.push(s); + } + + s = new CompoundStatement(loc, a); + return new SwitchStatement(loc, e, s, false); +} + +/************************************* + * Turn foreach body into the function literal: + * int delegate(ref T param) { body } + * Params: + * sc = context + * fs = ForeachStatement + * tfld = type of function literal to be created (type of opApply() function if any), can be null + * Returns: + * Function literal created, as an expression + * null if error. + */ +private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFunction tfld, + /*IN_LLVM*/ bool enforceSizeTIndex) +{ + auto params = new Parameters(); + foreach (i, p; *fs.parameters) + { + StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_); + Identifier id; + + p.type = p.type.typeSemantic(fs.loc, sc); + p.type = p.type.addStorageClass(p.storageClass); +version (IN_LLVM) +{ + // Type of parameter may be different; see below + auto para_type = p.type; +} + if (tfld) + { + Parameter prm = tfld.parameterList[i]; + //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars()); + stc = (prm.storageClass & STC.ref_) | (p.storageClass & STC.scope_); + if ((p.storageClass & STC.ref_) != (prm.storageClass & STC.ref_)) + { + if (!(prm.storageClass & STC.ref_)) + { + fs.error("`foreach`: cannot make `%s` `ref`", p.ident.toChars()); + return null; + } + goto LcopyArg; + } + id = p.ident; // argument copy is not need. + } + else if (p.storageClass & STC.ref_) + { + // default delegate parameters are marked as ref, then + // argument copy is not need. + id = p.ident; + } + else + { + // Make a copy of the ref argument so it isn't + // a reference. + LcopyArg: + id = Identifier.generateId("__applyArg", cast(int)i); + +version (IN_LLVM) +{ + // In case of a foreach loop on an array the index passed + // to the delegate is always of type size_t. The type of + // the parameter must be changed to size_t and a cast to + // the type used must be inserted. Otherwise the index is + // always 0 on a big endian architecture. This fixes + // issue #326. + Initializer ie; + if (fs.parameters.length == 2 && i == 0 && enforceSizeTIndex) + { + para_type = Type.tsize_t; + ie = new ExpInitializer(fs.loc, + new CastExp(fs.loc, + new IdentifierExp(fs.loc, id), p.type)); + } + else + { + ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); + } +} +else +{ + Initializer ie = new ExpInitializer(fs.loc, new IdentifierExp(fs.loc, id)); +} + auto v = new VarDeclaration(fs.loc, p.type, p.ident, ie); + v.storage_class |= STC.temp | (stc & STC.scope_); + Statement s = new ExpStatement(fs.loc, v); + fs._body = new CompoundStatement(fs.loc, s, fs._body); + } + params.push(new Parameter(stc, IN_LLVM ? para_type : p.type, id, null, null)); + } + // https://issues.dlang.org/show_bug.cgi?id=13840 + // Throwable nested function inside nothrow function is acceptable. + StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func); + auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc); + fs.cases = new Statements(); + fs.gotos = new ScopeStatements(); + auto fld = new FuncLiteralDeclaration(fs.loc, fs.endloc, tf, TOK.delegate_, fs); + fld.fbody = fs._body; + Expression flde = new FuncExp(fs.loc, fld); + flde = flde.expressionSemantic(sc); + fld.tookAddressOf = 0; + if (flde.op == EXP.error) + return null; + return cast(FuncExp)flde; +} + + void catchSemantic(Catch c, Scope* sc) { //printf("Catch::semantic(%s)\n", ident.toChars()); @@ -4833,8 +4874,8 @@ private Statements* flatten(Statement statement, Scope* sc) (*a)[0] = ls; return a; - case STMT.Compile: - auto cs = statement.isCompileStatement(); + case STMT.Mixin: + auto cs = statement.isMixinStatement(); OutBuffer buf; @@ -4845,13 +4886,16 @@ private Statements* flatten(Statement statement, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(cs.loc, sc._module, str, false, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + 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.vin; p.nextToken(); auto a = new Statements(); while (p.token.value != TOK.endOfFile) { - Statement s = p.parseStatement(ParseStatementFlags.semi | ParseStatementFlags.curlyScope); + Statement s = p.parseStatement(ParseStatementFlags.curlyScope); if (!s || global.errors != errors) return errorStatements(); a.push(s); diff --git a/dmd/strictvisitor.d b/dmd/strictvisitor.d index 7c2f6ae89e4..82e91c6aa76 100644 --- a/dmd/strictvisitor.d +++ b/dmd/strictvisitor.d @@ -45,7 +45,7 @@ extern(C++) class StrictVisitor(AST) : ParseTimeVisitor!AST override void visit(AST.TemplateDeclaration) { assert(0); } override void visit(AST.TemplateInstance) { assert(0); } override void visit(AST.Nspace) { assert(0); } - override void visit(AST.CompileDeclaration) { assert(0); } + override void visit(AST.MixinDeclaration) { assert(0); } override void visit(AST.UserAttributeDeclaration) { assert(0); } override void visit(AST.LinkDeclaration) { assert(0); } override void visit(AST.AnonDeclaration) { assert(0); } @@ -71,7 +71,7 @@ extern(C++) class StrictVisitor(AST) : ParseTimeVisitor!AST override void visit(AST.ReturnStatement) { assert(0); } override void visit(AST.LabelStatement) { assert(0); } override void visit(AST.StaticAssertStatement) { assert(0); } - override void visit(AST.CompileStatement) { assert(0); } + override void visit(AST.MixinStatement) { assert(0); } override void visit(AST.WhileStatement) { assert(0); } override void visit(AST.ForStatement) { assert(0); } override void visit(AST.DoStatement) { assert(0); } diff --git a/dmd/target.d b/dmd/target.d index e08cfd27a81..bcbe11fd0cd 100644 --- a/dmd/target.d +++ b/dmd/target.d @@ -25,7 +25,7 @@ module dmd.target; -import dmd.globals : Param; +import dmd.globals : Param, CHECKENABLE; version (IN_LLVM) { @@ -91,6 +91,195 @@ ubyte defaultTargetOSMajor() return 0; } +version (IN_LLVM) {} else +{ + +/** + * Add default `version` identifier for dmd, and set the + * target platform in `params`. + * https://dlang.org/spec/version.html#predefined-versions + * + * Needs to be run after all arguments parsing (command line, DFLAGS environment + * variable and config file) in order to add final flags (such as `X86_64` or + * the `CRuntime` used). + * + * Params: + * params = which target to compile for (set by `setTarget()`) + * tgt = target + */ +public +void addDefaultVersionIdentifiers(const ref Param params, const ref Target tgt) +{ + import dmd.cond : VersionCondition; + import dmd.dmdparams : driverParams, PIC; + + VersionCondition.addPredefinedGlobalIdent("DigitalMars"); + VersionCondition.addPredefinedGlobalIdent("LittleEndian"); + VersionCondition.addPredefinedGlobalIdent("D_Version2"); + VersionCondition.addPredefinedGlobalIdent("all"); + + addPredefinedGlobalIdentifiers(tgt); + + if (params.ddoc.doOutput) + VersionCondition.addPredefinedGlobalIdent("D_Ddoc"); + if (params.cov) + VersionCondition.addPredefinedGlobalIdent("D_Coverage"); + if (driverParams.pic != PIC.fixed) + VersionCondition.addPredefinedGlobalIdent(driverParams.pic == PIC.pic ? "D_PIC" : "D_PIE"); + if (params.useUnitTests) + VersionCondition.addPredefinedGlobalIdent("unittest"); + if (params.useAssert == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("assert"); + if (params.useIn == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("D_PreConditions"); + if (params.useOut == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("D_PostConditions"); + if (params.useInvariants == CHECKENABLE.on) + VersionCondition.addPredefinedGlobalIdent("D_Invariants"); + if (params.useArrayBounds == CHECKENABLE.off) + VersionCondition.addPredefinedGlobalIdent("D_NoBoundsChecks"); + if (params.betterC) + { + VersionCondition.addPredefinedGlobalIdent("D_BetterC"); + } + else + { + VersionCondition.addPredefinedGlobalIdent("D_ModuleInfo"); + VersionCondition.addPredefinedGlobalIdent("D_Exceptions"); + VersionCondition.addPredefinedGlobalIdent("D_TypeInfo"); + } + + VersionCondition.addPredefinedGlobalIdent("D_HardFloat"); + + if (params.tracegc) + VersionCondition.addPredefinedGlobalIdent("D_ProfileGC"); + + if (driverParams.optimize) + VersionCondition.addPredefinedGlobalIdent("D_Optimized"); +} + +// /** +// * Add predefined global identifiers that are determied by the target +// */ +private +void addPredefinedGlobalIdentifiers(const ref Target tgt) +{ + import dmd.cond : VersionCondition; + + alias predef = VersionCondition.addPredefinedGlobalIdent; + if (tgt.cpu >= CPU.sse2) + { + predef("D_SIMD"); + if (tgt.cpu >= CPU.avx) + predef("D_AVX"); + if (tgt.cpu >= CPU.avx2) + predef("D_AVX2"); + } + + with (Target) + { + if (tgt.os & OS.Posix) + predef("Posix"); + if (tgt.os & (OS.linux | OS.FreeBSD | OS.OpenBSD | OS.DragonFlyBSD | OS.Solaris)) + predef("ELFv1"); + switch (tgt.os) + { + case OS.none: { predef("FreeStanding"); break; } + case OS.linux: { predef("linux"); break; } + case OS.OpenBSD: { predef("OpenBSD"); break; } + case OS.DragonFlyBSD: { predef("DragonFlyBSD"); break; } + case OS.Solaris: { predef("Solaris"); break; } + case OS.Windows: + { + predef("Windows"); + VersionCondition.addPredefinedGlobalIdent(tgt.is64bit ? "Win64" : "Win32"); + break; + } + case OS.OSX: + { + predef("OSX"); + // For legacy compatibility + predef("darwin"); + break; + } + case OS.FreeBSD: + { + predef("FreeBSD"); + switch (tgt.osMajor) + { + case 10: predef("FreeBSD_10"); break; + case 11: predef("FreeBSD_11"); break; + case 12: predef("FreeBSD_12"); break; + case 13: predef("FreeBSD_13"); break; + default: predef("FreeBSD_11"); break; + } + break; + } + default: assert(0); + } + } + + addCRuntimePredefinedGlobalIdent(tgt.c); + addCppRuntimePredefinedGlobalIdent(tgt.cpp); + + if (tgt.is64bit) + { + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86_64"); + VersionCondition.addPredefinedGlobalIdent("X86_64"); + } + else + { + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm"); //legacy + VersionCondition.addPredefinedGlobalIdent("D_InlineAsm_X86"); + VersionCondition.addPredefinedGlobalIdent("X86"); + } + if (tgt.isLP64) + VersionCondition.addPredefinedGlobalIdent("D_LP64"); + else if (tgt.is64bit) + VersionCondition.addPredefinedGlobalIdent("X32"); +} + +private +void addCRuntimePredefinedGlobalIdent(const ref TargetC c) +{ + import dmd.cond : VersionCondition; + + alias predef = VersionCondition.addPredefinedGlobalIdent; + with (TargetC.Runtime) switch (c.runtime) + { + default: + case Unspecified: return; + case Bionic: return predef("CRuntime_Bionic"); + case DigitalMars: return predef("CRuntime_DigitalMars"); + case Glibc: return predef("CRuntime_Glibc"); + case Microsoft: return predef("CRuntime_Microsoft"); + case Musl: return predef("CRuntime_Musl"); + case Newlib: return predef("CRuntime_Newlib"); + case UClibc: return predef("CRuntime_UClibc"); + case WASI: return predef("CRuntime_WASI"); + } +} + +private +void addCppRuntimePredefinedGlobalIdent(const ref TargetCPP cpp) +{ + import dmd.cond : VersionCondition; + + alias predef = VersionCondition.addPredefinedGlobalIdent; + with (TargetCPP.Runtime) switch (cpp.runtime) + { + default: + case Unspecified: return; + case Clang: return predef("CppRuntime_Clang"); + case DigitalMars: return predef("CppRuntime_DigitalMars"); + case Gcc: return predef("CppRuntime_Gcc"); + case Microsoft: return predef("CppRuntime_Microsoft"); + case Sun: return predef("CppRuntime_Sun"); + } +} + +} // !IN_LLVM + //////////////////////////////////////////////////////////////////////////////// /** * Describes a back-end target. At present it is incomplete, but in the future diff --git a/dmd/target.h b/dmd/target.h index 15d90658e6e..09236391931 100644 --- a/dmd/target.h +++ b/dmd/target.h @@ -98,11 +98,11 @@ struct TargetCPP Microsoft, Sun }; - bool reverseOverloads; // with dmc and cl, overloaded functions are grouped and in reverse order - bool exceptions; // set if catching C++ exceptions is supported - bool twoDtorInVtable; // target C++ ABI puts deleting and non-deleting destructor into vtable - bool splitVBasetable; // set if C++ ABI uses separate tables for virtual functions and virtual bases - bool wrapDtorInExternD; // set if C++ dtors require a D wrapper to be callable from runtime + d_bool reverseOverloads; // with dmc and cl, overloaded functions are grouped and in reverse order + d_bool exceptions; // set if catching C++ exceptions is supported + d_bool twoDtorInVtable; // target C++ ABI puts deleting and non-deleting destructor into vtable + d_bool splitVBasetable; // set if C++ ABI uses separate tables for virtual functions and virtual bases + d_bool wrapDtorInExternD; // set if C++ dtors require a D wrapper to be callable from runtime #if !IN_LLVM Runtime runtime; #endif @@ -118,7 +118,7 @@ struct TargetCPP struct TargetObjC { - bool supported; // set if compiler can interface with Objective-C + d_bool supported; // set if compiler can interface with Objective-C }; struct Target @@ -167,15 +167,15 @@ struct Target DString architectureName; // name of the platform architecture (e.g. X86_64) CPU cpu; // CPU instruction set to target - bool is64bit; // generate 64 bit code for x86_64; true by default for 64 bit dmd - bool isLP64; // pointers are 64 bits + d_bool is64bit; // generate 64 bit code for x86_64; true by default for 64 bit dmd + d_bool isLP64; // pointers are 64 bits // Environmental DString obj_ext; /// extension for object files DString lib_ext; /// extension for static library files DString dll_ext; /// extension for dynamic library files - bool run_noext; /// allow -run sources without extensions - bool omfobj; /// for Win32: write OMF object files instead of COFF + d_bool run_noext; /// allow -run sources without extensions + d_bool omfobj; /// for Win32: write OMF object files instead of COFF template struct FPTypeProperties diff --git a/dmd/template.h b/dmd/template.h index 09db40fd126..2150233ea3b 100644 --- a/dmd/template.h +++ b/dmd/template.h @@ -78,12 +78,12 @@ class TemplateDeclaration final : public ScopeDsymbol Dsymbol *onemember; // if !=NULL then one member of this template - bool literal; // this template declaration is a literal - bool ismixin; // template declaration is only to be used as a mixin - bool isstatic; // this is static template declaration - bool isTrivialAliasSeq; // matches `template AliasSeq(T...) { alias AliasSeq = T; } - bool isTrivialAlias; // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }` - bool deprecated_; // this template declaration is deprecated + d_bool literal; // this template declaration is a literal + d_bool ismixin; // template declaration is only to be used as a mixin + d_bool isstatic; // this is static template declaration + d_bool isTrivialAliasSeq; // matches `template AliasSeq(T...) { alias AliasSeq = T; } + d_bool isTrivialAlias; // matches pattern `template Alias(T) { alias Alias = qualifiers(T); }` + d_bool deprecated_; // this template declaration is deprecated Visibility visibility; TemplatePrevious *previous; // threaded list of previous instantiation attempts on stack @@ -137,7 +137,7 @@ class TemplateParameter : public ASTNode * A dependent template parameter should return MATCHexact in matchArg() * to respect the match level of the corresponding precedent parameter. */ - bool dependent; + d_bool dependent; virtual TemplateTypeParameter *isTemplateTypeParameter(); virtual TemplateValueParameter *isTemplateValueParameter(); diff --git a/dmd/tokens.d b/dmd/tokens.d index aec3a77dee8..352c89ef164 100644 --- a/dmd/tokens.d +++ b/dmd/tokens.d @@ -274,6 +274,7 @@ enum TOK : ubyte __cdecl, __declspec, __stdcall, + __thread, __pragma, __int128, __attribute__, @@ -289,8 +290,6 @@ enum EXP : ubyte cast_, null_, assert_, - true_, - false_, array, call, address, @@ -307,13 +306,10 @@ enum EXP : ubyte dotType, slice, arrayLength, - version_, dollar, template_, dotTemplateDeclaration, declaration, - typeof_, - pragma_, dSymbol, typeid_, uadd, @@ -394,13 +390,11 @@ enum EXP : ubyte int64, float64, complex80, - char_, import_, delegate_, function_, mixin_, in_, - default_, break_, continue_, goto_, @@ -414,7 +408,6 @@ enum EXP : ubyte moduleString, // __MODULE__ functionString, // __FUNCTION__ prettyFunction, // __PRETTY_FUNCTION__ - shared_, pow, powAssign, vector, @@ -424,10 +417,11 @@ enum EXP : ubyte showCtfeContext, objcClassReference, vectorArray, - arrow, // -> compoundLiteral, // ( type-name ) { initializer-list } _Generic, interval, + + loweredAssignExp, } enum FirstCKeyword = TOK.inline; @@ -586,6 +580,7 @@ private immutable TOK[] keywords = TOK.__cdecl, TOK.__declspec, TOK.__stdcall, + TOK.__thread, TOK.__pragma, TOK.__int128, TOK.__attribute__, @@ -617,7 +612,7 @@ static immutable TOK[TOK.max + 1] Ckeywords = union_, unsigned, void_, volatile, while_, asm_, typeof_, _Alignas, _Alignof, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn, _Static_assert, _Thread_local, - _import, __cdecl, __declspec, __stdcall, __pragma, __int128, __attribute__, + _import, __cdecl, __declspec, __stdcall, __thread, __pragma, __int128, __attribute__, _assert ]; foreach (kw; Ckwds) @@ -889,6 +884,7 @@ extern (C++) struct Token TOK.__cdecl : "__cdecl", TOK.__declspec : "__declspec", TOK.__stdcall : "__stdcall", + TOK.__thread : "__thread", TOK.__pragma : "__pragma", TOK.__int128 : "__int128", TOK.__attribute__ : "__attribute__", diff --git a/dmd/tokens.h b/dmd/tokens.h index 87361f327a4..6c1b9792dbb 100644 --- a/dmd/tokens.h +++ b/dmd/tokens.h @@ -283,6 +283,7 @@ enum class TOK : unsigned char cdecl_, declspec, stdcall, + thread, pragma, int128_, attribute__, @@ -299,8 +300,6 @@ enum class EXP : unsigned char cast_, null_, assert_, - true_, - false_, array, call, address, @@ -317,13 +316,10 @@ enum class EXP : unsigned char dotType, slice, arrayLength, - version_, dollar, template_, dotTemplateDeclaration, declaration, - typeof_, - pragma_, dSymbol, typeid_, uadd, @@ -404,13 +400,11 @@ enum class EXP : unsigned char int64, float64, complex80, - char_, import_, delegate_, function_, mixin_, in_, - default_, break_, continue_, goto_, @@ -424,7 +418,6 @@ enum class EXP : unsigned char moduleString, // __MODULE__ functionString, // __FUNCTION__ prettyFunction, // __PRETTY_FUNCTION__ - shared_, pow, powAssign, vector, @@ -434,7 +427,6 @@ enum class EXP : unsigned char showCtfeContext, objcClassReference, vectorArray, - arrow, // -> compoundLiteral, // ( type-name ) { initializer-list } _Generic_, interval, diff --git a/dmd/traits.d b/dmd/traits.d index 989e978b8e9..f5f6283f604 100644 --- a/dmd/traits.d +++ b/dmd/traits.d @@ -118,55 +118,40 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data) data.setDim(cast(size_t)cntdata); data.zero(); - extern (C++) final class PointerBitmapVisitor : Visitor - { - alias visit = Visitor.visit; - public: - extern (D) this(Array!(ulong)* _data, ulong _sz_size_t) scope - { - this.data = _data; - this.sz_size_t = _sz_size_t; - } + ulong offset; + bool error; + void visit(Type t) + { void setpointer(ulong off) { ulong ptroff = off / sz_size_t; (*data)[cast(size_t)(ptroff / (8 * sz_size_t))] |= 1L << (ptroff % (8 * sz_size_t)); } - override void visit(Type t) + void visitType(Type t) { Type tb = t.toBasetype(); if (tb != t) - tb.accept(this); - } - - override void visit(TypeError t) - { - visit(cast(Type)t); + visit(tb); } - override void visit(TypeNext t) + void visitError(TypeError t) { - assert(0); + visitType(t); } - override void visit(TypeBasic t) + void visitBasic(TypeBasic t) { if (t.ty == Tvoid) setpointer(offset); } - override void visit(TypeVector t) + void visitVector(TypeVector t) { } - override void visit(TypeArray t) - { - assert(0); - } - - override void visit(TypeSArray t) + void visitSArray(TypeSArray t) { ulong arrayoff = offset; ulong nextsize = t.next.size(); @@ -176,95 +161,67 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data) for (ulong i = 0; i < dim; i++) { offset = arrayoff + i * nextsize; - t.next.accept(this); + visit(t.next); } offset = arrayoff; } - override void visit(TypeDArray t) + void visitDArray(TypeDArray t) { setpointer(offset + sz_size_t); } // dynamic array is {length,ptr} - override void visit(TypeAArray t) + void visitAArray(TypeAArray t) { setpointer(offset); } - override void visit(TypePointer t) + void visitPointer(TypePointer t) { if (t.nextOf().ty != Tfunction) // don't mark function pointers setpointer(offset); } - override void visit(TypeReference t) + void visitReference(TypeReference t) { setpointer(offset); } - override void visit(TypeClass t) + void visitClass(TypeClass t) { setpointer(offset); } - override void visit(TypeFunction t) + void visitFunction(TypeFunction t) { } - override void visit(TypeDelegate t) + void visitDelegate(TypeDelegate t) { setpointer(offset); } - // delegate is {context, function} - override void visit(TypeQualified t) + void visitEnum(TypeEnum t) { - assert(0); + visitType(t); } - // assume resolved - override void visit(TypeIdentifier t) + void visitTuple(TypeTuple t) { - assert(0); - } - - override void visit(TypeInstance t) - { - assert(0); + visitType(t); } - override void visit(TypeTypeof t) + void visitNull(TypeNull t) { - assert(0); - } - - override void visit(TypeReturn t) - { - assert(0); - } - - override void visit(TypeEnum t) - { - visit(cast(Type)t); - } - - override void visit(TypeTuple t) - { - visit(cast(Type)t); - } - - override void visit(TypeSlice t) - { - assert(0); + // always a null pointer } - override void visit(TypeNull t) + void visitNoreturn(TypeNoreturn t) { - // always a null pointer } - override void visit(TypeStruct t) + void visitStruct(TypeStruct t) { ulong structoff = offset; foreach (v; t.sym.fields) @@ -273,38 +230,43 @@ ulong getTypePointerBitmap(Loc loc, Type t, Array!(ulong)* data) if (v.type.ty == Tclass) setpointer(offset); else - v.type.accept(this); + visit(v.type); } offset = structoff; } + void visitDefaultCase(Type t) + { + //printf("ty = %d\n", t.ty); + assert(0); + } + + mixin VisitType!void visit; + visit.VisitType(t); + } + + if (auto tc = t.isTypeClass()) + { // a "toplevel" class is treated as an instance, while TypeClass fields are treated as references - void visitClass(TypeClass t) + void visitTopLevelClass(TypeClass t) { ulong classoff = offset; // skip vtable-ptr and monitor if (t.sym.baseClass) - visitClass(cast(TypeClass)t.sym.baseClass.type); + visitTopLevelClass(t.sym.baseClass.type.isTypeClass()); foreach (v; t.sym.fields) { offset = classoff + v.offset; - v.type.accept(this); + visit(v.type); } offset = classoff; } - Array!(ulong)* data; - ulong offset; - ulong sz_size_t; - bool error; + visitTopLevelClass(tc); } - - scope PointerBitmapVisitor pbv = new PointerBitmapVisitor(data, sz_size_t); - if (t.ty == Tclass) - pbv.visitClass(cast(TypeClass)t); else - t.accept(pbv); - return pbv.error ? ulong.max : sz; + visit(t); + return error ? ulong.max : sz; } /** @@ -985,15 +947,24 @@ Expression semanticTraits(TraitsExp e, Scope* sc) */ Dsymbol sym = getDsymbol(o); + + if (sym && e.ident == Id.hasMember) + { + if (auto sm = sym.search(e.loc, id)) + return True(); + + // https://issues.dlang.org/show_bug.cgi?id=23951 + if (auto decl = sym.isDeclaration()) + { + ex = typeDotIdExp(e.loc, decl.type, id); + goto doSemantic; + } + } + if (auto t = isType(o)) ex = typeDotIdExp(e.loc, t, id); else if (sym) { - if (e.ident == Id.hasMember) - { - if (auto sm = sym.search(e.loc, id)) - return True(); - } ex = new DsymbolExp(e.loc, sym); ex = new DotIdExp(e.loc, ex, id); } @@ -1004,7 +975,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) e.error("invalid first argument"); return ErrorExp.get(); } - + doSemantic: // ignore symbol visibility and disable access checks for these traits Scope* scx = sc.push(); scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck; @@ -1261,7 +1232,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) // @@@DEPRECATION 2.100.2 if (auto td = s.isTemplateDeclaration()) { - if (td.overnext || td.funcroot) + if (td.overnext || td.overroot) { deprecation(e.loc, "`__traits(getAttributes)` may only be used for individual functions, not overload sets such as: `%s`", td.ident.toChars()); deprecationSupplemental(e.loc, "the result of `__traits(getOverloads)` may be used to select the desired function to extract attributes from"); @@ -1314,6 +1285,19 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return ErrorExp.get(); } + // https://issues.dlang.org/show_bug.cgi?id=19706 + // When getting the attributes of the instance of a + // templated member function semantic tiargs does + // not perform semantic3 on the instance. + // For more information see FuncDeclaration.functionSemantic. + // For getFunctionAttributes it is mandatory to do + // attribute inference. + if (fd && fd.parent && fd.parent.isTemplateInstance) + { + fd.functionSemantic3(); + tf = cast(TypeFunction)fd.type; + } + auto mods = new Expressions(); void addToMods(string str) @@ -1354,6 +1338,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) * "argptr" extern(D) void dstyle(...), use `__argptr` and `__arguments` * "stdarg" extern(C) void cstyle(int, ...), use core.stdc.stdarg * "typesafe" void typesafe(T[] ...) + * "KR" old K+R style */ // get symbol linkage as a string if (dim != 1) @@ -1388,6 +1373,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) case VarArg.variadic: style = (link == LINK.d) ? "argptr" : "stdarg"; break; + case VarArg.KRvariadic: style = "KR"; break; case VarArg.typesafe: style = "typesafe"; break; } auto se = new StringExp(e.loc, style); @@ -2254,65 +2240,65 @@ private void traitNotFound(TraitsExp e) // All possible traits __gshared Identifier*[59] idents = [ + &Id.allMembers, + &Id.child, + &Id.classInstanceAlignment, + &Id.classInstanceSize, + &Id.compiles, + &Id.derivedMembers, + &Id.fullyQualifiedName, + &Id.getAliasThis, + &Id.getAttributes, + &Id.getFunctionAttributes, + &Id.getFunctionVariadicStyle, + &Id.getLinkage, + &Id.getLocation, + &Id.getMember, + &Id.getOverloads, + &Id.getParameterStorageClasses, + &Id.getPointerBitmap, + &Id.getProtection, + &Id.getTargetInfo, + &Id.getUnitTests, + &Id.getVirtualFunctions, + &Id.getVirtualIndex, + &Id.getVirtualMethods, + &Id.getVisibility, + &Id.hasCopyConstructor, + &Id.hasMember, + &Id.hasPostblit, + &Id.identifier, &Id.isAbstractClass, + &Id.isAbstractFunction, &Id.isArithmetic, &Id.isAssociativeArray, - &Id.isDisabled, + &Id.isCopyable, &Id.isDeprecated, - &Id.isFuture, + &Id.isDisabled, &Id.isFinalClass, - &Id.isPOD, - &Id.isNested, + &Id.isFinalFunction, &Id.isFloating, + &Id.isFuture, &Id.isIntegral, - &Id.isScalar, - &Id.isStaticArray, - &Id.isUnsigned, - &Id.isVirtualFunction, - &Id.isVirtualMethod, - &Id.isAbstractFunction, - &Id.isFinalFunction, - &Id.isOverrideFunction, - &Id.isStaticFunction, + &Id.isLazy, &Id.isModule, + &Id.isNested, + &Id.isOut, + &Id.isOverrideFunction, &Id.isPackage, + &Id.isPOD, &Id.isRef, - &Id.isOut, - &Id.isLazy, &Id.isReturnOnStack, - &Id.hasMember, - &Id.identifier, - &Id.fullyQualifiedName, - &Id.getProtection, - &Id.getVisibility, - &Id.parent, - &Id.child, - &Id.getLinkage, - &Id.getMember, - &Id.getOverloads, - &Id.getVirtualFunctions, - &Id.getVirtualMethods, - &Id.classInstanceSize, - &Id.classInstanceAlignment, - &Id.allMembers, - &Id.derivedMembers, &Id.isSame, - &Id.compiles, - &Id.getAliasThis, - &Id.getAttributes, - &Id.getFunctionAttributes, - &Id.getFunctionVariadicStyle, - &Id.getParameterStorageClasses, - &Id.getUnitTests, - &Id.getVirtualIndex, - &Id.getPointerBitmap, + &Id.isScalar, + &Id.isStaticArray, + &Id.isStaticFunction, + &Id.isUnsigned, + &Id.isVirtualFunction, + &Id.isVirtualMethod, &Id.isZeroInit, - &Id.getTargetInfo, - &Id.getLocation, - &Id.hasPostblit, - &Id.hasCopyConstructor, - &Id.isCopyable, &Id.parameters, + &Id.parent, ]; StringTable!(bool)* stringTable = cast(StringTable!(bool)*) &traitsStringTable; diff --git a/dmd/transitivevisitor.d b/dmd/transitivevisitor.d index 5844911bc6a..c58827063d2 100644 --- a/dmd/transitivevisitor.d +++ b/dmd/transitivevisitor.d @@ -44,9 +44,9 @@ package mixin template ParseVisitMethods(AST) } } - override void visit(AST.CompileStatement s) + override void visit(AST.MixinStatement s) { - //printf("Visiting CompileStatement\n"); + //printf("Visiting MixinStatement\n"); visitArgs(s.exps.peekSlice()); } @@ -579,7 +579,7 @@ package mixin template ParseVisitMethods(AST) de.accept(this); } - override void visit(AST.CompileDeclaration d) + override void visit(AST.MixinDeclaration d) { //printf("Visiting compileDeclaration\n"); visitArgs(d.exps.peekSlice()); diff --git a/dmd/typesem.d b/dmd/typesem.d index c668199e5f7..f0decf2a48d 100644 --- a/dmd/typesem.d +++ b/dmd/typesem.d @@ -290,6 +290,8 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb if (!sm) return helper3(); + if (sm.isAliasDeclaration) + sm.checkDeprecated(loc, sc); s = sm.toAlias(); } @@ -456,6 +458,17 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) return t.merge(); } + Type visitComplex(TypeBasic t) + { + if (!(sc.flags & SCOPE.Cfile)) + return visitType(t); + + auto tc = getComplexLibraryType(loc, sc, t.ty); + if (tc.ty == Terror) + return tc; + return tc.addMod(t.mod).merge(); + } + Type visitVector(TypeVector mtype) { const errors = global.errors; @@ -1185,19 +1198,31 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) // -preview=in: Always add `ref` when used with `extern(C++)` functions // Done here to allow passing opaque types with `in` - if (global.params.previewIn && (fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_) + if ((fparam.storageClass & (STC.in_ | STC.ref_)) == STC.in_) { switch (tf.linkage) { case LINK.cpp: - fparam.storageClass |= STC.ref_; + if (global.params.previewIn) + fparam.storageClass |= STC.ref_; break; case LINK.default_, LINK.d: break; default: - .error(loc, "cannot use `in` parameters with `extern(%s)` functions", - linkageToChars(tf.linkage)); - .errorSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars()); + if (global.params.previewIn) + { + .error(loc, "cannot use `in` parameters with `extern(%s)` functions", + linkageToChars(tf.linkage)); + .errorSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars()); + } + else + { + // Note that this deprecation will not trigger on `in ref` / `ref in` + // parameters, however the parser will trigger a deprecation on them. + .deprecation(loc, "using `in` parameters with `extern(%s)` functions is deprecated", + linkageToChars(tf.linkage)); + .deprecationSupplemental(loc, "parameter `%s` declared as `in` here", fparam.toChars()); + } break; } } @@ -1292,28 +1317,6 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) // error(loc, "inout on parameter means inout must be on return type as well (if from D1 code, replace with `ref`)"); } - /* Scope attribute is not necessary if the parameter type does not have pointers - */ - const sr = buildScopeRef(fparam.storageClass); - switch (sr) - { - case ScopeRef.Scope: - case ScopeRef.RefScope: - case ScopeRef.ReturnRef_Scope: - if (!fparam.type.hasPointers()) - fparam.storageClass &= ~STC.scope_; - break; - - case ScopeRef.ReturnScope: - case ScopeRef.Ref_ReturnScope: - if (!fparam.type.hasPointers()) - fparam.storageClass &= ~(STC.return_ | STC.scope_ | STC.returnScope); - break; - - default: - break; - } - // Remove redundant storage classes for type, they are already applied fparam.storageClass &= ~(STC.TYPECTOR); @@ -1786,12 +1789,14 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) case TOK.struct_: auto sd = new StructDeclaration(mtype.loc, mtype.id, false); + sd.alignment = mtype.packalign; declare(sd); mtype.resolved = visitStruct(new TypeStruct(sd)); break; case TOK.union_: auto ud = new UnionDeclaration(mtype.loc, mtype.id); + ud.alignment = mtype.packalign; declare(ud); mtype.resolved = visitStruct(new TypeStruct(ud)); break; @@ -1928,6 +1933,9 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) switch (type.ty) { default: return visitType(type); + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: return visitComplex(type.isTypeBasic()); case Tvector: return visitVector(type.isTypeVector()); case Tsarray: return visitSArray(type.isTypeSArray()); case Tarray: return visitDArray(type.isTypeDArray()); @@ -2697,7 +2705,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type { void semanticOnMixin(Dsymbol member) { - if (auto compileDecl = member.isCompileDeclaration()) + if (auto compileDecl = member.isMixinDeclaration()) compileDecl.dsymbolSemantic(sc); else if (auto mixinTempl = member.isTemplateMixin()) mixinTempl.dsymbolSemantic(sc); @@ -2796,8 +2804,10 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type } mt.exp = exp2; - if (mt.exp.op == EXP.type || - mt.exp.op == EXP.scope_) + if ((mt.exp.op == EXP.type || mt.exp.op == EXP.scope_) && + // https://issues.dlang.org/show_bug.cgi?id=23863 + // compile time sequences are valid types + !mt.exp.type.isTypeTuple()) { if (!(sc.flags & SCOPE.Cfile) && // in (extended) C typeof may be used on types as with sizeof mt.exp.checkType()) @@ -3126,7 +3136,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type * Returns: * resulting expression with e.ident resolved */ -Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) +Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag) { Expression visitType(Type mt) { @@ -3624,7 +3634,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) * template opDispatch(name) if (isValid!name) { ... } */ uint errors = gagError ? global.startGagging() : 0; - e = dti.dotTemplateSemanticProp(sc, 0); + e = dti.dotTemplateSemanticProp(sc, DotExpFlag.none); if (gagError && global.endGagging(errors)) e = null; return returnExp(e); @@ -3730,6 +3740,9 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) { return noMember(mt, sc, e, ident, flag); } + // check before alias resolution; the alias itself might be deprecated! + if (s.isAliasDeclaration) + e.checkDeprecated(sc, s); s = s.toAlias(); if (auto em = s.isEnumMember()) @@ -3917,7 +3930,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) return mt.getProperty(sc, e.loc, ident, flag & 1); } - Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, 1); + Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, DotExpFlag.gag); if (!(flag & 1) && !res) { if (auto ns = mt.sym.search_correct(ident)) @@ -4599,6 +4612,74 @@ extern (C++) Expression defaultInit(Type mt, const ref Loc loc, const bool isCfi } } + +/********************************************** + * Extract complex type from core.stdc.config + * Params: + * loc = for error messages + * sc = context + * ty = a complex or imaginary type + * Returns: + * Complex!float, Complex!double, Complex!real or null for error + */ + +Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty) +{ + // singleton + __gshared Type complex_float; + __gshared Type complex_double; + __gshared Type complex_real; + + Type* pt; + Identifier id; + switch (ty) + { + case Timaginary32: + case Tcomplex32: id = Id.c_complex_float; pt = &complex_float; break; + case Timaginary64: + case Tcomplex64: id = Id.c_complex_double; pt = &complex_double; break; + case Timaginary80: + case Tcomplex80: id = Id.c_complex_real; pt = &complex_real; break; + default: + return Type.terror; + } + + if (*pt) + return *pt; + *pt = Type.terror; + + Module mConfig = Module.loadCoreStdcConfig(); + if (!mConfig) + { + error(loc, "`core.stdc.config` is required for complex numbers"); + return *pt; + } + + Dsymbol s = mConfig.searchX(Loc.initial, sc, id, IgnorePrivateImports); + if (!s) + { + error(loc, "`%s` not found in core.stdc.config", id.toChars()); + return *pt; + } + s = s.toAlias(); + if (auto t = s.getType()) + { + if (auto ts = t.toBasetype().isTypeStruct()) + { + *pt = ts; + return ts; + } + } + if (auto sd = s.isStructDeclaration()) + { + *pt = sd.type; + return sd.type; + } + + error(loc, "`%s` must be an alias for a complex struct", s.toChars()); + return *pt; +} + /******************************* Private *****************************************/ private: @@ -4909,7 +4990,7 @@ Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) * Return: * null if error, else RootObject AST as parsed */ -RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) +RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc) { OutBuffer buf; if (expressionsToString(buf, sc, tm.exps)) @@ -4919,7 +5000,10 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) const len = buf.length; buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink); + const bool doUnittests = global.params.useUnitTests || global.params.ddoc.doOutput || global.params.dihdr.doOutput; + 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.vin; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/dmd/typinf.d b/dmd/typinf.d index f993262e607..805eb585e5d 100644 --- a/dmd/typinf.d +++ b/dmd/typinf.d @@ -47,6 +47,7 @@ extern (C++) void genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope { if (!global.params.useTypeInfo) { + global.gag = 0; if (e) .error(loc, "expression `%s` uses the GC and cannot be used with switch `-betterC`", e.toChars()); else diff --git a/dmd/visitor.d b/dmd/visitor.d index 8990ce49a2a..7b059a061fd 100644 --- a/dmd/visitor.d +++ b/dmd/visitor.d @@ -89,6 +89,7 @@ public: void visit(ASTCodegen.ClassReferenceExp e) { visit(cast(ASTCodegen.Expression)e); } void visit(ASTCodegen.VoidInitExp e) { visit(cast(ASTCodegen.Expression)e); } void visit(ASTCodegen.ThrownExceptionExp e) { visit(cast(ASTCodegen.Expression)e); } + void visit(ASTCodegen.LoweredAssignExp e) { visit(cast(ASTCodegen.AssignExp)e); } } /** @@ -152,7 +153,7 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor // need to avoid infinite recursion. if (!(e.stageflags & stageToCBuffer)) { - int old = e.stageflags; + const old = e.stageflags; e.stageflags |= stageToCBuffer; foreach (el; *e.elements) if (el) @@ -240,6 +241,12 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor e.e1.accept(this); e.e2.accept(this); } + + override void visit(ASTCodegen.LoweredAssignExp e) + { + e.lowering.accept(this); + visit(cast(AssignExp)e); + } } extern (C++) class StoppableVisitor : Visitor diff --git a/dmd/visitor.h b/dmd/visitor.h index f8cbdb48c92..3d8c3e60220 100644 --- a/dmd/visitor.h +++ b/dmd/visitor.h @@ -10,13 +10,14 @@ #pragma once #include "root/dsystem.h" +#include "root/dcompat.h" // for d_bool class Statement; class ErrorStatement; class PeelStatement; class ExpStatement; class DtorExpStatement; -class CompileStatement; +class MixinStatement; class CompoundStatement; class CompoundDeclarationStatement; class UnrolledLoopStatement; @@ -109,7 +110,7 @@ class AnonDeclaration; class PragmaDeclaration; class ConditionalDeclaration; class StaticIfDeclaration; -class CompileDeclaration; +class MixinDeclaration; class StaticForeachDeclaration; class UserAttributeDeclaration; class ForwardingAttribDeclaration; @@ -267,6 +268,7 @@ class UshrAssignExp; class CatAssignExp; class CatElemAssignExp; class CatDcharAssignExp; +class LoweredAssignExp; class AddExp; class MinExp; class CatExp; @@ -364,7 +366,7 @@ class ParseTimeVisitor virtual void visit(SharedStaticDtorDeclaration *s) { visit((StaticDtorDeclaration *)s); } // AttribDeclarations - virtual void visit(CompileDeclaration *s) { visit((AttribDeclaration *)s); } + virtual void visit(MixinDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(UserAttributeDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(LinkDeclaration *s) { visit((AttribDeclaration *)s); } virtual void visit(AnonDeclaration *s) { visit((AttribDeclaration *)s); } @@ -395,7 +397,7 @@ class ParseTimeVisitor virtual void visit(ReturnStatement *s) { visit((Statement *)s); } virtual void visit(LabelStatement *s) { visit((Statement *)s); } virtual void visit(StaticAssertStatement *s) { visit((Statement *)s); } - virtual void visit(CompileStatement *s) { visit((Statement *)s); } + virtual void visit(MixinStatement *s) { visit((Statement *)s); } virtual void visit(WhileStatement *s) { visit((Statement *)s); } virtual void visit(ForStatement *s) { visit((Statement *)s); } virtual void visit(DoStatement *s) { visit((Statement *)s); } @@ -658,11 +660,12 @@ class Visitor : public ParseTimeVisitor virtual void visit(ClassReferenceExp *e) { visit((Expression *)e); } virtual void visit(VoidInitExp *e) { visit((Expression *)e); } virtual void visit(ThrownExceptionExp *e) { visit((Expression *)e); } + virtual void visit(LoweredAssignExp *e) { visit((AssignExp *)e); } }; class StoppableVisitor : public Visitor { public: - bool stop; + d_bool stop; StoppableVisitor() : stop(false) {} }; diff --git a/driver/main.cpp b/driver/main.cpp index 2b35dee08b9..7683cf24ae4 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -1112,6 +1112,10 @@ int cppmain() { fatal(); } + global.compileEnv.previewIn = global.params.previewIn; + global.compileEnv.ddocOutput = global.params.ddoc.doOutput; + global.compileEnv.shortenedMethods = global.params.shortenedMethods; + if (opts::fTimeTrace) { initializeTimeTrace(opts::fTimeTraceGranularity, 0, opts::allArguments[0]); } diff --git a/driver/timetrace_sema.d b/driver/timetrace_sema.d index 11be682c34b..ab7e10bbd7a 100644 --- a/driver/timetrace_sema.d +++ b/driver/timetrace_sema.d @@ -131,7 +131,7 @@ extern(C++) final class SemanticTimeTraceVisitor(SemaVisitor) : Visitor override void visit(StaticForeachDeclaration sfd) { semavisitor.visit(sfd); } - override void visit(CompileDeclaration cd) { semavisitor.visit(cd); } + override void visit(MixinDeclaration md) { semavisitor.visit(md); } override void visit(CPPNamespaceDeclaration ns) { semavisitor.visit(ns); } diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 14b1b26614d..16abb3c9aca 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -748,133 +748,6 @@ DSliceValue *DtoNewMulDimDynArray(const Loc &loc, Type *arrayType, return getSlice(arrayType, newptr); } -//////////////////////////////////////////////////////////////////////////////// -DSliceValue *DtoResizeDynArray(const Loc &loc, Type *arrayType, DValue *array, - LLValue *newdim) { - IF_LOG Logger::println("DtoResizeDynArray : %s", arrayType->toChars()); - LOG_SCOPE; - - assert(array); - assert(newdim); - assert(arrayType); - assert(arrayType->toBasetype()->ty == TY::Tarray); - - // decide on what runtime function to call based on whether the type is zero - // initialized - bool zeroInit = arrayType->toBasetype()->nextOf()->isZeroInit(); - - // call runtime - LLFunction *fn = - getRuntimeFunction(loc, gIR->module, zeroInit ? "_d_arraysetlengthT" - : "_d_arraysetlengthiT"); - - LLValue *newArray = gIR->CreateCallOrInvoke( - fn, DtoTypeInfoOf(loc, arrayType), newdim, - DtoBitCast(DtoLVal(array), fn->getFunctionType()->getParamType(2)), - ".gc_mem"); - - return getSlice(arrayType, newArray); -} - -//////////////////////////////////////////////////////////////////////////////// - -static LLValue *DtoSlicePtr(DValue *dval) { - Loc loc; - Type *vt = dval->type->toBasetype(); - if (vt->ty == TY::Tarray) { - return makeLValue(loc, dval); - } - - bool isStaticArray = vt->ty == TY::Tsarray; - LLValue *val = isStaticArray ? DtoLVal(dval) : makeLValue(loc, dval); - LLStructType *i8arrty = DtoArrayType(LLType::getInt8Ty(gIR->context())); - LLValue *array = DtoRawAlloca(i8arrty, 0, ".array"); - LLValue *len = isStaticArray ? DtoArrayLen(dval) : DtoConstSize_t(1); - DtoStore(len, DtoGEP(i8arrty, array, 0u, 0)); - DtoStore(DtoBitCast(val, getVoidPtrType()), DtoGEP(i8arrty, array, 0, 1)); - return array; -} - -static llvm::StructType *DtoSlicePtrType(DValue *dval) { - if(dval->type->toBasetype()->ty == TY::Tarray) - return isaStruct(DtoType(dval->type->toBasetype())); - else - return DtoArrayType(LLType::getInt8Ty(gIR->context())); -} - -static LLValue *DtoSlicePtr(Expression *e) { - return DtoSlicePtr(toElem(e)); -} - -DSliceValue *DtoCatArrays(const Loc &loc, Type *arrayType, Expression *exp1, - Expression *exp2) { - IF_LOG Logger::println("DtoCatArrays"); - LOG_SCOPE; - - llvm::SmallVector args; - LLFunction *fn = nullptr; - - if (auto ce = exp1->isCatExp()) { // handle multiple concat - fn = getRuntimeFunction(loc, gIR->module, "_d_arraycatnTX"); - - // Create array of slices - typedef llvm::SmallVector ArgVector; - ArgVector arrs; - DValue * dval = toElem(exp2); - arrs.push_back(DtoSlicePtr(dval)); - do { - arrs.push_back(DtoSlicePtr(ce->e2)); - ce = static_cast(ce->e1); - } while (ce->op == EXP::concatenate); - arrs.push_back(DtoSlicePtr(ce)); - - // Create static array from slices - LLPointerType *ptrarraytype = isaPointer(arrs[0]); - assert(ptrarraytype && "Expected pointer type"); - LLStructType *arraytype = DtoSlicePtrType(dval); - assert(arraytype && "Expected struct type"); - LLArrayType *type = LLArrayType::get(arraytype, arrs.size()); - LLValue *array = DtoRawAlloca(type, 0, ".slicearray"); - unsigned int i = 0; - for (ArgVector::reverse_iterator I = arrs.rbegin(), E = arrs.rend(); I != E; - ++I) { - LLValue *v = DtoLoad(arraytype, DtoBitCast(*I, ptrarraytype)); - DtoStore(v, DtoGEP(type, array, 0, i++, ".slice")); - } - - LLStructType *type2 = DtoArrayType(arraytype); - LLValue *array2 = DtoRawAlloca(type2, 0, ".array"); - DtoStore(DtoConstSize_t(arrs.size()), DtoGEP(type2, array2, 0u, 0, ".len")); - DtoStore(DtoBitCast(array, ptrarraytype), DtoGEP(type2, array2, 0, 1, ".ptr")); - LLType *bytearrarr = DtoArrayType(DtoArrayType(LLType::getInt8Ty(gIR->context()))); - LLType *pbytearrarr = getPtrToType(bytearrarr); - LLValue *val = DtoLoad(bytearrarr, DtoBitCast(array2, pbytearrarr)); - - // TypeInfo ti - args.push_back(DtoTypeInfoOf(loc, arrayType)); - // byte[][] arrs - args.push_back(val); - } else { - fn = getRuntimeFunction(loc, gIR->module, "_d_arraycatT"); - - // TypeInfo ti - args.push_back(DtoTypeInfoOf(loc, arrayType)); - - auto loadArray = [fn](Expression* e, int paramTypeIdx) { - DValue * dval = toElem(e); - LLValue *val = DtoLoad(DtoSlicePtrType(dval), DtoSlicePtr(dval)); - return DtoSlicePaint(val, fn->getFunctionType()->getParamType(paramTypeIdx)); - }; - // byte[] x - args.push_back(loadArray(exp1,1)); - // byte[] y - args.push_back(loadArray(exp2,2)); - } - - auto newArray = gIR->CreateCallOrInvoke(fn, args, ".appendedArray"); - return getSlice(arrayType, newArray); -} - //////////////////////////////////////////////////////////////////////////////// DSliceValue *DtoAppendDChar(const Loc &loc, DValue *arr, Expression *exp, diff --git a/gen/arrays.h b/gen/arrays.h index 8ea6e5ccfbc..3e893bbdc79 100644 --- a/gen/arrays.h +++ b/gen/arrays.h @@ -64,8 +64,6 @@ DSliceValue *DtoNewDynArray(const Loc &loc, Type *arrayType, DValue *dim, bool defaultInit = true); DSliceValue *DtoNewMulDimDynArray(const Loc &loc, Type *arrayType, DValue **dims, size_t ndims); -DSliceValue *DtoResizeDynArray(const Loc &loc, Type *arrayType, DValue *array, - llvm::Value *newdim); DSliceValue *DtoCatArrays(const Loc &loc, Type *type, Expression *e1, Expression *e2); diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp index 48d8342bf9c..7ca35acc0ea 100644 --- a/gen/asmstmt.cpp +++ b/gen/asmstmt.cpp @@ -501,9 +501,7 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) { { FuncDeclaration *fd = gIR->func()->decl; - OutBuffer mangleBuf; - mangleToBuffer(fd, &mangleBuf); - const char *fdmangle = mangleBuf.peekChars(); + const char *fdmangle = mangleExact(fd); // we use a simple static counter to make sure the new end labels are // unique diff --git a/gen/functions.cpp b/gen/functions.cpp index b9d85c8341d..701cd602ea4 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -173,7 +173,8 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype, // Non-typesafe variadics (both C and D styles) are also variadics on the LLVM // level. - const bool isLLVMVariadic = (f->parameterList.varargs == VARARGvariadic); + const bool isLLVMVariadic = (f->parameterList.varargs == VARARGvariadic || + f->parameterList.varargs == VARARGKRvariadic); if (isLLVMVariadic && f->linkage == LINK::d) { // Add extra `_arguments` parameter for D-style variadic functions. newIrFty.arg_arguments = diff --git a/gen/modules.cpp b/gen/modules.cpp index 54412fd1dee..6ad3f6e9c01 100644 --- a/gen/modules.cpp +++ b/gen/modules.cpp @@ -452,9 +452,11 @@ void codegenModule(IRState *irs, Module *m) { // Skip emission of all the additional module metadata if: // a) the -betterC switch is on, - // b) requested explicitly by the user via pragma(LDC_no_moduleinfo), or if - // c) there's no ModuleInfo declaration. - if (global.params.useModuleInfo && !m->noModuleInfo && Module::moduleinfo) { + // b) requested explicitly by the user via pragma(LDC_no_moduleinfo), + // c) there's no ModuleInfo declaration, or if + // d) the module is a C file. + if (global.params.useModuleInfo && !m->noModuleInfo && Module::moduleinfo && + m->filetype != FileType::c) { // generate ModuleInfo registerModuleInfo(m); } diff --git a/gen/pgo_ASTbased.cpp b/gen/pgo_ASTbased.cpp index 7819d9594f1..299d8be0ba3 100644 --- a/gen/pgo_ASTbased.cpp +++ b/gen/pgo_ASTbased.cpp @@ -190,7 +190,7 @@ struct MapRegionCounters : public StoppableVisitor { void visit(ScopeStatement *) override {} void visit(ReturnStatement *) override {} void visit(StaticAssertStatement *) override {} - void visit(CompileStatement *) override {} + void visit(MixinStatement *) override {} void visit(ScopeGuardStatement *) override {} void visit(ConditionalStatement *) override {} void visit(StaticForeachStatement *) override {} diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 37cd6dc7419..e145db51e7d 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -73,8 +73,6 @@ static void checkForImplicitGCCall(const Loc &loc, const char *name) { "_d_arrayappendcTX", "_d_arrayappendcd", "_d_arrayappendwd", - "_d_arraycatT", - "_d_arraycatnTX", "_d_arraysetlengthT", "_d_arraysetlengthiT", "_d_assocarrayliteralTX", @@ -606,25 +604,11 @@ static void buildRuntimeModule() { createFwdDecl(LINK::c, voidArrayTy, {"_d_newarraymTX", "_d_newarraymiTX"}, {typeInfoTy, sizeTy->arrayOf()}, {STCconst, 0}); - // void[] _d_arraysetlengthT (const TypeInfo ti, size_t newlength, void[]* p) - // void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) - createFwdDecl(LINK::c, voidArrayTy, - {"_d_arraysetlengthT", "_d_arraysetlengthiT"}, - {typeInfoTy, sizeTy, voidArrayPtrTy}, {STCconst, 0, 0}); - // void[] _d_arrayappendcd(ref byte[] x, dchar c) // void[] _d_arrayappendwd(ref byte[] x, dchar c) createFwdDecl(LINK::c, voidArrayTy, {"_d_arrayappendcd", "_d_arrayappendwd"}, {voidArrayTy, dcharTy}, {STCref, 0}); - // byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) - createFwdDecl(LINK::c, voidArrayTy, {"_d_arraycatT"}, - {typeInfoTy, voidArrayTy, voidArrayTy}, {STCconst, 0, 0}); - - // void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs) - createFwdDecl(LINK::c, voidArrayTy, {"_d_arraycatnTX"}, - {typeInfoTy, voidArrayTy->arrayOf()}, {STCconst, 0}); - // Object _d_newclass(const ClassInfo ci) // Object _d_allocclass(const ClassInfo ci) createFwdDecl(LINK::c, objectTy, {"_d_newclass", "_d_allocclass"}, diff --git a/gen/toir.cpp b/gen/toir.cpp index 265a12a35bf..403ff996d8b 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -456,6 +456,14 @@ class ToElemVisitor : public Visitor { ////////////////////////////////////////////////////////////////////////////// + void visit(LoweredAssignExp *e) override { + IF_LOG Logger::print("LoweredAssignExp::toElem: %s @ %s\n", e->toChars(), + e->type->toChars()); + LOG_SCOPE; + + result = toElem(e->lowering); + } + void visit(AssignExp *e) override { IF_LOG Logger::print("AssignExp::toElem: %s | (%s)(%s = %s)\n", e->toChars(), e->type->toChars(), @@ -463,17 +471,6 @@ class ToElemVisitor : public Visitor { e->e2->type ? e->e2->type->toChars() : nullptr); LOG_SCOPE; - if (auto ale = e->e1->isArrayLengthExp()) { - Logger::println("performing array.length assignment"); - DLValue arrval(ale->e1->type, DtoLVal(ale->e1)); - DValue *newlen = toElem(e->e2); - DSliceValue *slice = - DtoResizeDynArray(e->loc, arrval.type, &arrval, DtoRVal(newlen)); - DtoStore(DtoRVal(slice), DtoLVal(&arrval)); - result = newlen; - return; - } - // Initialization of ref variable? // Can't just override ConstructExp::toElem because not all EXP::construct // operations are actually instances of ConstructExp... Long live the DMD @@ -1196,8 +1193,8 @@ class ToElemVisitor : public Visitor { p->arrays.pop_back(); const bool hasLength = etype->ty != TY::Tpointer; - const bool needCheckUpper = hasLength && !e->upperIsInBounds; - const bool needCheckLower = !e->lowerIsLessThanUpper; + const bool needCheckUpper = hasLength && !e->upperIsInBounds(); + const bool needCheckLower = !e->lowerIsLessThanUpper(); if (p->emitArrayBoundsChecks() && (needCheckUpper || needCheckLower)) { llvm::BasicBlock *okbb = p->insertBB("bounds.ok"); llvm::BasicBlock *failbb = p->insertBBAfter(okbb, "bounds.fail"); @@ -2161,6 +2158,7 @@ class ToElemVisitor : public Visitor { e->type->toChars()); LOG_SCOPE; + // TODO: still required? if (global.params.betterC) { error( e->loc, @@ -2172,7 +2170,12 @@ class ToElemVisitor : public Visitor { return; } - result = DtoCatArrays(e->loc, e->type, e->e1, e->e2); + if (e->lowering) { + result = toElem(e->lowering); + return; + } + + llvm_unreachable("CatExp should have been lowered"); } ////////////////////////////////////////////////////////////////////////////// diff --git a/packaging/dlang-tools_version b/packaging/dlang-tools_version index 4ac687073e6..9a0ba479e62 100644 --- a/packaging/dlang-tools_version +++ b/packaging/dlang-tools_version @@ -1 +1 @@ -v2.103.1 \ No newline at end of file +v2.104.2 \ No newline at end of file diff --git a/packaging/dub_version b/packaging/dub_version index 4beef39c0b7..3fdd57c402b 100644 --- a/packaging/dub_version +++ b/packaging/dub_version @@ -1 +1 @@ -v1.32.1 \ No newline at end of file +v1.33.1 \ No newline at end of file diff --git a/packaging/mimalloc_version b/packaging/mimalloc_version index 01990b444d6..e1fbb880433 100644 --- a/packaging/mimalloc_version +++ b/packaging/mimalloc_version @@ -1 +1 @@ -v1.7.9 \ No newline at end of file +v1.8.2 \ No newline at end of file diff --git a/runtime/druntime/src/core/atomic.d b/runtime/druntime/src/core/atomic.d index 52e9590a0dd..637a112c2e8 100644 --- a/runtime/druntime/src/core/atomic.d +++ b/runtime/druntime/src/core/atomic.d @@ -2,6 +2,9 @@ * The atomic module provides basic support for lock-free * concurrent programming. * + * $(NOTE Use the `-preview=nosharedaccess` compiler flag to detect + * unsafe individual read or write operations on shared data.) + * * Copyright: Copyright Sean Kelly 2005 - 2016. * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Sean Kelly, Alex Rønne Petersen, Manu Evans @@ -10,6 +13,22 @@ module core.atomic; +/// +@safe unittest +{ + int y = 2; + shared int x = y; // OK + + //x++; // read modify write error + x.atomicOp!"+="(1); // OK + //y = x; // read error with preview flag + y = x.atomicLoad(); // OK + assert(y == 3); + //x = 5; // write error with preview flag + x.atomicStore(5); // OK + assert(x.atomicLoad() == 5); +} + import core.internal.atomic; import core.internal.attributes : betterC; import core.internal.traits : hasUnsharedIndirections; @@ -1186,42 +1205,6 @@ version (CoreUnittest) assert(ptr is null); } - unittest - { - import core.thread; - - // Use heap memory to ensure an optimizing - // compiler doesn't put things in registers. - uint* x = new uint(); - bool* f = new bool(); - uint* r = new uint(); - - auto thr = new Thread(() - { - while (!*f) - { - } - - atomicFence(); - - *r = *x; - }); - - thr.start(); - - *x = 42; - - atomicFence(); - - *f = true; - - atomicFence(); - - thr.join(); - - assert(*r == 42); - } - // === atomicFetchAdd and atomicFetchSub operations ==== @betterC pure nothrow @nogc @safe unittest { diff --git a/runtime/druntime/src/core/demangle.d b/runtime/druntime/src/core/demangle.d index c7ab6a9b489..2eece8b8863 100644 --- a/runtime/druntime/src/core/demangle.d +++ b/runtime/druntime/src/core/demangle.d @@ -69,62 +69,17 @@ pure @safe: { buf = buf_; addType = addType_; - dst = dst_; + dst.dst = dst_; } - - enum size_t minBufSize = 4000; - - const(char)[] buf = null; - char[] dst = null; + Buffer dst; size_t pos = 0; - size_t len = 0; size_t brp = 0; // current back reference pos AddType addType = AddType.yes; bool mute = false; Hooks hooks; - static class ParseException : Exception - { - this(string msg) @safe pure nothrow - { - super( msg ); - } - } - - - static class OverflowException : Exception - { - this(string msg) @safe pure nothrow - { - super( msg ); - } - } - - - static noreturn error( string msg = "Invalid symbol" ) @trusted /* exception only used in module */ - { - pragma(inline, false); // tame dmd inliner - - //throw new ParseException( msg ); - debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); - throw __ctfe ? new ParseException(msg) - : cast(ParseException) __traits(initSymbol, ParseException).ptr; - - } - - - static noreturn overflow( string msg = "Buffer overflow" ) @trusted /* exception only used in module */ - { - pragma(inline, false); // tame dmd inliner - - //throw new OverflowException( msg ); - debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); - throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr; - } - - ////////////////////////////////////////////////////////////////////////// // Type Testing and Conversion ////////////////////////////////////////////////////////////////////////// @@ -163,96 +118,16 @@ pure @safe: error(); } - - ////////////////////////////////////////////////////////////////////////// - // Data Output - ////////////////////////////////////////////////////////////////////////// - - - static bool contains( const(char)[] a, const(char)[] b ) @trusted - { - if (a.length && b.length) - { - auto bend = b.ptr + b.length; - auto aend = a.ptr + a.length; - return a.ptr <= b.ptr && bend <= aend; - } - return false; - } - - - // move val to the end of the dst buffer - char[] shift( const(char)[] val ) - { - pragma(inline, false); // tame dmd inliner - - if ( val.length && !mute ) - { - assert( contains( dst[0 .. len], val ) ); - debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr ); - - if (len + val.length > dst.length) - overflow(); - size_t v = &val[0] - &dst[0]; - dst[len .. len + val.length] = val[]; - for (size_t p = v; p < len; p++) - dst[p] = dst[p + val.length]; - - return dst[len - val.length .. len]; - } - return null; - } - - // remove val from dst buffer - void remove( const(char)[] val ) + char[] shift(scope const(char)[] val) return scope { - pragma(inline, false); // tame dmd inliner - - if ( val.length ) - { - assert( contains( dst[0 .. len], val ) ); - debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr ); - size_t v = &val[0] - &dst[0]; - assert( len >= val.length && len <= dst.length ); - len -= val.length; - for (size_t p = v; p < len; p++) - dst[p] = dst[p + val.length]; - } - } - - char[] append( const(char)[] val ) return scope - { - pragma(inline, false); // tame dmd inliner - - if ( val.length && !mute ) - { - if ( !dst.length ) - dst.length = minBufSize; - assert( !contains( dst[0 .. len], val ) ); - debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr ); - - if ( dst.length - len >= val.length && &dst[len] == &val[0] ) - { - // data is already in place - auto t = dst[len .. len + val.length]; - len += val.length; - return t; - } - if ( dst.length - len >= val.length ) - { - dst[len .. len + val.length] = val[]; - auto t = dst[len .. len + val.length]; - len += val.length; - return t; - } - overflow(); - } - return null; + if (mute) + return null; + return dst.shift(val); } void putComma(size_t n) { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); if (n) put(", "); } @@ -265,14 +140,9 @@ pure @safe: void put(scope const(char)[] val) return scope { - pragma(inline, false); // tame dmd inliner - - if (!val.length) return; - - if (!contains(dst[0 .. len], val)) - append(val); - else - shift(val); + if (mute) + return; + dst.append(val); } @@ -297,7 +167,7 @@ pure @safe: { if ( val.length ) { - append( " " ); + put(" "); put( val ); } } @@ -307,7 +177,7 @@ pure @safe: { debug(trace) printf( "silent+\n" ); debug(trace) scope(success) printf( "silent-\n" ); - auto n = len; dg(); len = n; + auto n = dst.length; dg(); dst.len = n; } @@ -835,7 +705,7 @@ pure @safe: debug(trace) printf( "parseType+\n" ); debug(trace) scope(success) printf( "parseType-\n" ); - auto beg = len; + auto beg = dst.length; auto t = front; char[] parseBackrefType(scope char[] delegate() pure @safe parseDg) pure @safe @@ -867,19 +737,19 @@ pure @safe: put( "shared(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'x': // Const (x Type) popFront(); put( "const(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'y': // Immutable (y Type) popFront(); put( "immutable(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'N': popFront(); switch ( front ) @@ -887,29 +757,28 @@ pure @safe: case 'n': // Noreturn popFront(); put("noreturn"); - return dst[beg .. len]; + return dst[beg .. $]; case 'g': // Wild (Ng Type) popFront(); // TODO: Anything needed here? put( "inout(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'h': // TypeVector (Nh Type) popFront(); put( "__vector(" ); parseType(); put( ')' ); - return dst[beg .. len]; + return dst[beg .. $]; default: error(); - assert( 0 ); } case 'A': // TypeArray (A Type) popFront(); parseType(); put( "[]" ); - return dst[beg .. len]; + return dst[beg .. $]; case 'G': // TypeStaticArray (G Number Type) popFront(); auto num = sliceNumber(); @@ -917,21 +786,21 @@ pure @safe: put( '[' ); put( num ); put( ']' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'H': // TypeAssocArray (H Type Type) popFront(); // skip t1 auto tx = parseType(); parseType(); put( '[' ); - put( tx ); + shift(tx); put( ']' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'P': // TypePointer (P Type) popFront(); parseType(); put( '*' ); - return dst[beg .. len]; + return dst[beg .. $]; case 'F': case 'U': case 'W': case 'V': case 'R': // TypeFunction return parseTypeFunction(); case 'C': // TypeClass (C LName) @@ -940,7 +809,7 @@ pure @safe: case 'T': // TypeTypedef (T LName) popFront(); parseQualifiedName(); - return dst[beg .. len]; + return dst[beg .. $]; case 'D': // TypeDelegate (D TypeFunction) popFront(); auto modifiers = parseModifier(); @@ -957,15 +826,15 @@ pure @safe: put(str); } } - return dst[beg .. len]; + return dst[beg .. $]; case 'n': // TypeNone (n) popFront(); // TODO: Anything needed here? - return dst[beg .. len]; + return dst[beg .. $]; case 'B': // TypeTuple (B Number Arguments) popFront(); // TODO: Handle this. - return dst[beg .. len]; + return dst[beg .. $]; case 'Z': // Internal symbol // This 'type' is used for untyped internal symbols, i.e.: // __array @@ -975,13 +844,13 @@ pure @safe: // __Interface // __ModuleInfo popFront(); - return dst[beg .. len]; + return dst[beg .. $]; default: if (t >= 'a' && t <= 'w') { popFront(); put( primitives[cast(size_t)(t - 'a')] ); - return dst[beg .. len]; + return dst[beg .. $]; } else if (t == 'z') { @@ -991,14 +860,13 @@ pure @safe: case 'i': popFront(); put( "cent" ); - return dst[beg .. len]; + return dst[beg .. $]; case 'k': popFront(); put( "ucent" ); - return dst[beg .. len]; + return dst[beg .. $]; default: error(); - assert( 0 ); } } error(); @@ -1351,12 +1219,13 @@ pure @safe: { debug(trace) printf( "parseTypeFunction+\n" ); debug(trace) scope(success) printf( "parseTypeFunction-\n" ); - auto beg = len; + auto beg = dst.length; parseCallConvention(); auto attributes = parseFuncAttr(); - auto argbeg = len; + auto argbeg = dst.length; + put(IsDelegate.yes == isdg ? "delegate" : "function"); put( '(' ); parseFuncArguments(); put( ')' ); @@ -1369,17 +1238,18 @@ pure @safe: put(str); } } - auto retbeg = len; - parseType(); - put( ' ' ); - // append delegate/function - if (IsDelegate.yes == isdg) - put( "delegate" ); - else - put( "function" ); - // move arguments and attributes behind name - shift( dst[argbeg .. retbeg] ); - return dst[beg..len]; + + // A function / delegate return type is located at the end of its mangling + // Write it in order, then shift it back to 'code order' + // e.g. `delegate(int) @safedouble ' => 'double delegate(int) @safe' + { + auto retbeg = dst.length; + parseType(); + put(' '); + shift(dst[argbeg .. retbeg]); + } + + return dst[beg .. $]; } static bool isCallConvention( char ch ) @@ -1701,7 +1571,7 @@ pure @safe: if ( mayBeMangledNameArg() ) { - auto l = len; + auto l = dst.length; auto p = pos; auto b = brp; try @@ -1712,7 +1582,7 @@ pure @safe: } catch ( ParseException e ) { - len = l; + dst.len = l; pos = p; brp = b; debug(trace) printf( "not a mangled name arg\n" ); @@ -1724,7 +1594,7 @@ pure @safe: // try all possible pairs of numbers auto qlen = decodeNumber() / 10; // last digit needed for QualifiedName pos--; - auto l = len; + auto l = dst.length; auto p = pos; auto b = brp; while ( qlen > 0 ) @@ -1740,7 +1610,7 @@ pure @safe: } qlen /= 10; // retry with one digit less pos = --p; - len = l; + dst.len = l; brp = b; } } @@ -1860,7 +1730,7 @@ pure @safe: case '0': .. case '9': if ( mayBeTemplateInstanceName() ) { - auto t = len; + auto t = dst.length; try { @@ -1871,7 +1741,7 @@ pure @safe: catch ( ParseException e ) { debug(trace) printf( "not a template instance name\n" ); - len = t; + dst.len = t; } } goto case; @@ -1889,10 +1759,9 @@ pure @safe: { // try to demangle a function, in case we are pointing to some function local auto prevpos = pos; - auto prevlen = len; + auto prevlen = dst.length; auto prevbrp = brp; - char[] attr; try { if ( 'M' == front ) @@ -1908,6 +1777,7 @@ pure @safe: } if ( isCallConvention( front ) ) { + char[] attr; // we don't want calling convention and attributes in the qualified name parseCallConvention(); auto attributes = parseFuncAttr(); @@ -1917,23 +1787,23 @@ pure @safe: put(str); put(' '); } - attr = dst[prevlen .. len]; + attr = dst[prevlen .. $]; } put( '(' ); parseFuncArguments(); put( ')' ); + return attr; } } catch ( ParseException ) { // not part of a qualified name, so back up pos = prevpos; - len = prevlen; + dst.len = prevlen; brp = prevbrp; - attr = null; } - return attr; + return null; } /* @@ -1945,7 +1815,7 @@ pure @safe: { debug(trace) printf( "parseQualifiedName+\n" ); debug(trace) scope(success) printf( "parseQualifiedName-\n" ); - size_t beg = len; + size_t beg = dst.length; size_t n = 0; do @@ -1956,7 +1826,7 @@ pure @safe: parseFunctionTypeNoReturn(); } while ( isSymbolNameFront() ); - return dst[beg .. len]; + return dst[beg .. $]; } @@ -1977,17 +1847,17 @@ pure @safe: match( 'D' ); do { - size_t beg = len; - size_t nameEnd = len; + size_t beg = dst.length; + size_t nameEnd = dst.length; char[] attr; do { if ( attr ) - remove( attr ); // dump attributes of parent symbols - if ( beg != len ) + dst.remove(attr); // dump attributes of parent symbols + if (beg != dst.length) put( '.' ); parseSymbolName(); - nameEnd = len; + nameEnd = dst.length; attr = parseFunctionTypeNoReturn( displayType ); } while ( isSymbolNameFront() ); @@ -1995,7 +1865,7 @@ pure @safe: if ( displayType ) { attr = shift( attr ); - nameEnd = len - attr.length; // name includes function arguments + nameEnd = dst.length - attr.length; // name includes function arguments } name = dst[beg .. nameEnd]; @@ -2003,7 +1873,7 @@ pure @safe: if ( 'M' == front ) popFront(); // has 'this' pointer - auto lastlen = len; + auto lastlen = dst.length; auto type = parseType(); if ( displayType ) { @@ -2016,7 +1886,7 @@ pure @safe: { // remove type assert( attr.length == 0 ); - len = lastlen; + dst.len = lastlen; } if ( pos >= buf.length || (n != 0 && pos >= end) ) return; @@ -2040,15 +1910,6 @@ pure @safe: parseMangledName( AddType.yes == addType ); } - char[] copyInput() return scope - { - if (dst.length < buf.length) - dst.length = buf.length; - char[] r = dst[0 .. buf.length]; - r[] = buf[]; - return r; - } - char[] doDemangle(alias FUNC)() return scope { while ( true ) @@ -2057,17 +1918,17 @@ pure @safe: { debug(info) printf( "demangle(%.*s)\n", cast(int) buf.length, buf.ptr ); FUNC(); - return dst[0 .. len]; + return dst[0 .. $]; } catch ( OverflowException e ) { debug(trace) printf( "overflow... restarting\n" ); - auto a = minBufSize; - auto b = 2 * dst.length; + auto a = Buffer.minSize; + auto b = 2 * dst.dst.length; auto newsz = a < b ? b : a; debug(info) printf( "growing dst to %lu bytes\n", newsz ); - dst.length = newsz; - pos = len = brp = 0; + dst.dst.length = newsz; + pos = dst.len = brp = 0; continue; } catch ( ParseException e ) @@ -2077,7 +1938,7 @@ pure @safe: auto msg = e.toString(); printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); } - return copyInput(); + return dst.copyInput(buf); } catch ( Exception e ) { @@ -2119,7 +1980,7 @@ char[] demangle(return scope const(char)[] buf, return scope char[] dst = null, // fast path (avoiding throwing & catching exception) for obvious // non-D mangled names if (buf.length < 2 || !(buf[0] == 'D' || buf[0..2] == "_D")) - return d.copyInput(); + return d.dst.copyInput(buf); return d.demangleName(); } @@ -2212,7 +2073,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe d.popFront(); size_t n = d.decodeBackref(); if (!n || n > refpos) - d.error("invalid back reference"); + error("invalid back reference"); auto savepos = d.pos; scope(exit) d.pos = savepos; @@ -2220,11 +2081,11 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe auto idlen = d.decodeNumber(); if (d.pos + idlen > d.buf.length) - d.error("invalid back reference"); + error("invalid back reference"); auto id = d.buf[d.pos .. d.pos + idlen]; auto pid = id in idpos; if (!pid) - d.error("invalid back reference"); + error("invalid back reference"); npos = positionInResult(*pid); } encodeBackref(reslen - npos); @@ -2235,7 +2096,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe { auto n = d.decodeNumber(); if (!n || n > d.buf.length || n > d.buf.length - d.pos) - d.error("LName too shot or too long"); + error("LName too shot or too long"); auto id = d.buf[d.pos .. d.pos + n]; d.pos += n; if (auto pid = id in idpos) @@ -2267,7 +2128,7 @@ char[] reencodeMangled(return scope const(char)[] mangled) nothrow pure @safe d.popFront(); auto n = d.decodeBackref(); if (n == 0 || n > refPos) - d.error("invalid back reference"); + error("invalid back reference"); size_t npos = positionInResult(refPos - n); size_t reslen = result.length; @@ -2910,6 +2771,7 @@ private shared CXX_DEMANGLER __cxa_demangle; CXX_DEMANGLER getCXXDemangler() nothrow @trusted { + import core.atomic : atomicLoad, atomicStore; if (__cxa_demangle is null) version (Posix) { @@ -2923,17 +2785,21 @@ CXX_DEMANGLER getCXXDemangler() nothrow @trusted version (Solaris) import core.sys.solaris.dlfcn : RTLD_DEFAULT; if (auto found = cast(CXX_DEMANGLER) dlsym(RTLD_DEFAULT, "__cxa_demangle")) - __cxa_demangle = found; + atomicStore(__cxa_demangle, found); } if (__cxa_demangle is null) - __cxa_demangle = (const char* mangled_name, char* output_buffer, - size_t* length, int* status) nothrow pure @trusted { - *status = -1; - return null; - }; + { + static extern(C) char* _(const char* mangled_name, char* output_buffer, + size_t* length, int* status) nothrow pure @trusted + { + *status = -1; + return null; + } + atomicStore(__cxa_demangle, &_); + } - return __cxa_demangle; + return atomicLoad(__cxa_demangle); } /** @@ -2974,3 +2840,163 @@ private char[] demangleCXX(return scope const(char)[] buf, CXX_DEMANGLER __cxa_d dst[] = buf[]; return dst; } + +/** + * Error handling through Exceptions + * + * The following types / functions are only used in this module, + * hence why the functions are `@trusted`. + * To make things `@nogc`, default-initialized instances are thrown. + */ +private class ParseException : Exception +{ + public this(string msg) @safe pure nothrow + { + super(msg); + } +} + +/// Ditto +private class OverflowException : Exception +{ + public this(string msg) @safe pure nothrow + { + super(msg); + } +} + +/// Ditto +private noreturn error(string msg = "Invalid symbol") @trusted pure +{ + version (DigitalMars) pragma(inline, false); // tame dmd inliner + + //throw new ParseException( msg ); + debug(info) printf( "error: %.*s\n", cast(int) msg.length, msg.ptr ); + throw __ctfe ? new ParseException(msg) + : cast(ParseException) __traits(initSymbol, ParseException).ptr; +} + +/// Ditto +private noreturn overflow(string msg = "Buffer overflow") @trusted pure +{ + version (DigitalMars) pragma(inline, false); // tame dmd inliner + + //throw new OverflowException( msg ); + debug(info) printf( "overflow: %.*s\n", cast(int) msg.length, msg.ptr ); + throw cast(OverflowException) __traits(initSymbol, OverflowException).ptr; +} + +private struct Buffer +{ + enum size_t minSize = 4000; + + @safe pure: + + private char[] dst; + private size_t len; + + public alias opDollar = len; + + public size_t length () const scope @safe pure nothrow @nogc + { + return this.len; + } + + public inout(char)[] opSlice (size_t from, size_t to) + inout return scope @safe pure nothrow @nogc + { + assert(from <= to); + assert(to <= len); + return this.dst[from .. to]; + } + + static bool contains(scope const(char)[] a, scope const(char)[] b) @trusted + { + if (a.length && b.length) + { + auto bend = b.ptr + b.length; + auto aend = a.ptr + a.length; + return a.ptr <= b.ptr && bend <= aend; + } + return false; + } + + char[] copyInput(scope const(char)[] buf) + return scope nothrow + { + if (dst.length < buf.length) + dst.length = buf.length; + char[] r = dst[0 .. buf.length]; + r[] = buf[]; + return r; + } + + // move val to the end of the dst buffer + char[] shift(scope const(char)[] val) return scope + { + version (DigitalMars) pragma(inline, false); // tame dmd inliner + + if (val.length) + { + assert( contains( dst[0 .. len], val ) ); + debug(info) printf( "shifting (%.*s)\n", cast(int) val.length, val.ptr ); + + if (len + val.length > dst.length) + overflow(); + size_t v = &val[0] - &dst[0]; + dst[len .. len + val.length] = val[]; + for (size_t p = v; p < len; p++) + dst[p] = dst[p + val.length]; + + return dst[len - val.length .. len]; + } + return null; + } + + // remove val from dst buffer + void remove(scope const(char)[] val) scope + { + version (DigitalMars) pragma(inline, false); // tame dmd inliner + + if ( val.length ) + { + assert( contains( dst[0 .. len], val ) ); + debug(info) printf( "removing (%.*s)\n", cast(int) val.length, val.ptr ); + size_t v = &val[0] - &dst[0]; + assert( len >= val.length && len <= dst.length ); + len -= val.length; + for (size_t p = v; p < len; p++) + dst[p] = dst[p + val.length]; + } + } + + char[] append(scope const(char)[] val) return scope + { + version (DigitalMars) pragma(inline, false); // tame dmd inliner + + if (val.length) + { + if ( !dst.length ) + dst.length = minSize; + assert( !contains( dst[0 .. len], val ) ); + debug(info) printf( "appending (%.*s)\n", cast(int) val.length, val.ptr ); + + if ( dst.length - len >= val.length && &dst[len] == &val[0] ) + { + // data is already in place + auto t = dst[len .. len + val.length]; + len += val.length; + return t; + } + if ( dst.length - len >= val.length ) + { + dst[len .. len + val.length] = val[]; + auto t = dst[len .. len + val.length]; + len += val.length; + return t; + } + overflow(); + } + return null; + } +} diff --git a/runtime/druntime/src/core/internal/array/appending.d b/runtime/druntime/src/core/internal/array/appending.d index b609167eefe..bb24813ae9e 100644 --- a/runtime/druntime/src/core/internal/array/appending.d +++ b/runtime/druntime/src/core/internal/array/appending.d @@ -35,7 +35,7 @@ template _d_arrayappendcTXImpl(Tarr : T[], T) ref Tarr _d_arrayappendcTX(return ref scope Tarr px, size_t n) @trusted pure nothrow { // needed for CTFE: https://github.com/dlang/druntime/pull/3870#issuecomment-1178800718 - pragma(inline, false); + version (DigitalMars) pragma(inline, false); version (D_TypeInfo) { auto ti = typeid(Tarr); @@ -70,7 +70,7 @@ template _d_arrayappendcTXImpl(Tarr : T[], T) /// Implementation of `_d_arrayappendT` ref Tarr _d_arrayappendT(Tarr : T[], T)(return ref scope Tarr x, scope Tarr y) @trusted { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); import core.stdc.string : memcpy; import core.internal.traits : hasElaborateCopyConstructor, Unqual; diff --git a/runtime/druntime/src/core/internal/array/capacity.d b/runtime/druntime/src/core/internal/array/capacity.d index 254e9501f63..10ce2c65c95 100644 --- a/runtime/druntime/src/core/internal/array/capacity.d +++ b/runtime/druntime/src/core/internal/array/capacity.d @@ -36,7 +36,7 @@ template _d_arraysetlengthTImpl(Tarr : T[], T) */ size_t _d_arraysetlengthT(return scope ref Tarr arr, size_t newlength) @trusted pure nothrow { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); version (D_TypeInfo) { auto ti = typeid(Tarr); diff --git a/runtime/druntime/src/core/internal/array/concatenation.d b/runtime/druntime/src/core/internal/array/concatenation.d index 99f33da7683..ff777a6b3ab 100644 --- a/runtime/druntime/src/core/internal/array/concatenation.d +++ b/runtime/druntime/src/core/internal/array/concatenation.d @@ -8,71 +8,172 @@ */ module core.internal.array.concatenation; -/// See $(REF _d_arraycatnTX, rt,lifetime) -private extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) pure nothrow; - -/// Implementation of `_d_arraycatnTX` and `_d_arraycatnTXTrace` -template _d_arraycatnTXImpl(Tarr : ResultArrT[], ResultArrT : T[], T) +/** + * Concatenate the arrays inside of `froms`. + * `_d_arraycatnTX(a, b, c)` means `a ~ b ~ c`. + * + * Params: + * froms = Arrays to be concatenated. + * Returns: + * A newly allocated array that contains all the elements from `froms`. + */ +Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted { - private enum errorMessage = "Cannot concatenate arrays if compiling without support for runtime type information!"; + import core.internal.traits : hasElaborateCopyConstructor, Unqual; + import core.lifetime : copyEmplace; + import core.stdc.string : memcpy; - /** - * Concatenating the arrays inside of `arrs`. - * `_d_arraycatnTX([a, b, c])` means `a ~ b ~ c`. - * Params: - * arrs = Array containing arrays that will be concatenated. - * Returns: - * A newly allocated array that contains all the elements from all the arrays in `arrs`. - * Bugs: - * This function template was ported from a much older runtime hook that bypassed safety, - * purity, and throwabilty checks. To prevent breaking existing code, this function template - * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations. - */ - ResultArrT _d_arraycatnTX(scope const Tarr arrs) @trusted pure nothrow + Tret res; + size_t totalLen; + + alias T = typeof(res[0]); + enum elemSize = T.sizeof; + enum hasPostblit = __traits(hasPostblit, T); + + static foreach (from; froms) + static if (is (typeof(from) : T)) + totalLen++; + else + totalLen += from.length; + + if (totalLen == 0) + return res; + res.length = totalLen; + + /* Currently, if both a postblit and a cpctor are defined, the postblit is + * used. If this changes, the condition below will have to be adapted. + */ + static if (hasElaborateCopyConstructor!T && !hasPostblit) { - pragma(inline, false); - version (D_TypeInfo) - { - auto ti = typeid(ResultArrT); + size_t i = 0; + foreach (ref from; froms) + static if (is (typeof(from) : T)) + copyEmplace(cast(T) from, res[i++]); + else + { + if (from.length) + foreach (ref elem; from) + copyEmplace(cast(T) elem, res[i++]); + } + } + else + { + auto resptr = cast(Unqual!T *) res; + foreach (ref from; froms) + static if (is (typeof(from) : T)) + memcpy(resptr++, cast(Unqual!T *) &from, elemSize); + else + { + const len = from.length; + if (len) + { + memcpy(resptr, cast(Unqual!T *) from, len * elemSize); + resptr += len; + } + } + + static if (hasPostblit) + foreach (ref elem; res) + (cast() elem).__xpostblit(); + } + + return res; +} - byte[][] arrs2 = (cast(byte[]*)arrs.ptr)[0 .. arrs.length]; - void[] result = ._d_arraycatnTX(ti, arrs2); - return (cast(T*)result.ptr)[0 .. result.length]; +// postblit +@safe unittest +{ + int counter; + struct S + { + int val; + this(this) + { + counter++; } - else - assert(0, errorMessage); } - version (D_ProfileGC) + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = []; + S[] arr3 = [S(6), S(7), S(8)]; + S elem = S(9); + S[] result = _d_arraycatnTX!(S[])(arr1, arr2, arr3, elem); + + assert(counter == 7); + assert(result == [S(0), S(1), S(2), S(6), S(7), S(8), S(9)]); +} + +// copy constructor +@safe unittest +{ + int counter; + struct S { - import core.internal.array.utils : _d_HookTraceImpl; - - /** - * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concat). - * Bugs: - * This function template was ported from a much older runtime hook that bypassed safety, - * purity, and throwabilty checks. To prevent breaking existing code, this function template - * is temporarily declared `@trusted pure nothrow` until the implementation can be brought up to modern D expectations. - */ - alias _d_arraycatnTXTrace = _d_HookTraceImpl!(ResultArrT, _d_arraycatnTX, errorMessage); + int val; + this(ref return scope S rhs) + { + val = rhs.val; + counter++; + } } + + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = [S(3), S(4), S(5)]; + S[] arr3 = [S(6), S(7), S(8)]; + S elem = S(9); + S[] result = _d_arraycatnTX!(S[])(arr1, elem, arr2, arr3); + + assert(counter == 10); + assert(result == [S(0), S(1), S(2), S(9), S(3), S(4), S(5), S(6), S(7), S(8)]); } +// throwing @safe unittest { int counter; + bool didThrow; struct S { int val; this(this) { counter++; + if (counter == 4) + throw new Exception(""); } } - S[][] arr = [[S(0), S(1), S(2), S(3)], [S(4), S(5), S(6), S(7)]]; - S[] result = _d_arraycatnTXImpl!(typeof(arr))._d_arraycatnTX(arr); + try + { + S[] arr1 = [S(0), S(1), S(2)]; + S[] arr2 = [S(3), S(4), S(5)]; + _d_arraycatnTX!(S[])(arr1, arr2); + } + catch (Exception) + { + didThrow = true; + } + + assert(counter == 4); + assert(didThrow); +} + +version (D_ProfileGC) +{ + /** + * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concatenation). + */ + Tret _d_arraycatnTXTrace(Tret, Tarr...)(string file, int line, string funcname, scope auto ref Tarr froms) @trusted + { + version (D_TypeInfo) + { + import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure; + mixin(TraceHook!(Tarr.stringof, "_d_arraycatnTX")); - assert(counter == 8); - assert(result == [S(0), S(1), S(2), S(3), S(4), S(5), S(6), S(7)]); + import core.lifetime: forward; + return _d_arraycatnTX!Tret(forward!froms); + } + else + assert(0, "Cannot concatenate arrays if compiling without support for runtime type information!"); + } } diff --git a/runtime/druntime/src/core/internal/array/construction.d b/runtime/druntime/src/core/internal/array/construction.d index ae71f513129..25083597761 100644 --- a/runtime/druntime/src/core/internal/array/construction.d +++ b/runtime/druntime/src/core/internal/array/construction.d @@ -36,7 +36,7 @@ import core.internal.traits : Unqual; */ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* makeWeaklyPure = null) @trusted { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); import core.internal.traits : hasElaborateCopyConstructor; import core.lifetime : copyEmplace; import core.stdc.string : memcpy; @@ -200,7 +200,7 @@ Tarr _d_arrayctor(Tarr : T[], T)(return scope Tarr to, scope Tarr from, char* ma */ void _d_arraysetctor(Tarr : T[], T)(scope Tarr p, scope ref T value) @trusted { - pragma(inline, false); + version (DigitalMars) pragma(inline, false); import core.lifetime : copyEmplace; size_t i; diff --git a/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d b/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d index 1159d10aafe..59205c04caa 100644 --- a/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d +++ b/runtime/druntime/src/core/internal/gc/impl/conservative/gc.d @@ -1510,7 +1510,7 @@ struct Gcx List*[Bins.B_NUMSMALL] bucket; // free list for each small size // run a collection when reaching those thresholds (number of used pages) - float smallCollectThreshold, largeCollectThreshold; + float smallCollectThreshold = 0.0f, largeCollectThreshold = 0.0f; uint usedSmallPages, usedLargePages; // total number of mapped pages uint mappedPages; @@ -3533,7 +3533,7 @@ struct Pool Small = 4, Large = 12 } - ShiftBy shiftBy; // shift count for the divisor used for determining bit indices. + ShiftBy shiftBy = void; // shift count for the divisor used for determining bit indices. // This tracks how far back we have to go to find the nearest B_PAGE at // a smaller address than a B_PAGEPLUS. To save space, we use a uint. diff --git a/runtime/druntime/src/core/internal/string.d b/runtime/druntime/src/core/internal/string.d index 64a9cc92ffb..e09bba4707b 100644 --- a/runtime/druntime/src/core/internal/string.d +++ b/runtime/druntime/src/core/internal/string.d @@ -12,26 +12,57 @@ module core.internal.string; pure: nothrow: @nogc: +@safe: -alias UnsignedStringBuf = char[20]; +alias UnsignedStringBuf = char[64]; /** Converts an unsigned integer value to a string of characters. -This implementation is a template so it can be used when compiling with -betterC. +Can be used when compiling with -betterC. Does not allocate memory. Params: + T = char, wchar or dchar value = the unsigned integer value to convert buf = the pre-allocated buffer used to store the result - radix = the numeric base to use in the conversion (defaults to 10) + radix = the numeric base to use in the conversion 2 through 36 (defaults to 10) + upperCase = use upper case letters for radices 11 - 36 Returns: The unsigned integer value as a string of characters */ -char[] unsignedToTempString(uint radix = 10)(ulong value, return scope char[] buf) @safe -if (radix >= 2 && radix <= 16) +T[] unsignedToTempString(uint radix = 10, bool upperCase = false, T)(ulong value, return scope T[] buf) +if (radix >= 2 && radix <= 36 && + (is(T == char) || is(T == wchar) || is(T == dchar))) { + enum baseChar = upperCase ? 'A' : 'a'; size_t i = buf.length; + + static if (size_t.sizeof == 4) // 32 bit CPU + { + if (value <= uint.max) + { + // use faster 32 bit arithmetic + uint val = cast(uint) value; + do + { + uint x = void; + if (val < radix) + { + x = cast(uint)val; + val = 0; + } + else + { + x = cast(uint)(val % radix); + val /= radix; + } + buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar); + } while (val); + return buf[i .. $]; + } + } + do { uint x = void; @@ -45,7 +76,7 @@ if (radix >= 2 && radix <= 16) x = cast(uint)(value % radix); value /= radix; } - buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + 'a'); + buf[--i] = cast(char)((radix <= 10 || x < 10) ? x + '0' : x - 10 + baseChar); } while (value); return buf[i .. $]; } @@ -73,7 +104,7 @@ Params: Returns: The unsigned integer value as a string of characters */ -auto unsignedToTempString(uint radix = 10)(ulong value) @safe +auto unsignedToTempString(uint radix = 10)(ulong value) { // Need a buffer of 65 bytes for radix of 2 with room for // signedToTempString to possibly add a negative sign. @@ -85,11 +116,12 @@ auto unsignedToTempString(uint radix = 10)(ulong value) @safe unittest { - UnsignedStringBuf buf; + UnsignedStringBuf buf = void; assert(0.unsignedToTempString(buf) == "0"); assert(1.unsignedToTempString(buf) == "1"); assert(12.unsignedToTempString(buf) == "12"); assert(0x12ABCF .unsignedToTempString!16(buf) == "12abcf"); + assert(0x12ABCF .unsignedToTempString!(16, true)(buf) == "12ABCF"); assert(long.sizeof.unsignedToTempString(buf) == "8"); assert(uint.max.unsignedToTempString(buf) == "4294967295"); assert(ulong.max.unsignedToTempString(buf) == "18446744073709551615"); @@ -106,16 +138,17 @@ unittest // test bad radices assert(!is(typeof(100.unsignedToTempString!1(buf)))); assert(!is(typeof(100.unsignedToTempString!0(buf) == ""))); + assert(!is(typeof(100.unsignedToTempString!37(buf) == ""))); } -alias SignedStringBuf = char[20]; +alias SignedStringBuf = char[65]; -char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf) @safe +T[] signedToTempString(uint radix = 10, bool upperCase = false, T)(long value, return scope T[] buf) { bool neg = value < 0; if (neg) value = cast(ulong)-value; - auto r = unsignedToTempString!radix(value, buf); + auto r = unsignedToTempString!(radix, upperCase)(value, buf); if (neg) { // about to do a slice without a bounds check @@ -126,7 +159,7 @@ char[] signedToTempString(uint radix = 10)(long value, return scope char[] buf) return r; } -auto signedToTempString(uint radix = 10)(long value) @safe +auto signedToTempString(uint radix = 10)(long value) { bool neg = value < 0; if (neg) @@ -142,7 +175,7 @@ auto signedToTempString(uint radix = 10)(long value) @safe unittest { - SignedStringBuf buf; + SignedStringBuf buf = void; assert(0.signedToTempString(buf) == "0"); assert(1.signedToTempString(buf) == "1"); assert((-1).signedToTempString(buf) == "-1"); @@ -150,6 +183,7 @@ unittest assert((-12).signedToTempString(buf) == "-12"); assert(0x12ABCF .signedToTempString!16(buf) == "12abcf"); assert((-0x12ABCF) .signedToTempString!16(buf) == "-12abcf"); + assert((-0x12ABCF) .signedToTempString!(16, true)(buf) == "-12ABCF"); assert(long.sizeof.signedToTempString(buf) == "8"); assert(int.max.signedToTempString(buf) == "2147483647"); assert(int.min.signedToTempString(buf) == "-2147483648"); @@ -183,7 +217,7 @@ unittest * Returns: * number of digits */ -int numDigits(uint radix = 10)(ulong value) @safe if (radix >= 2 && radix <= 36) +int numDigits(uint radix = 10)(ulong value) if (radix >= 2 && radix <= 36) { int n = 1; while (1) diff --git a/runtime/druntime/src/core/lifetime.d b/runtime/druntime/src/core/lifetime.d index 5e339c041d1..2745d54e105 100644 --- a/runtime/druntime/src/core/lifetime.d +++ b/runtime/druntime/src/core/lifetime.d @@ -1570,7 +1570,11 @@ template forward(args...) alias fwd = arg; // (r)value else - @property auto fwd(){ pragma(inline, true); return move(arg); } + @property auto fwd() + { + version (DigitalMars) { /* @@BUG 23890@@ */ } else pragma(inline, true); + return move(arg); + } } alias Result = AliasSeq!(); diff --git a/runtime/druntime/src/core/stdc/assert_.d b/runtime/druntime/src/core/stdc/assert_.d index fc9402f6051..a7a54e873c2 100644 --- a/runtime/druntime/src/core/stdc/assert_.d +++ b/runtime/druntime/src/core/stdc/assert_.d @@ -60,7 +60,7 @@ else version (FreeBSD) /*** * Assert failure function in the FreeBSD C library. */ - void __assert(const(char)* exp, const(char)* file, uint line); + void __assert(const(char)* func, const(char)* file, uint line, const(char)* exp); } else version (NetBSD) { @@ -83,7 +83,7 @@ else version (DragonFlyBSD) /*** * Assert failure function in the DragonFlyBSD C library. */ - void __assert(const(char)* exp, const(char)* file, uint line); + void __assert(const(char)* func, const(char)* file, uint line, const(char)* exp); } else version (CRuntime_Glibc) { diff --git a/runtime/druntime/src/core/stdc/config.d b/runtime/druntime/src/core/stdc/config.d index 8033018bf66..3ab41f834da 100644 --- a/runtime/druntime/src/core/stdc/config.d +++ b/runtime/druntime/src/core/stdc/config.d @@ -259,11 +259,185 @@ else alias cpp_ptrdiff_t = ptrdiff_t; } -// ABI layout of native complex types. -private struct _Complex(T) +/** ABI layout of native complex types. + */ +struct _Complex(T) + if (is(T == float) || is(T == double) || is(T == c_long_double)) { - T re; - T im; + T re = 0; + T im = 0; + + // Construction +/+ https://issues.dlang.org/show_bug.cgi?id=23788 dmd codegen problem with constructors and _Complex!float + this(_Complex!float c) { re = c.re; im = c.im; } + this(_Complex!double c) { re = c.re; im = c.im; } + this(_Complex!c_long_double c) { re = c.re; im = c.im; } + + this(T re, T im) { this.re = re; this.im = im; } + + this(T re) { this.re = re; this.im = 0; } ++/ + // Cast + R opCast(R)() + if (is(R == _Complex!float) || is(R == _Complex!double) || is(R == _Complex!c_long_double)) + { + return R(this.re, this.im); + } + + // Assignment + + ref _Complex opAssign(_Complex!float c) { re = c.re; im = c.im; return this; } + ref _Complex opAssign(_Complex!double c) { re = c.re; im = c.im; return this; } + ref _Complex opAssign(_Complex!c_long_double c) { re = c.re; im = c.im; return this; } + + ref _Complex opAssign(T t) { re = t; im = 0; return this; } + + // Equals + + bool opEquals(_Complex!float c) { return re == c.re && im == c.im; } + bool opEquals(_Complex!double c) { return re == c.re && im == c.im; } + bool opEquals(_Complex!c_long_double c) { return re == c.re && im == c.im; } + + bool opEquals(T t) { return re == t && im == 0; } + + // Unary operators + + // +complex + _Complex opUnary(string op)() + if (op == "+") + { + return this; + } + + // -complex + _Complex opUnary(string op)() + if (op == "-") + { + return _Complex(-re, -im); + } + + // BINARY OPERATORS + + // complex op complex + _Complex!(CommonType!(T,R)) opBinary(string op, R)(_Complex!R z) + { + alias C = typeof(return); + auto w = C(this.re, this.im); + return w.opOpAssign!(op)(z); + } + + // complex op numeric + _Complex!(CommonType!(T,R)) opBinary(string op, R)(R r) + if (is(R : c_long_double)) + { + alias C = typeof(return); + auto w = C(this.re, this.im); + return w.opOpAssign!(op)(r); + } + + // numeric + complex, numeric * complex + _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) + if ((op == "+" || op == "*") && is(R : c_long_double)) + { + return opBinary!(op)(r); + } + + // numeric - complex + _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) + if (op == "-" && is(R : c_long_double)) + { + return _Complex(r - re, -im); + } + + // numeric / complex + _Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) + if (op == "/" && is(R : c_long_double)) + { + import core.math : fabs; + typeof(return) w = void; + if (fabs(re) < fabs(im)) + { + immutable ratio = re/im; + immutable rdivd = r/(re*ratio + im); + + w.re = rdivd*ratio; + w.im = -rdivd; + } + else + { + immutable ratio = im/re; + immutable rdivd = r/(re + im*ratio); + + w.re = rdivd; + w.im = -rdivd*ratio; + } + + return w; + } + + // OP-ASSIGN OPERATORS + + // complex += complex, complex -= complex + ref _Complex opOpAssign(string op, C)(C z) + if ((op == "+" || op == "-") && is(C R == _Complex!R)) + { + mixin ("re "~op~"= z.re;"); + mixin ("im "~op~"= z.im;"); + return this; + } + + // complex *= complex + ref _Complex opOpAssign(string op, C)(C z) + if (op == "*" && is(C R == _Complex!R)) + { + auto temp = re*z.re - im*z.im; + im = im*z.re + re*z.im; + re = temp; + return this; + } + + // complex /= complex + ref _Complex opOpAssign(string op, C)(C z) + if (op == "/" && is(C R == _Complex!R)) + { + import core.math : fabs; + if (fabs(z.re) < fabs(z.im)) + { + immutable ratio = z.re/z.im; + immutable denom = z.re*ratio + z.im; + + immutable temp = (re*ratio + im)/denom; + im = (im*ratio - re)/denom; + re = temp; + } + else + { + immutable ratio = z.im/z.re; + immutable denom = z.re + z.im*ratio; + + immutable temp = (re + im*ratio)/denom; + im = (im - re*ratio)/denom; + re = temp; + } + return this; + } + + // complex += numeric, complex -= numeric + ref _Complex opOpAssign(string op, U : T)(U a) + if (op == "+" || op == "-") + { + mixin ("re "~op~"= a;"); + return this; + } + + // complex *= numeric, complex /= numeric + ref _Complex opOpAssign(string op, U : T)(U a) + if (op == "*" || op == "/") + { + mixin ("re "~op~"= a;"); + mixin ("im "~op~"= a;"); + return this; + } // Helper properties. pragma(inline, true) @@ -289,6 +463,168 @@ enum __c_complex_real : _Complex!c_long_double; alias c_complex_float = __c_complex_float; alias c_complex_double = __c_complex_double; alias c_complex_real = __c_complex_real; + +private template CommonType(T, R) +{ + // Special kludge for Microsoft c_long_double + static if (is(T == c_long_double)) + alias CommonType = T; + else static if (is(R == c_long_double)) + alias CommonType = R; + else + alias CommonType = typeof(true ? T.init : R.init); +} + +/************ unittests ****************/ + +version (unittest) +{ + private: + + alias _cfloat = _Complex!float; + alias _cdouble = _Complex!double; + alias _creal = _Complex!c_long_double; + + T abs(T)(T t) => t < 0 ? -t : t; +} + +@safe pure nothrow unittest +{ + auto c1 = _cdouble(1.0, 1.0); + + // Check unary operations. + auto c2 = _cdouble(0.5, 2.0); + + assert(c2 == +c2); + + assert((-c2).re == -(c2.re)); + assert((-c2).im == -(c2.im)); + assert(c2 == -(-c2)); + + // Check complex-complex operations. + auto cpc = c1 + c2; + assert(cpc.re == c1.re + c2.re); + assert(cpc.im == c1.im + c2.im); + + auto cmc = c1 - c2; + assert(cmc.re == c1.re - c2.re); + assert(cmc.im == c1.im - c2.im); + + auto ctc = c1 * c2; + assert(ctc == _cdouble(-1.5, 2.5)); + + auto cdc = c1 / c2; + assert(abs(cdc.re - 0.5882352941177) < 1e-12); + assert(abs(cdc.im - -0.3529411764706) < 1e-12); + + // Check complex-real operations. + double a = 123.456; + + auto cpr = c1 + a; + assert(cpr.re == c1.re + a); + assert(cpr.im == c1.im); + + auto cmr = c1 - a; + assert(cmr.re == c1.re - a); + assert(cmr.im == c1.im); + + auto ctr = c1 * a; + assert(ctr.re == c1.re*a); + assert(ctr.im == c1.im*a); + + auto cdr = c1 / a; + assert(abs(cdr.re - 0.00810005184033) < 1e-12); + assert(abs(cdr.im - 0.00810005184033) < 1e-12); + + auto rpc = a + c1; + assert(rpc == cpr); + + auto rmc = a - c1; + assert(rmc.re == a-c1.re); + assert(rmc.im == -c1.im); + + auto rtc = a * c1; + assert(rtc == ctr); + + auto rdc = a / c1; + assert(abs(rdc.re - 61.728) < 1e-12); + assert(abs(rdc.im - -61.728) < 1e-12); + + rdc = a / c2; + assert(abs(rdc.re - 14.5242352941) < 1e-10); + assert(abs(rdc.im - -58.0969411765) < 1e-10); + + // Check operations between different complex types. + auto cf = _cfloat(1.0, 1.0); + auto cr = _creal(1.0, 1.0); + auto c1pcf = c1 + cf; + auto c1pcr = c1 + cr; + static assert(is(typeof(c1pcf) == _cdouble)); + static assert(is(typeof(c1pcr) == _creal)); + assert(c1pcf.re == c1pcr.re); + assert(c1pcf.im == c1pcr.im); + + auto c1c = c1; + auto c2c = c2; + + c1c /= c1; + assert(abs(c1c.re - 1.0) < 1e-10); + assert(abs(c1c.im - 0.0) < 1e-10); + + c1c = c1; + c1c /= c2; + assert(abs(c1c.re - 0.5882352941177) < 1e-12); + assert(abs(c1c.im - -0.3529411764706) < 1e-12); + + c2c /= c1; + assert(abs(c2c.re - 1.25) < 1e-11); + assert(abs(c2c.im - 0.75) < 1e-12); + + c2c = c2; + c2c /= c2; + assert(abs(c2c.re - 1.0) < 1e-11); + assert(abs(c2c.im - 0.0) < 1e-12); +} + +@safe pure nothrow unittest +{ + // Initialization + _cdouble a = _cdouble(1, 0); + assert(a.re == 1 && a.im == 0); + _cdouble b = _cdouble(1.0, 0); + assert(b.re == 1.0 && b.im == 0); +// _cdouble c = _creal(1.0, 2); +// assert(c.re == 1.0 && c.im == 2); +} + +@safe pure nothrow unittest +{ + // Assignments and comparisons + _cdouble z; + + z = 1; + assert(z == 1); + assert(z.re == 1.0 && z.im == 0.0); + + z = 2.0; + assert(z == 2.0); + assert(z.re == 2.0 && z.im == 0.0); + + z = 1.0L; + assert(z == 1.0L); + assert(z.re == 1.0 && z.im == 0.0); + + auto w = _creal(1.0, 1.0); + z = w; + assert(z == w); + assert(z.re == 1.0 && z.im == 1.0); + + auto c = _cfloat(2.0, 2.0); + z = c; + assert(z == c); + assert(z.re == 2.0 && z.im == 2.0); +} + } diff --git a/runtime/druntime/src/core/sync/condition.d b/runtime/druntime/src/core/sync/condition.d index ddd04ae0576..afcfd744f0a 100644 --- a/runtime/druntime/src/core/sync/condition.d +++ b/runtime/druntime/src/core/sync/condition.d @@ -84,7 +84,8 @@ class Condition /// ditto this( shared Mutex m ) shared nothrow @safe @nogc { - this(m, true); + import core.atomic : atomicLoad; + this(atomicLoad(m), true); } // @@ -117,7 +118,15 @@ class Condition } else version (Posix) { - m_assocMutex = m; + static if (is(Q == shared)) + { + import core.atomic : atomicLoad; + m_assocMutex = atomicLoad(m); + } + else + { + m_assocMutex = m; + } static if ( is( typeof( pthread_condattr_setclock ) ) ) { () @trusted @@ -183,7 +192,8 @@ class Condition /// ditto @property shared(Mutex) mutex() shared { - return m_assocMutex; + import core.atomic : atomicLoad; + return atomicLoad(m_assocMutex); } // undocumented function for internal use @@ -195,7 +205,8 @@ class Condition // ditto final @property shared(Mutex) mutex_nothrow() shared pure nothrow @safe @nogc { - return m_assocMutex; + import core.atomic : atomicLoad; + return atomicLoad(m_assocMutex); } //////////////////////////////////////////////////////////////////////////// diff --git a/runtime/druntime/src/core/thread/package.d b/runtime/druntime/src/core/thread/package.d index 71b0237c114..d81ebbdcc82 100644 --- a/runtime/druntime/src/core/thread/package.d +++ b/runtime/druntime/src/core/thread/package.d @@ -18,3 +18,42 @@ public import core.thread.threadbase; public import core.thread.threadgroup; public import core.thread.types; public import core.thread.context; + + +// this test is here to avoid a cyclic dependency between +// core.thread and core.atomic +unittest +{ + import core.atomic; + + // Use heap memory to ensure an optimizing + // compiler doesn't put things in registers. + uint* x = new uint(); + bool* f = new bool(); + uint* r = new uint(); + + auto thr = new Thread(() + { + while (!*f) + { + } + + atomicFence(); + + *r = *x; + }); + + thr.start(); + + *x = 42; + + atomicFence(); + + *f = true; + + atomicFence(); + + thr.join(); + + assert(*r == 42); +} diff --git a/runtime/druntime/src/core/thread/types.d b/runtime/druntime/src/core/thread/types.d index eb84ad74b48..991299b808d 100644 --- a/runtime/druntime/src/core/thread/types.d +++ b/runtime/druntime/src/core/thread/types.d @@ -39,11 +39,20 @@ version (GNU) else enum isStackGrowingDown = false; } -else +else version (LDC) { - // this should be true for most architectures + // The only LLVM targets as of LLVM 16 with stack growing *upwards* are + // apparently NVPTX and AMDGPU, both without druntime support. + // Note that there's an analogous `version = StackGrowsDown` in + // core.thread.fiber. enum isStackGrowingDown = true; } +else +{ + version (X86) enum isStackGrowingDown = true; + else version (X86_64) enum isStackGrowingDown = true; + else static assert(0, "It is undefined how the stack grows on this architecture."); +} package { diff --git a/runtime/druntime/src/core/time.d b/runtime/druntime/src/core/time.d index 8d508755c7d..be941e2abcd 100644 --- a/runtime/druntime/src/core/time.d +++ b/runtime/druntime/src/core/time.d @@ -18,7 +18,7 @@ $(LEADINGROW Types) $(TR $(TDNW $(LREF Duration)) $(TD Represents a duration of time of weeks or less (kept internally as hnsecs). (e.g. 22 days or 700 seconds).)) - $(TR $(TDNW $(LREF TickDuration)) $(TD Represents a duration of time in + $(TR $(TDNW $(LREF TickDuration)) $(TD $(RED DEPRECATED) Represents a duration of time in system clock ticks, using the highest precision that the system provides.)) $(TR $(TDNW $(LREF MonoTime)) $(TD Represents a monotonic timestamp in system clock ticks, using the highest precision that the system provides.)) @@ -682,21 +682,21 @@ public: $(TR $(TD Duration) $(TD +) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD -) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD %) $(TD Duration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD +) $(TD TickDuration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD -) $(TD TickDuration) $(TD -->) $(TD Duration)) ) Params: rhs = The duration to add to or subtract from this $(D Duration). +/ - Duration opBinary(string op, D)(D rhs) const nothrow @nogc - if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) || - ((op == "+" || op == "-") && is(immutable D == immutable TickDuration))) + Duration opBinary(string op)(const Duration rhs) const nothrow @nogc + if (op == "+" || op == "-" || op == "%") { - static if (is(immutable D == immutable Duration)) - return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs")); - else - return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs")); + return Duration(mixin("_hnsecs " ~ op ~ " rhs._hnsecs")); + } + + deprecated Duration opBinary(string op)(const TickDuration rhs) const nothrow @nogc + if (op == "+" || op == "-") + { + return Duration(mixin("_hnsecs " ~ op ~ " rhs.hnsecs")); } version (CoreUnittest) unittest @@ -733,7 +733,13 @@ public: assert((cast(D)Duration(-7)) - (cast(E)Duration(-5)) == Duration(-2)); assert((cast(D)Duration(-7)) % (cast(E)Duration(5)) == Duration(-2)); } + } + } + version (CoreUnittest) deprecated unittest + { + foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) + { foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { assertApprox((cast(D)Duration(5)) + cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80)); @@ -761,6 +767,8 @@ public: /++ + $(RED TickDuration is Deprecated) + Adds or subtracts two durations. The legal types of arithmetic for $(D Duration) using this operator are @@ -774,14 +782,14 @@ public: lhs = The $(D TickDuration) to add to this $(D Duration) or to subtract this $(D Duration) from. +/ - Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc + deprecated Duration opBinaryRight(string op, D)(D lhs) const nothrow @nogc if ((op == "+" || op == "-") && is(immutable D == immutable TickDuration)) { return Duration(mixin("lhs.hnsecs " ~ op ~ " _hnsecs")); } - version (CoreUnittest) unittest + version (CoreUnittest) deprecated unittest { foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) { @@ -821,21 +829,22 @@ public: $(TR $(TD Duration) $(TD +) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD -) $(TD Duration) $(TD -->) $(TD Duration)) $(TR $(TD Duration) $(TD %) $(TD Duration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD +) $(TD TickDuration) $(TD -->) $(TD Duration)) - $(TR $(TD Duration) $(TD -) $(TD TickDuration) $(TD -->) $(TD Duration)) ) Params: rhs = The duration to add to or subtract from this $(D Duration). +/ - ref Duration opOpAssign(string op, D)(const scope D rhs) nothrow @nogc - if (((op == "+" || op == "-" || op == "%") && is(immutable D == immutable Duration)) || - ((op == "+" || op == "-") && is(immutable D == immutable TickDuration))) + ref Duration opOpAssign(string op)(const Duration rhs) nothrow @nogc + if (op == "+" || op == "-" || op == "%") { - static if (is(immutable D == immutable Duration)) - mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;"); - else - mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;"); + mixin("_hnsecs " ~ op ~ "= rhs._hnsecs;"); + return this; + } + + deprecated ref Duration opOpAssign(string op)(const TickDuration rhs) nothrow @nogc + if (op == "+" || op == "-") + { + mixin("_hnsecs " ~ op ~ "= rhs.hnsecs;"); return this; } @@ -850,13 +859,6 @@ public: throw new AssertError("op assign failed", __FILE__, line); } - static void test2(string op, E) - (Duration actual, in E rhs, Duration lower, Duration upper, size_t line = __LINE__) - { - assertApprox(mixin("actual " ~ op ~ " rhs"), lower, upper, "op failed", line); - assertApprox(actual, lower, upper, "op assign failed", line); - } - foreach (E; AliasSeq!(Duration, const Duration, immutable Duration)) { test1!"+="(Duration(5), (cast(E)Duration(7)), Duration(12)); @@ -888,6 +890,26 @@ public: test1!"%="(Duration(-7), (cast(E)Duration(-5)), Duration(-2)); } + foreach (D; AliasSeq!(const Duration, immutable Duration)) + { + foreach (E; AliasSeq!(Duration, const Duration, immutable Duration)) + { + D lhs = D(120); + E rhs = E(120); + static assert(!__traits(compiles, lhs += rhs), D.stringof ~ " " ~ E.stringof); + } + } + } + + version (CoreUnittest) deprecated unittest + { + static void test2(string op, E) + (Duration actual, in E rhs, Duration lower, Duration upper, size_t line = __LINE__) + { + assertApprox(mixin("actual " ~ op ~ " rhs"), lower, upper, "op failed", line); + assertApprox(actual, lower, upper, "op assign failed", line); + } + foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { test2!"+="(Duration(5), cast(T)TickDuration.from!"usecs"(7), Duration(70), Duration(80)); @@ -913,8 +935,7 @@ public: foreach (D; AliasSeq!(const Duration, immutable Duration)) { - foreach (E; AliasSeq!(Duration, const Duration, immutable Duration, - TickDuration, const TickDuration, immutable TickDuration)) + foreach (E; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { D lhs = D(120); E rhs = E(120); @@ -1170,19 +1191,21 @@ public: /++ + $(RED TickDuration is Deprecated) + Returns a $(LREF TickDuration) with the same number of hnsecs as this $(D Duration). Note that the conventional way to convert between $(D Duration) and $(D TickDuration) is using $(REF to, std,conv), e.g.: $(D duration.to!TickDuration()) +/ - TickDuration opCast(T)() const nothrow @nogc + deprecated TickDuration opCast(T)() const nothrow @nogc if (is(immutable T == immutable TickDuration)) { return TickDuration.from!"hnsecs"(_hnsecs); } - version (CoreUnittest) unittest + version (CoreUnittest) deprecated unittest { foreach (D; AliasSeq!(Duration, const Duration, immutable Duration)) { @@ -1762,6 +1785,8 @@ version (CoreUnittest) @safe pure nothrow @nogc unittest } /++ + $(RED TickDuration is DEPRECATED) + Converts a $(D TickDuration) to the given units as either an integral value or a floating point value. @@ -1773,6 +1798,7 @@ version (CoreUnittest) @safe pure nothrow @nogc unittest td = The TickDuration to convert +/ +deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead") T to(string units, T, D)(D td) @safe pure nothrow @nogc if (is(immutable D == immutable TickDuration) && (units == "seconds" || @@ -1804,7 +1830,7 @@ T to(string units, T, D)(D td) @safe pure nothrow @nogc } /// -unittest +deprecated unittest { auto t = TickDuration.from!"seconds"(1000); @@ -1816,7 +1842,7 @@ unittest assert(fabs(td - 1000) < 0.001); } -unittest +deprecated unittest { void testFun(string U)() { auto t1v = 1000; @@ -2756,22 +2782,24 @@ unittest /++ - $(RED Warning: TickDuration will be deprecated in the near future (once all - uses of it in Phobos have been deprecated). Please use + $(RED Warning: TickDuration is deprecated. Please use $(LREF MonoTime) for the cases where a monotonic timestamp is needed and $(LREF Duration) when a duration is needed, rather than using - TickDuration. It has been decided that TickDuration is too confusing - (e.g. it conflates a monotonic timestamp and a duration in monotonic - clock ticks) and that having multiple duration types is too awkward - and confusing.) + TickDuration.) Represents a duration of time in system clock ticks. The system clock ticks are the ticks of the system clock at the highest precision that the system provides. +/ +deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead") struct TickDuration { +deprecated: + private static TickDuration TDRvalueOf(TickDuration td) + { + return td; + } /++ The number of ticks that the system clock has in one second. @@ -2811,14 +2839,14 @@ struct TickDuration version (CoreUnittest) unittest { - assert(zero == TickDuration(0)); - assert(TickDuration.max == TickDuration(long.max)); - assert(TickDuration.min == TickDuration(long.min)); - assert(TickDuration.min < TickDuration.zero); - assert(TickDuration.zero < TickDuration.max); - assert(TickDuration.min < TickDuration.max); - assert(TickDuration.min - TickDuration(1) == TickDuration.max); - assert(TickDuration.max + TickDuration(1) == TickDuration.min); + assert((zero == TickDuration(0)) == true); + assert((TickDuration.max == TickDuration(long.max)) == true); + assert((TickDuration.min == TickDuration(long.min)) == true); + assert((TickDuration.min < TickDuration.zero) == true); + assert((TickDuration.zero < TickDuration.max) == true); + assert((TickDuration.min < TickDuration.max) == true); + assert((TickDuration.min - TickDuration(1) == TickDuration.max) == true); + assert((TickDuration.max + TickDuration(1) == TickDuration.min) == true); } @@ -3040,12 +3068,12 @@ struct TickDuration { auto a = TickDuration.currSystemTick; auto result = a += cast(T)TickDuration.currSystemTick; - assert(a == result); + assert((a == result) == true); assert(a.to!("seconds", real)() >= 0); auto b = TickDuration.currSystemTick; result = b -= cast(T)TickDuration.currSystemTick; - assert(b == result); + assert((b == result) == true); assert(b.to!("seconds", real)() <= 0); foreach (U; AliasSeq!(const TickDuration, immutable TickDuration)) @@ -3104,11 +3132,11 @@ struct TickDuration { foreach (T; AliasSeq!(TickDuration, const TickDuration, immutable TickDuration)) { - assert(-(cast(T)TickDuration(7)) == TickDuration(-7)); - assert(-(cast(T)TickDuration(5)) == TickDuration(-5)); - assert(-(cast(T)TickDuration(-7)) == TickDuration(7)); - assert(-(cast(T)TickDuration(-5)) == TickDuration(5)); - assert(-(cast(T)TickDuration(0)) == TickDuration(0)); + assert((-(cast(T)TickDuration(7)) == TickDuration(-7)) == true); + assert((-(cast(T)TickDuration(5)) == TickDuration(-5)) == true); + assert((-(cast(T)TickDuration(-7)) == TickDuration(7)) == true); + assert((-(cast(T)TickDuration(-5)) == TickDuration(5)) == true); + assert((-(cast(T)TickDuration(0)) == TickDuration(0)) == true); } } @@ -3130,9 +3158,9 @@ struct TickDuration { T t = TickDuration.currSystemTick; U u = t; - assert(t == u); - assert(rvalueOf(t) == u); - assert(t == rvalueOf(u)); + assert((t == u) == true); + assert((TDRvalueOf(t) == u) == true); + assert((t == TDRvalueOf(u)) == true); } } @@ -3142,20 +3170,20 @@ struct TickDuration { T t = TickDuration.currSystemTick; U u = t + t; - assert(t < u); - assert(t <= t); - assert(u > t); - assert(u >= u); - - assert(rvalueOf(t) < u); - assert(rvalueOf(t) <= t); - assert(rvalueOf(u) > t); - assert(rvalueOf(u) >= u); - - assert(t < rvalueOf(u)); - assert(t <= rvalueOf(t)); - assert(u > rvalueOf(t)); - assert(u >= rvalueOf(u)); + assert((t < u) == true); + assert((t <= t) == true); + assert((u > t) == true); + assert((u >= u) == true); + + assert((TDRvalueOf(t) < u) == true); + assert((TDRvalueOf(t) <= t) == true); + assert((TDRvalueOf(u) > t) == true); + assert((TDRvalueOf(u) >= u) == true); + + assert((t < TDRvalueOf(u)) == true); + assert((t <= TDRvalueOf(t)) == true); + assert((u > TDRvalueOf(t)) == true); + assert((u >= TDRvalueOf(u)) == true); } } } @@ -3186,7 +3214,7 @@ struct TickDuration TickDuration t1 = curr; immutable t2 = curr + curr; t1 *= 2; - assert(t1 == t2); + assert((t1 == t2) == true); t1 = curr; t1 *= 2.0; @@ -3195,7 +3223,7 @@ struct TickDuration t1 = curr; t1 *= 2.1; - assert(t1 > t2); + assert((t1 > t2) == true); foreach (T; AliasSeq!(const TickDuration, immutable TickDuration)) { @@ -3237,7 +3265,7 @@ struct TickDuration immutable t1 = curr; TickDuration t2 = curr + curr; t2 /= 2; - assert(t1 == t2); + assert((t1 == t2) == true); t2 = curr + curr; t2 /= 2.0; @@ -3246,7 +3274,7 @@ struct TickDuration t2 = curr + curr; t2 /= 2.1; - assert(t1 > t2); + assert((t1 > t2) == true); _assertThrown!TimeException(t2 /= 0); @@ -3284,10 +3312,10 @@ struct TickDuration { T t1 = TickDuration.currSystemTick; T t2 = t1 + t1; - assert(t1 * 2 == t2); + assert((t1 * 2 == t2) == true); immutable tol = TickDuration(cast(long)(_abs(t1.length) * double.epsilon * 2.0)); assertApprox(t1 * 2.0, t2 - tol, t2 + tol); - assert(t1 * 2.1 > t2); + assert((t1 * 2.1 > t2) == true); } } @@ -3323,12 +3351,12 @@ struct TickDuration { T t1 = TickDuration.currSystemTick; T t2 = t1 + t1; - assert(t2 / 2 == t1); + assert((t2 / 2 == t1) == true); immutable tol = TickDuration(cast(long)(_abs(t2.length) * double.epsilon / 2.0)); assertApprox(t2 / 2.0, t1 - tol, t1 + tol); - assert(t2 / 2.1 < t1); + assert((t2 / 2.1 < t1) == true); - _assertThrown!TimeException(t2 / 0); + _assertThrownDep!TimeException(t2 / 0); } } @@ -3430,7 +3458,6 @@ struct TickDuration } } - /++ Generic way of converting between two time units. Conversions to smaller units use truncating division. Years and months can be converted to each @@ -3641,6 +3668,7 @@ Duration abs(Duration duration) @safe pure nothrow @nogc } /++ Ditto +/ +deprecated("TickDuration has been deprecated, please use Duration or MonoTime instead") TickDuration abs(TickDuration duration) @safe pure nothrow @nogc { return TickDuration(_abs(duration.length)); @@ -3650,9 +3678,12 @@ unittest { assert(abs(dur!"msecs"(5)) == dur!"msecs"(5)); assert(abs(dur!"msecs"(-5)) == dur!"msecs"(5)); +} - assert(abs(TickDuration(17)) == TickDuration(17)); - assert(abs(TickDuration(-17)) == TickDuration(17)); +deprecated unittest +{ + assert((abs(TickDuration(17)) == TickDuration(17)) == true); + assert((abs(TickDuration(-17)) == TickDuration(17)) == true); } @@ -3987,6 +4018,28 @@ unittest } } +version (CoreUnittest) deprecated void _assertThrownDep(T : Throwable = Exception, E) + (lazy E expression, + string msg = null, + string file = __FILE__, + size_t line = __LINE__) +{ + bool thrown = false; + + try + expression(); + catch (T t) + thrown = true; + + if (!thrown) + { + immutable tail = msg.length == 0 ? "." : ": " ~ msg; + + throw new AssertError("assertThrown() failed: No " ~ T.stringof ~ " was thrown" ~ tail, file, line); + } +} + + version (CoreUnittest) void assertApprox(D, E)(D actual, E lower, @@ -4001,7 +4054,7 @@ version (CoreUnittest) void assertApprox(D, E)(D actual, throw new AssertError(msg ~ ": upper: " ~ actual.toString(), __FILE__, line); } -version (CoreUnittest) void assertApprox(D, E)(D actual, +version (CoreUnittest) deprecated void assertApprox(D, E)(D actual, E lower, E upper, string msg = "unittest failure", diff --git a/runtime/druntime/src/importc.h b/runtime/druntime/src/importc.h index 3db162120dd..0b2f4c6958c 100644 --- a/runtime/druntime/src/importc.h +++ b/runtime/druntime/src/importc.h @@ -29,6 +29,7 @@ #define __asm asm #define __inline__ inline #define __inline inline +#define __volatile__ volatile /******************** * Clang nullability extension used by macOS headers. @@ -73,6 +74,9 @@ */ #define __extension__ /* ignore it, as ImportC doesn't do warnings */ +#define __builtin_isnan(x) isnan(x) +#define __builtin_isfinite(x) finite(x) + /******************************** * __has_extension is a clang thing: * https://clang.llvm.org/docs/LanguageExtensions.html @@ -100,11 +104,21 @@ #endif #if __FreeBSD__ +#define __volatile volatile +#define __sync_synchronize() +#define __sync_swap(A, B) 1 #endif #if _MSC_VER //#undef _Post_writable_size //#define _Post_writable_size(x) // consider #include +#define _CRT_INSECURE_DEPRECATE(x) +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#define _CRT_SECURE_NO_WARNINGS 1 +#define __ptr32 +#define __ptr64 +#define __unaligned +#define _NO_CRT_STDIO_INLINE 1 #endif /**************************** diff --git a/runtime/druntime/src/ldc/eh_msvc.d b/runtime/druntime/src/ldc/eh_msvc.d index 9ea66485e7c..a19079c9233 100644 --- a/runtime/druntime/src/ldc/eh_msvc.d +++ b/runtime/druntime/src/ldc/eh_msvc.d @@ -99,7 +99,7 @@ struct CxxExceptionInfo extern(C) int _d_isbaseof(ClassInfo oc, ClassInfo c); // error and exit -extern(C) void fatalerror(in char* format, ...) +extern(C) void fatalerror(const(char)* format, ...) { import core.stdc.stdarg; import core.stdc.stdio; diff --git a/runtime/druntime/src/object.d b/runtime/druntime/src/object.d index 62b0234a7dc..28c6db1b009 100644 --- a/runtime/druntime/src/object.d +++ b/runtime/druntime/src/object.d @@ -4588,12 +4588,15 @@ public import core.internal.entrypoint : _d_cmain; public import core.internal.array.appending : _d_arrayappendT; version (D_ProfileGC) +{ public import core.internal.array.appending : _d_arrayappendTTrace; + public import core.internal.array.concatenation : _d_arraycatnTXTrace; +} public import core.internal.array.appending : _d_arrayappendcTXImpl; public import core.internal.array.comparison : __cmp; public import core.internal.array.equality : __equals; public import core.internal.array.casting: __ArrayCast; -public import core.internal.array.concatenation : _d_arraycatnTXImpl; +public import core.internal.array.concatenation : _d_arraycatnTX; public import core.internal.array.construction : _d_arrayctor; public import core.internal.array.construction : _d_arraysetctor; public import core.internal.array.arrayassign : _d_arrayassign_l; diff --git a/runtime/druntime/src/rt/aApply.d b/runtime/druntime/src/rt/aApply.d index 5d5ddb34740..c59d9dc1234 100644 --- a/runtime/druntime/src/rt/aApply.d +++ b/runtime/druntime/src/rt/aApply.d @@ -71,7 +71,7 @@ Params: Returns: non-zero when the loop was exited through a `break` */ -extern (C) int _aApplycd1(in char[] aa, dg_t dg) +extern (C) int _aApplycd1(scope const(char)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -132,7 +132,7 @@ unittest } /// ditto -extern (C) int _aApplywd1(in wchar[] aa, dg_t dg) +extern (C) int _aApplywd1(scope const(wchar)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -193,7 +193,7 @@ unittest } /// ditto -extern (C) int _aApplycw1(in char[] aa, dg_t dg) +extern (C) int _aApplycw1(scope const(char)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -267,7 +267,7 @@ unittest } /// ditto -extern (C) int _aApplywc1(in wchar[] aa, dg_t dg) +extern (C) int _aApplywc1(scope const(wchar)[] aa, dg_t dg) { int result; size_t len = aa.length; @@ -347,7 +347,7 @@ unittest } /// ditto -extern (C) int _aApplydc1(in dchar[] aa, dg_t dg) +extern (C) int _aApplydc1(scope const(dchar)[] aa, dg_t dg) { int result; @@ -423,7 +423,7 @@ unittest } /// ditto -extern (C) int _aApplydw1(in dchar[] aa, dg_t dg) +extern (C) int _aApplydw1(scope const(dchar)[] aa, dg_t dg) { int result; @@ -508,7 +508,7 @@ extern (D) alias dg2_t = int delegate(void* i, void* c); /** Variants of _aApplyXXX that include a loop index. */ -extern (C) int _aApplycd2(in char[] aa, dg2_t dg) +extern (C) int _aApplycd2(scope const(char)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -576,7 +576,7 @@ unittest } /// ditto -extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplywd2(scope const(wchar)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -644,7 +644,7 @@ unittest } /// ditto -extern (C) int _aApplycw2(in char[] aa, dg2_t dg) +extern (C) int _aApplycw2(scope const(char)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -723,7 +723,7 @@ unittest } /// ditto -extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplywc2(scope const(wchar)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -808,7 +808,7 @@ unittest } /// ditto -extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplydc2(scope const(dchar)[] aa, dg2_t dg) { int result; size_t len = aa.length; @@ -888,7 +888,7 @@ unittest } /// ditto -extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplydw2(scope const(dchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplydw2(), len = %d\n", aa.length); diff --git a/runtime/druntime/src/rt/aApplyR.d b/runtime/druntime/src/rt/aApplyR.d index ce3bb9eaf70..560025c636d 100644 --- a/runtime/druntime/src/rt/aApplyR.d +++ b/runtime/druntime/src/rt/aApplyR.d @@ -34,7 +34,7 @@ Params: Returns: non-zero when the loop was exited through a `break` */ -extern (C) int _aApplyRcd1(in char[] aa, dg_t dg) +extern (C) int _aApplyRcd1(scope const(char)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRcd1(), len = %d\n", aa.length); @@ -107,7 +107,7 @@ unittest } /// ditto -extern (C) int _aApplyRwd1(in wchar[] aa, dg_t dg) +extern (C) int _aApplyRwd1(scope const(wchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRwd1(), len = %d\n", aa.length); @@ -170,7 +170,7 @@ unittest } /// ditto -extern (C) int _aApplyRcw1(in char[] aa, dg_t dg) +extern (C) int _aApplyRcw1(scope const(char)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRcw1(), len = %d\n", aa.length); @@ -256,7 +256,7 @@ unittest } /// ditto -extern (C) int _aApplyRwc1(in wchar[] aa, dg_t dg) +extern (C) int _aApplyRwc1(scope const(wchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRwc1(), len = %d\n", aa.length); @@ -340,7 +340,7 @@ unittest } /// ditto -extern (C) int _aApplyRdc1(in dchar[] aa, dg_t dg) +extern (C) int _aApplyRdc1(scope const(dchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRdc1(), len = %d\n", aa.length); @@ -418,7 +418,7 @@ unittest } /// ditto -extern (C) int _aApplyRdw1(in dchar[] aa, dg_t dg) +extern (C) int _aApplyRdw1(scope const(dchar)[] aa, dg_t dg) { int result; debug(apply) printf("_aApplyRdw1(), len = %d\n", aa.length); @@ -502,7 +502,7 @@ extern (D) alias dg2_t = int delegate(void* i, void* c); /** Variants of _aApplyRXXX that include a loop index. */ -extern (C) int _aApplyRcd2(in char[] aa, dg2_t dg) +extern (C) int _aApplyRcd2(scope const(char)[] aa, dg2_t dg) { int result; size_t i; size_t len = aa.length; @@ -578,7 +578,7 @@ unittest } /// ditto -extern (C) int _aApplyRwd2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplyRwd2(scope const(wchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRwd2(), len = %d\n", aa.length); @@ -643,7 +643,7 @@ unittest } /// ditto -extern (C) int _aApplyRcw2(in char[] aa, dg2_t dg) +extern (C) int _aApplyRcw2(scope const(char)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRcw2(), len = %d\n", aa.length); @@ -731,7 +731,7 @@ unittest } /// ditto -extern (C) int _aApplyRwc2(in wchar[] aa, dg2_t dg) +extern (C) int _aApplyRwc2(scope const(wchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRwc2(), len = %d\n", aa.length); @@ -817,7 +817,7 @@ unittest } /// ditto -extern (C) int _aApplyRdc2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplyRdc2(scope const(dchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRdc2(), len = %d\n", aa.length); @@ -896,7 +896,7 @@ unittest } /// ditto -extern (C) int _aApplyRdw2(in dchar[] aa, dg2_t dg) +extern (C) int _aApplyRdw2(scope const(dchar)[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplyRdw2(), len = %d\n", aa.length); diff --git a/runtime/druntime/src/rt/lifetime.d b/runtime/druntime/src/rt/lifetime.d index 13d3c9132ae..6405cee0017 100644 --- a/runtime/druntime/src/rt/lifetime.d +++ b/runtime/druntime/src/rt/lifetime.d @@ -1237,7 +1237,7 @@ extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak } /// ditto -extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak +extern (C) void* _d_newitemT(const TypeInfo _ti) pure nothrow @weak { import core.stdc.string; auto p = _d_newitemU(_ti); @@ -1246,7 +1246,7 @@ extern (C) void* _d_newitemT(in TypeInfo _ti) pure nothrow @weak } /// Same as above, for item with non-zero initializer. -extern (C) void* _d_newitemiT(in TypeInfo _ti) pure nothrow @weak +extern (C) void* _d_newitemiT(const TypeInfo _ti) pure nothrow @weak { import core.stdc.string; auto p = _d_newitemU(_ti); @@ -1326,7 +1326,7 @@ extern (C) CollectHandler rt_getCollectHandler() /** * */ -extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, in void[] segment) nothrow +extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, scope const(void)[] segment) nothrow { if (attr & BlkAttr.STRUCTFINAL) { @@ -2271,148 +2271,6 @@ extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak return x; } - -/** -Concatenate two arrays into a new array - ---- -void main() -{ - int[] x = [10, 20, 30]; - int[] y = [40, 50]; - int[] c = x ~ y; // _d_arraycatT(typeid(int[]), (cast(byte*) x)[0..x.length], (cast(byte*) y)[0..y.length]); -} ---- - -Params: - ti = type that the two arrays share - x = left hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length - y = right hand side array casted to `byte[]`. Despite this cast, its `.length` is original element length, not byte length -Returns: - resulting concatenated array, with `.length` equal to new element length despite `byte` type -*/ -extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y) @weak -out (result) -{ - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d => %d,%p)\n", x.length, x.ptr, y.length, y.ptr, sizeelem, result.length, result.ptr); - assert(result.length == x.length + y.length); - - // If a postblit is involved, the contents of result might rightly differ - // from the bitwise concatenation of x and y. - if (!hasPostblit(tinext)) - { - for (size_t i = 0; i < x.length * sizeelem; i++) - assert((cast(byte*)result)[i] == (cast(byte*)x)[i]); - for (size_t i = 0; i < y.length * sizeelem; i++) - assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]); - } - - size_t cap = GC.sizeOf(result.ptr); - assert(!cap || cap > result.length * sizeelem); -} -do -{ - import core.stdc.string; - version (none) - { - /* Cannot use this optimization because: - * char[] a, b; - * char c = 'a'; - * b = a ~ c; - * c = 'b'; - * will change the contents of b. - */ - if (!y.length) - return x; - if (!x.length) - return y; - } - - auto tinext = unqualify(ti.next); - auto sizeelem = tinext.tsize; // array element size - debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem); - size_t xlen = x.length * sizeelem; - size_t ylen = y.length * sizeelem; - size_t len = xlen + ylen; - - if (!len) - return null; - - auto info = __arrayAlloc(len, ti, tinext); - byte* p = cast(byte*)__arrayStart(info); - p[len] = 0; // guessing this is to optimize for null-terminated arrays? - memcpy(p, x.ptr, xlen); - memcpy(p + xlen, y.ptr, ylen); - // do postblit processing - __doPostblit(p, xlen + ylen, tinext); - - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, len, isshared, tinext); - return p[0 .. x.length + y.length]; -} - - -/** -Concatenate multiple arrays at once - -This is more efficient than repeatedly concatenating pairs of arrays because the total size is known in advance. - -``` -void main() -{ - int[] a, b, c; - int[] res = a ~ b ~ c; - // _d_arraycatnTX(typeid(int[]), - // [(cast(byte*)a.ptr)[0..a.length], (cast(byte*)b.ptr)[0..b.length], (cast(byte*)c.ptr)[0..c.length]]); -} -``` - -Params: - ti = type of arrays to concatenate and resulting array - arrs = array of arrays to concatenate, cast to `byte[]` while keeping `.length` the same - -Returns: - newly created concatenated array, `.length` equal to the total element length despite `void` type -*/ -extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @weak -{ - import core.stdc.string; - - size_t length; - auto tinext = unqualify(ti.next); - auto size = tinext.tsize; // array element size - - foreach (b; arrs) - length += b.length; - - if (!length) - return null; - - auto allocsize = length * size; - auto info = __arrayAlloc(allocsize, ti, tinext); - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, allocsize, isshared, tinext); - void *a = __arrayStart (info); - - size_t j = 0; - foreach (b; arrs) - { - if (b.length) - { - memcpy(a + j, b.ptr, b.length * size); - j += b.length * size; - } - } - - // do postblit processing - __doPostblit(a, j, tinext); - - return a[0..length]; -} - - /** Allocate an array literal diff --git a/runtime/druntime/src/rt/profilegc.d b/runtime/druntime/src/rt/profilegc.d index 45e0d51b711..b97a5c5437b 100644 --- a/runtime/druntime/src/rt/profilegc.d +++ b/runtime/druntime/src/rt/profilegc.d @@ -15,6 +15,7 @@ module rt.profilegc; private: +import core.stdc.errno; import core.stdc.stdio; import core.stdc.stdlib; import core.stdc.string; @@ -151,7 +152,7 @@ shared static ~this() { qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp); - FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename).ptr, "w"); + FILE* fp = logfilename == "\0" ? stdout : fopen((logfilename).ptr, "w"); if (fp) { fprintf(fp, "bytes allocated, allocations, type, function, file:line\n"); @@ -165,6 +166,12 @@ shared static ~this() fclose(fp); } else - fprintf(stderr, "cannot write profilegc log file '%.*s'", cast(int) logfilename.length, logfilename.ptr); + { + const err = errno; + fprintf(stderr, "cannot write profilegc log file '%.*s' (errno=%d)", + cast(int) logfilename.length, + logfilename.ptr, + cast(int) err); + } } } diff --git a/runtime/druntime/src/rt/sections_ldc.d b/runtime/druntime/src/rt/sections_ldc.d index 5ee022bcf05..abeffadde3b 100644 --- a/runtime/druntime/src/rt/sections_ldc.d +++ b/runtime/druntime/src/rt/sections_ldc.d @@ -100,7 +100,7 @@ private * Scan segments in Linux dl_phdr_info struct and store * the TLS and writeable data segments in *pdso. */ - void scanSegments(in ref dl_phdr_info info, DSO* pdso) nothrow @nogc + void scanSegments(const scope ref dl_phdr_info info, DSO* pdso) nothrow @nogc { foreach (ref phdr; info.dlpi_phdr[0 .. info.dlpi_phnum]) { @@ -153,7 +153,7 @@ private return dl_iterate_phdr(&callback, &dg) != 0; } - bool findSegmentForAddr(in ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc + bool findSegmentForAddr(const scope ref dl_phdr_info info, in void* addr, ElfW!"Phdr"* result=null) nothrow @nogc { if (addr < cast(void*)info.dlpi_addr) // quick reject return false; diff --git a/runtime/druntime/src/rt/tracegc.d b/runtime/druntime/src/rt/tracegc.d index 29b61468056..ab65cb9887a 100644 --- a/runtime/druntime/src/rt/tracegc.d +++ b/runtime/druntime/src/rt/tracegc.d @@ -22,15 +22,13 @@ extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length); extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length); extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims); extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims); -extern (C) void* _d_newitemT(in TypeInfo ti); -extern (C) void* _d_newitemiT(in TypeInfo ti); +extern (C) void* _d_newitemT(const TypeInfo ti); +extern (C) void* _d_newitemiT(const TypeInfo ti); extern (C) void _d_callfinalizer(void* p); extern (C) void _d_callinterfacefinalizer(void *p); extern (C) void _d_delclass(Object* p); extern (C) void _d_delinterface(void** p); extern (C) void _d_delmemory(void* *p); -extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y); -extern (C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs); extern (C) void* _d_arrayliteralTX(const TypeInfo ti, size_t length); extern (C) void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] vals); diff --git a/runtime/druntime/test/profile/Makefile b/runtime/druntime/test/profile/Makefile index b0110c66e0c..f6a4bbe4cdb 100644 --- a/runtime/druntime/test/profile/Makefile +++ b/runtime/druntime/test/profile/Makefile @@ -7,6 +7,18 @@ TESTS:=profile DIFF:=diff --strip-trailing-cr GREP:=grep +ifeq (freebsd,$(OS)) + SHELL=/usr/local/bin/bash +else ifeq (openbsd,$(OS)) + SHELL=/usr/local/bin/bash +else ifeq (netbsd,$(OS)) + SHELL=/usr/pkg/bin/bash +else ifeq (dragonflybsd,$(OS)) + SHELL=/usr/local/bin/bash +else + SHELL=/bin/bash +endif + .PHONY: all clean all: $(addprefix $(ROOT)/,$(addsuffix .done,$(TESTS))) @@ -28,7 +40,9 @@ $(ROOT)/profilegc.done: $(ROOT)/%.done: $(ROOT)/% @echo Testing $* @rm -f $(ROOT)/myprofilegc.log $(QUIET)$(TIMELIMIT)$(ROOT)/$* $(ROOT)/myprofilegc.log - $(QUIET)$(DIFF) myprofilegc.log.$(OS).$(MODEL).exp $(ROOT)/myprofilegc.log + $(QUIET)$(DIFF) \ + <($(GREP) -vF 'core.' myprofilegc.log.$(OS).$(MODEL).exp) \ + <($(GREP) -vF 'core.' $(ROOT)/myprofilegc.log) @touch $@ $(ROOT)/both.done: DFLAGS+=-profile -profile=gc diff --git a/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp b/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp index 15b5e41fd7c..5f203cb7da3 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.freebsd.32.exp @@ -16,5 +16,4 @@ bytes allocated, allocations, type, function, file:line 16 1 int[] D main src/profilegc.d:14 16 1 int[] D main src/profilegc.d:22 16 1 int[] D main src/profilegc.d:37 - 16 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 16 1 wchar[] D main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp b/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp index 79c86edcfb3..73c4147f238 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.freebsd.64.exp @@ -6,7 +6,6 @@ bytes allocated, allocations, type, function, file:line 48 1 float[] D main src/profilegc.d:42 48 1 int[] D main src/profilegc.d:41 32 1 C D main src/profilegc.d:12 - 32 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 32 1 void[] profilegc.main src/profilegc.d:55 16 1 char[] D main src/profilegc.d:34 16 1 char[] D main src/profilegc.d:36 diff --git a/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp b/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp index 15b5e41fd7c..5f203cb7da3 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.linux.32.exp @@ -16,5 +16,4 @@ bytes allocated, allocations, type, function, file:line 16 1 int[] D main src/profilegc.d:14 16 1 int[] D main src/profilegc.d:22 16 1 int[] D main src/profilegc.d:37 - 16 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 16 1 wchar[] D main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp b/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp index 79c86edcfb3..73c4147f238 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.linux.64.exp @@ -6,7 +6,6 @@ bytes allocated, allocations, type, function, file:line 48 1 float[] D main src/profilegc.d:42 48 1 int[] D main src/profilegc.d:41 32 1 C D main src/profilegc.d:12 - 32 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 32 1 void[] profilegc.main src/profilegc.d:55 16 1 char[] D main src/profilegc.d:34 16 1 char[] D main src/profilegc.d:36 diff --git a/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp b/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp index 4faa76ae777..4c5494b7e11 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.osx.32.exp @@ -16,5 +16,4 @@ bytes allocated, allocations, type, function, file:line 16 1 int[] D main src/profilegc.d:14 16 1 int[] D main src/profilegc.d:22 16 1 int[] D main src/profilegc.d:37 - 16 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 16 1 wchar[] D main src/profilegc.d:35 diff --git a/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp b/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp index 79c86edcfb3..73c4147f238 100644 --- a/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp +++ b/runtime/druntime/test/profile/myprofilegc.log.osx.64.exp @@ -6,7 +6,6 @@ bytes allocated, allocations, type, function, file:line 48 1 float[] D main src/profilegc.d:42 48 1 int[] D main src/profilegc.d:41 32 1 C D main src/profilegc.d:12 - 32 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2755 32 1 void[] profilegc.main src/profilegc.d:55 16 1 char[] D main src/profilegc.d:34 16 1 char[] D main src/profilegc.d:36 diff --git a/runtime/phobos b/runtime/phobos index f4961356a33..f9112228cac 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit f4961356a33849f24557a77bdc386eff852bb9f5 +Subproject commit f9112228cac41b64a42b4720cc821ba6189ee88b diff --git a/tests/dmd/compilable/always_inline.i b/tests/dmd/compilable/always_inline.i new file mode 100644 index 00000000000..d7b4b0814b1 --- /dev/null +++ b/tests/dmd/compilable/always_inline.i @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug?id=21938 + +__attribute__((always_inline)) int square(int x) { return x * x; } + +int doSquare(int x) { return square(x); } diff --git a/tests/dmd/compilable/cattributes.i b/tests/dmd/compilable/cattributes.i new file mode 100644 index 00000000000..60b2f4c8b94 --- /dev/null +++ b/tests/dmd/compilable/cattributes.i @@ -0,0 +1,33 @@ +/* Smoke test dllimport, dllexport, and naked attributes */ + +__declspec(dllimport) int abc; + +__declspec(dllimport) int def(); + +__declspec(dllexport) int ghi() { return 3; } + +__declspec(dllexport) int jkl; + +/* LDC FIXME: __declspec(naked) restricts bodies to DMD-style inline asm +__declspec(naked)*/ __declspec(dllexport) +int test(int a, int b, int c, int d, int e, int f) +{ + return a + b + c + d + e + f + abc + def() + ghi() + jkl; +} + +/*****************************************/ + +__attribute__((dllimport)) int abcx; + +__attribute__((dllimport)) int defx(); + +__attribute__((dllexport)) int ghix() { return 3; } + +__attribute__((dllexport)) int jklx; + +/* LDC FIXME: ditto for __attribute__((naked)) +__attribute__((naked))*/ __attribute__((dllexport)) +int testx(int a, int b, int c, int d, int e, int f) +{ + return a + b + c + d + e + f + abcx + defx() + ghix() + jklx; +} diff --git a/tests/dmd/compilable/cppmangle.d b/tests/dmd/compilable/cppmangle.d index 1fe135d00bc..9501b4f3b04 100644 --- a/tests/dmd/compilable/cppmangle.d +++ b/tests/dmd/compilable/cppmangle.d @@ -550,7 +550,6 @@ version (CppMangle_Itanium) static assert(basic_ostream!(char, char_traits!char).food.mangleof == "_ZNSo4foodEv"); static assert(basic_iostream!(char, char_traits!char).fooe.mangleof == "_ZNSd4fooeEv"); - static assert(func_18957_2.mangleof == `_Z12func_18957_2PSaIiE`); static assert(func_18957_2!(allocator!int).mangleof == `_Z12func_18957_2ISaIiEET_PS1_`); static assert(func_20413.mangleof == `_Z10func_20413St4pairIifES_IfiE`); diff --git a/tests/dmd/compilable/cvariadic.i b/tests/dmd/compilable/cvariadic.i new file mode 100644 index 00000000000..70a9fef8cd1 --- /dev/null +++ b/tests/dmd/compilable/cvariadic.i @@ -0,0 +1,11 @@ +int abc() { return 1; } +int def(const char *p, ...) { return 2; } +int ghi(const char *p, int i) { return 3; } + +int main() +{ + abc("hello world %d\n", 1); + def("hello world %d\n", 2); + ghi("hello world %d\n", 3); + return 0; +} diff --git a/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d b/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d index 870387c7113..04363711bd3 100644 --- a/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d +++ b/tests/dmd/compilable/dtoh_CPPNamespaceDeclaration.d @@ -48,7 +48,7 @@ namespace nameSpace extern void fn2(); } - extern double identity(double _param_0); + extern double identity(double __param_0_); } --- @@ -63,5 +63,5 @@ extern(C++, "nameSpace") void fn2() {} } - double identity(double) { return _param_0; } + double identity(double) { return __param_0; } } diff --git a/tests/dmd/compilable/dtoh_functions.d b/tests/dmd/compilable/dtoh_functions.d index 1feff408adc..38607f6d4b1 100644 --- a/tests/dmd/compilable/dtoh_functions.d +++ b/tests/dmd/compilable/dtoh_functions.d @@ -159,7 +159,7 @@ extern int32_t(*f)(int32_t ); extern void special(int32_t a = ptr->i, int32_t b = ptr->get(1, 2), int32_t j = (*f)(1)); -extern void variadic(int32_t _param_0, ...); +extern void variadic(int32_t __param_0_, ...); --- +/ diff --git a/tests/dmd/compilable/dtoh_invalid_identifiers.d b/tests/dmd/compilable/dtoh_invalid_identifiers.d index b8e8d05649c..a8f5b990413 100644 --- a/tests/dmd/compilable/dtoh_invalid_identifiers.d +++ b/tests/dmd/compilable/dtoh_invalid_identifiers.d @@ -110,7 +110,7 @@ struct InvalidNames final } }; -extern void useInvalid(InvalidNames _param_0); +extern void useInvalid(InvalidNames __param_0_); extern size_t offsetof(); diff --git a/tests/dmd/compilable/dtoh_special_enum.d b/tests/dmd/compilable/dtoh_special_enum.d index 37b450703a9..ee86a5e7121 100644 --- a/tests/dmd/compilable/dtoh_special_enum.d +++ b/tests/dmd/compilable/dtoh_special_enum.d @@ -40,25 +40,25 @@ struct _d_dynamicArray final #endif enum class __c_not_special; -extern "C" void fn_long(long _param_0); +extern "C" void fn_long(long __param_0_); -extern "C" void fn_ulong(unsigned long _param_0); +extern "C" void fn_ulong(unsigned long __param_0_); -extern "C" void fn_longlong(long long _param_0); +extern "C" void fn_longlong(long long __param_0_); -extern "C" void fn_ulonglong(unsigned long long _param_0); +extern "C" void fn_ulonglong(unsigned long long __param_0_); -extern "C" void fn_long_double(long double _param_0); +extern "C" void fn_long_double(long double __param_0_); -extern "C" void fn_wchar_t(wchar_t _param_0); +extern "C" void fn_wchar_t(wchar_t __param_0_); -extern "C" void fn_complex_float(_Complex float _param_0); +extern "C" void fn_complex_float(_Complex float __param_0_); -extern "C" void fn_complex_double(_Complex double _param_0); +extern "C" void fn_complex_double(_Complex double __param_0_); -extern "C" void fn_complex_real(_Complex long double _param_0); +extern "C" void fn_complex_real(_Complex long double __param_0_); -extern "C" void fn_not_special(__c_not_special _param_0); +extern "C" void fn_not_special(__c_not_special __param_0_); --- +/ diff --git a/tests/dmd/compilable/enumbase.c b/tests/dmd/compilable/enumbase.c index f1fabfa2319..683a3ff04e5 100644 --- a/tests/dmd/compilable/enumbase.c +++ b/tests/dmd/compilable/enumbase.c @@ -23,3 +23,14 @@ enum U2: unsigned { enum U3: unsigned long { U3_A = 1, }; + +// https://issues.dlang.org/show_bug.cgi?id=23801 + +enum +{ + X = ~1ull, + Y, +}; + +_Static_assert(X == ~1ull, "3"); +_Static_assert(Y == ~1ull + 1, "4"); diff --git a/tests/dmd/compilable/imports/c23789.i b/tests/dmd/compilable/imports/c23789.i new file mode 100644 index 00000000000..74d7e80c2ad --- /dev/null +++ b/tests/dmd/compilable/imports/c23789.i @@ -0,0 +1,31 @@ +// https://issues.dlang.org/show_bug.cgi?id=23789 + +struct __declspec(align(64)) M128A { + char c; +}; + +typedef struct __declspec(align(32)) _M128B { + int x; +} M128B, *PM128A; + + +void testpl(p) +struct __declspec(align(2)) S *p; +{ +} + +///// + +struct __attribute__((aligned(64))) N128A { + char c; +}; + +typedef struct __attribute__((aligned(32))) _N128B { + int x; +} N128B, *PN128A; + + +void testpl2(p) +struct __attribute__((aligned(2))) S *p; +{ +} diff --git a/tests/dmd/compilable/imports/imp24022.c b/tests/dmd/compilable/imports/imp24022.c new file mode 100644 index 00000000000..b65e4e155bf --- /dev/null +++ b/tests/dmd/compilable/imports/imp24022.c @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug.cgi?id=24022 +typedef enum { + A = 1, + B, +} E; diff --git a/tests/dmd/compilable/imports/library.c b/tests/dmd/compilable/imports/library.c new file mode 100644 index 00000000000..4951e465574 --- /dev/null +++ b/tests/dmd/compilable/imports/library.c @@ -0,0 +1,5 @@ +typedef enum SomeEnum +{ + foo = 0, + bar = -10000, +} SomeEnum; diff --git a/tests/dmd/compilable/interpret3.d b/tests/dmd/compilable/interpret3.d index 2c9a84eddfb..14142630454 100644 --- a/tests/dmd/compilable/interpret3.d +++ b/tests/dmd/compilable/interpret3.d @@ -7208,7 +7208,7 @@ struct S13630(T) { T[3] arr; - this(A...)(auto ref in A args) + this(A...)(const auto ref A args) { auto p = arr.ptr; @@ -7238,7 +7238,7 @@ struct Matrix13827(T, uint N) T[N] flat; } - this(A...)(auto ref in A args) + this(A...)(const auto ref A args) { uint k; diff --git a/tests/dmd/compilable/posixbitfields.c b/tests/dmd/compilable/posixbitfields.c new file mode 100644 index 00000000000..8bda9d4b8b9 --- /dev/null +++ b/tests/dmd/compilable/posixbitfields.c @@ -0,0 +1,159 @@ +// DISABLED: win32 win64 + +// https://issues.dlang.org/show_bug.cgi?id=23427 + +_Static_assert(sizeof(unsigned) == 4, "1"); + +struct A { + unsigned x :8; + unsigned y :4; + unsigned z :20; +}; +_Static_assert(sizeof(struct A)==4, "2"); + +struct B { + unsigned x :4; + unsigned y :2; + unsigned z :26; +}; +_Static_assert(sizeof(struct B)==4, "3"); + +struct C { + unsigned x :4; + unsigned y :4; + unsigned z :24; +}; +_Static_assert(sizeof(struct C)==4, "4"); // This one fails + + +_Static_assert(sizeof(struct { + unsigned a: 1; + unsigned b: 7; + unsigned c: 24; +}) == sizeof(unsigned), "1 7 24"); + +_Static_assert(sizeof(struct { + unsigned a: 2; + unsigned b: 6; + unsigned c: 24; +}) == sizeof(unsigned), "2 6 24"); + +_Static_assert(sizeof(struct { + unsigned a: 3; + unsigned b: 5; + unsigned c: 24; +}) == sizeof(unsigned), "3 5 24"); + +_Static_assert(sizeof(struct { + unsigned a: 4; + unsigned b: 4; + unsigned c: 24; +}) == sizeof(unsigned), "4 4 24"); + +_Static_assert(sizeof(struct { + unsigned a: 5; + unsigned b: 3; + unsigned c: 24; +}) == sizeof(unsigned), "5 3 24"); + +_Static_assert(sizeof(struct { + unsigned a: 6; + unsigned b: 2; + unsigned c: 24; +}) == sizeof(unsigned), "6 2 24"); + +_Static_assert(sizeof(struct { + unsigned a: 7; + unsigned b: 1; + unsigned c: 24; +}) == sizeof(unsigned), "7 1 24"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 9; + unsigned c: 15; +}) == sizeof(unsigned), "8 9 15"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 10; + unsigned c: 14; +}) == sizeof(unsigned), "8 10 14"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 11; + unsigned c: 13; +}) == sizeof(unsigned), "8 11 13"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 12; + unsigned c: 12; +}) == sizeof(unsigned), "8 12 12"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 13; + unsigned c: 11; +}) == sizeof(unsigned), "8 13 11"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 14; + unsigned c: 10; +}) == sizeof(unsigned), "8 14 10"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 15; + unsigned c: 9; +}) == sizeof(unsigned), "8 15 9"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 16; + unsigned c: 8; +}) == sizeof(unsigned), "8 16 8"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 17; + unsigned c: 7; +}) == sizeof(unsigned), "8 17 7"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 18; + unsigned c: 6; +}) == sizeof(unsigned), "8 18 6"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 19; + unsigned c: 5; +}) == sizeof(unsigned), "8 19 5"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 20; + unsigned c: 4; +}) == sizeof(unsigned), "8 20 4"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 21; + unsigned c: 3; +}) == sizeof(unsigned), "8 21 3"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 22; + unsigned c: 2; +}) == sizeof(unsigned), "8 22 2"); + +_Static_assert(sizeof(struct { + unsigned a: 8; + unsigned b: 23; + unsigned c: 1; +}) == sizeof(unsigned), "8 23 1"); diff --git a/tests/dmd/compilable/stdcheaders.c b/tests/dmd/compilable/stdcheaders.c index 4b7399d8e46..63bbcb88caa 100644 --- a/tests/dmd/compilable/stdcheaders.c +++ b/tests/dmd/compilable/stdcheaders.c @@ -5,16 +5,14 @@ #include #ifndef __DMC__ // D:\a\1\s\tools\dm\include\complex.h(105): Deprecation: use of complex type `cdouble` is deprecated, use `std.complex.Complex!(double)` instead -#ifndef __FreeBSD__ // defines _COMPLEX_I with use of `i` postfix #include #endif -#endif #include #include #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt\fenv.h(68): Error: variable `stdcheaders._Fenv1` extern symbols cannot have initializers -#ifndef __FreeBSD__ // cannot turn off __GNUCLIKE_ASM in machine/ieeefp.h +#ifndef __FreeBSD__ // /usr/include/fenv.h(341): Error: use `.` for member lookup, not `->` #include #endif #endif @@ -45,13 +43,11 @@ #ifndef __linux__ #ifndef _MSC_VER #ifndef __APPLE__ // /Applications/Xcode-14.2.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/14.0.0/include/stdatomic.h(80): Error: type-specifier is missing -#ifndef __FreeBSD__ // /stdatomic.h(162): Error: found `volatile` when expecting `{` #include #endif #endif #endif #endif -#endif #include #include @@ -71,8 +67,8 @@ #ifndef __DMC__ // no tgmath.h #ifndef _MSC_VER // C:\Program Files (x86)\Windows Kits\10\include\10.0.22621.0\ucrt\tgmath.h(33): Error: no type for declarator before `)` #ifndef __APPLE__ // /Applications/Xcode-14.2.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/tgmath.h(39): Error: named parameter required before `...` -#ifndef __FreeBSD__ // #includes complex.h #if !(defined(__linux__) && defined(__aarch64__)) // /tmp/clang/lib/clang/15.0.3/include/tgmath.h(34): Error: named parameter required before `...` +#ifndef __FreeBSD__ // /usr/local/llvm15/lib/clang/15.0.7/include/tgmath.h(34): Error: named parameter required before `...` #include #endif #endif diff --git a/tests/dmd/compilable/test18493.d b/tests/dmd/compilable/test18493.d new file mode 100644 index 00000000000..558bf447904 --- /dev/null +++ b/tests/dmd/compilable/test18493.d @@ -0,0 +1,19 @@ +// https://issues.dlang.org/show_bug.cgi?id=18493 +// REQUIRED_ARGS: -betterC + +struct S +{ + this(this) + { + } + + ~this() + { + } +} + +struct C +{ + S s1; + S s2; +} diff --git a/tests/dmd/runnable/test19688.d b/tests/dmd/compilable/test19688.d similarity index 100% rename from tests/dmd/runnable/test19688.d rename to tests/dmd/compilable/test19688.d diff --git a/tests/dmd/compilable/test21667.d b/tests/dmd/compilable/test21667.d new file mode 100644 index 00000000000..6d83dc056c4 --- /dev/null +++ b/tests/dmd/compilable/test21667.d @@ -0,0 +1,19 @@ +// Issue 21667 - scope parameter causes 'no size because of forward references' +// https://issues.dlang.org/show_bug.cgi?id=21667 +@safe: + +struct Foo +{ + void delegate(scope Foo) dg; +} + +struct M +{ + F.Type f; +} + +struct F +{ + enum Type {a} + void foo(scope M m) {} +} diff --git a/tests/dmd/compilable/test22559.i b/tests/dmd/compilable/test22559.i new file mode 100644 index 00000000000..d8e5cc8847e --- /dev/null +++ b/tests/dmd/compilable/test22559.i @@ -0,0 +1,17 @@ +// https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html + +int alpha(char c) +{ + switch (c) + { + case 'A' ... 'Z': return 1; + case 'a' ... 'z': return 1; + default: return 0; + } +} + +_Static_assert(alpha('A') == 1, "1"); +_Static_assert(alpha('B') == 1, "2"); +_Static_assert(alpha('z') == 1, "3"); +_Static_assert(alpha('z' + 1) == 0, "3"); +_Static_assert(alpha('0') == 0, "4"); diff --git a/tests/dmd/compilable/test22760.d b/tests/dmd/compilable/test22760.d new file mode 100644 index 00000000000..5957db3256c --- /dev/null +++ b/tests/dmd/compilable/test22760.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=22760 + +extern(C++) void f(T)(T) +{ +} +struct S1(T) +{ + struct S2 + { + } +} +void fun() +{ + f(S1!int.S2()); +} diff --git a/tests/dmd/compilable/test23509.i b/tests/dmd/compilable/test23509.i new file mode 100644 index 00000000000..fd2c5d85e23 --- /dev/null +++ b/tests/dmd/compilable/test23509.i @@ -0,0 +1,8 @@ +// https://issues.dlang.org/show_bug.cgi?id=23509 + +int max(int a, int b) +{ + return ({int _a = (a), _b = (b); _a > _b ? _a : _b; }); +} + +_Static_assert(max(3,4) == 4, "1"); diff --git a/tests/dmd/compilable/test23789.d b/tests/dmd/compilable/test23789.d new file mode 100644 index 00000000000..37fc3d4bc20 --- /dev/null +++ b/tests/dmd/compilable/test23789.d @@ -0,0 +1,11 @@ +// https://issues.dlang.org/show_bug.cgi?id=23789 + +import imports.c23789; + +static assert(M128A.alignof == 64); +static assert(_M128B.alignof == 32); +static assert(M128B.alignof == 32); + +static assert(N128A.alignof == 64); +static assert(_N128B.alignof == 32); +static assert(N128B.alignof == 32); diff --git a/tests/dmd/compilable/test23862.d b/tests/dmd/compilable/test23862.d new file mode 100644 index 00000000000..c5b063b2784 --- /dev/null +++ b/tests/dmd/compilable/test23862.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=23862 + +enum E { A, B } + +void test(E e) +{ + with (e) + switch (e) + { + case A: + case B: + default: + break; + } +} diff --git a/tests/dmd/compilable/test23863.d b/tests/dmd/compilable/test23863.d new file mode 100644 index 00000000000..c3941ce2f81 --- /dev/null +++ b/tests/dmd/compilable/test23863.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=23863 + +alias AliasSeq(T...) = T; + +struct S +{ +} +alias Empty = S.tupleof; +Empty x; // accepts valid + +static assert(is(typeof(x))); +static assert(is(typeof(Empty))); +static assert(is(typeof(AliasSeq!(int)))); +static assert(is(typeof(Empty) == AliasSeq!())); +static assert(is(typeof(AliasSeq!()) == AliasSeq!())); diff --git a/tests/dmd/compilable/test23866.i b/tests/dmd/compilable/test23866.i new file mode 100644 index 00000000000..71a251a364d --- /dev/null +++ b/tests/dmd/compilable/test23866.i @@ -0,0 +1,5 @@ +// https://issues.dlang.org/show_bug.cgi?id=23866 + +struct __declspec(align(16)) __declspec(no_init_all) S { }; + +typedef struct __attribute__((aligned(16))) __declspec(no_init_all) S2 { } S2; diff --git a/tests/dmd/compilable/test23874.d b/tests/dmd/compilable/test23874.d new file mode 100644 index 00000000000..09121844ee1 --- /dev/null +++ b/tests/dmd/compilable/test23874.d @@ -0,0 +1,11 @@ +// https://issues.dlang.org/show_bug.cgi?id=23874 +// REQUIRED_ARGS: -profile=gc +// DISABLED: LDC // -profile=gc not supported + +string myToString() +{ + return ""; +} + +enum x = myToString ~ ""; +immutable x2 = myToString ~ ""; diff --git a/tests/dmd/compilable/test23913.d b/tests/dmd/compilable/test23913.d new file mode 100644 index 00000000000..e39c6dec317 --- /dev/null +++ b/tests/dmd/compilable/test23913.d @@ -0,0 +1,7 @@ +// EXTRA_FILES: imports/library.c + +// https://issues.dlang.org/show_bug.cgi?id=23913 + +import imports.library; + +alias x = __traits(getMember, imports.library, "SomeEnum"); diff --git a/tests/dmd/compilable/test23951.d b/tests/dmd/compilable/test23951.d new file mode 100644 index 00000000000..e09a3d7b5d1 --- /dev/null +++ b/tests/dmd/compilable/test23951.d @@ -0,0 +1,10 @@ +// https://issues.dlang.org/show_bug.cgi?id=23951 + +struct S { int x; } +struct T { S a; alias a this; } +struct U { T t; } +static assert(__traits(hasMember, T, "x")); +static assert(__traits(hasMember, T.init, "x")); +static assert(__traits(hasMember, U.init.t, "x")); +static assert(__traits(hasMember, U.t, "a")); +static assert(__traits(hasMember, U.t, "x")); diff --git a/tests/dmd/compilable/test23965.d b/tests/dmd/compilable/test23965.d new file mode 100644 index 00000000000..36e4e428a85 --- /dev/null +++ b/tests/dmd/compilable/test23965.d @@ -0,0 +1,11 @@ +// https://issues.dlang.org/show_bug.cgi?id=23965 +// REQUIRED_ARGS: -de +deprecated: + +struct S {} + +void fun() +{ + S[] arr; + arr ~= S(); +} diff --git a/tests/dmd/compilable/test23966.d b/tests/dmd/compilable/test23966.d new file mode 100644 index 00000000000..71aa8ca229e --- /dev/null +++ b/tests/dmd/compilable/test23966.d @@ -0,0 +1,19 @@ +// https://issues.dlang.org/show_bug.cgi?id=23966 +module test23966; + +@("gigi") +void fun() {} +@("mimi") +void fun(int) {} +@("hihi") +void fun(int, int) {} +@("bibi") +void fun()(int, ulong) {} + +void main() +{ + static foreach (t; __traits(getOverloads, test23966, "fun", true)) + static foreach(attr; __traits(getAttributes, t)) + {} + +} diff --git a/tests/dmd/compilable/test23978.d b/tests/dmd/compilable/test23978.d new file mode 100644 index 00000000000..cc30f728dee --- /dev/null +++ b/tests/dmd/compilable/test23978.d @@ -0,0 +1,30 @@ +// REQUIRED_ARGS: -preview=dip1021 -lowmem +// https://issues.dlang.org/show_bug.cgi?id=23978 + +// Note: this is a memory corruption bug. +// Memory returned by `GC.realloc` retains references to old memory in it, +// mostly because of the smallarray optimization for `Array(T)`. +// If this fails again, it might not be consistent, so try running it multiple times. + +class LUBench { } +void lup(ulong , ulong , int , int = 1) +{ + new LUBench; +} +void lup_3200(ulong iters, ulong flops) +{ + lup(iters, flops, 3200); +} +void raytrace() +{ + struct V + { + float x, y, z; + auto normalize() { } + struct Tid { } + auto spawnLinked() { } + string[] namesByTid; + class MessageBox { } + auto cross() { } + } +} diff --git a/tests/dmd/compilable/test23979.d b/tests/dmd/compilable/test23979.d new file mode 100644 index 00000000000..f7eb2c554ee --- /dev/null +++ b/tests/dmd/compilable/test23979.d @@ -0,0 +1,17 @@ +// REQUIRED_ARGS: -o- +// https://issues.dlang.org/show_bug.cgi?id=23979 +// Issue 23979 - ICE on failed alias this attempt on pointer expression + +class A {} + +void h() +{ + const auto classPtr = SPtr.init; + assert(!__traits(compiles, *classPtr)); +} + +struct SPtr +{ + A ptr() { return A.init; } + alias ptr this; +} diff --git a/tests/dmd/compilable/test23986.d b/tests/dmd/compilable/test23986.d new file mode 100644 index 00000000000..6bbf1c2aeaa --- /dev/null +++ b/tests/dmd/compilable/test23986.d @@ -0,0 +1,11 @@ +// REQUIRED_ARGS: -preview=dip1021 -o- +// https://issues.dlang.org/show_bug.cgi?id=23986 +// dip1021 asserts on `typeof(null)` parameter +@safe: + +void f(typeof(null) obj, int* x) {} + +void g() +{ + f(null, null); +} diff --git a/tests/dmd/compilable/test24013.d b/tests/dmd/compilable/test24013.d new file mode 100644 index 00000000000..132ada61c8e --- /dev/null +++ b/tests/dmd/compilable/test24013.d @@ -0,0 +1,43 @@ +// https://issues.dlang.org/show_bug.cgi?id=24013 + +struct PropDescriptor(T) +{ + void define(T delegate() aGetter, string aName) {} +} + +enum Get; + +mixin template PropertyPublisherImpl() +{ + void collectPublicationsFromPairs(T)() + { + foreach (member; __traits(derivedMembers, T)) + foreach (overload; __traits(getOverloads, T, member)) + { + alias Attributes = __traits(getAttributes, overload); + static if (Attributes.length != 0) + { + auto descriptor = new PropDescriptor!size_t; + auto dg = &overload; + descriptor.define(dg, member); + } + } + } +} + +void main() +{ + class Bar + { + size_t _field; + mixin PropertyPublisherImpl; + this() + { + collectPublicationsFromPairs!Bar; + } + @Get size_t field() + { + return _field; + } + } +} diff --git a/tests/dmd/compilable/test24022.d b/tests/dmd/compilable/test24022.d new file mode 100644 index 00000000000..f499636f126 --- /dev/null +++ b/tests/dmd/compilable/test24022.d @@ -0,0 +1,30 @@ +// https://issues.dlang.org/show_bug.cgi?id=24022 +// EXTRA_FILES: imports/imp24022.c +import imports.imp24022; + +auto some_d_func(E v) { + return v; +} + +auto some_d_other_func() { + const struct R { + E r; + this(in E vparam) { r = vparam; } + } + return R(A); +} + +void main(string[] args) { + E expected = E.A; + E res = some_d_func(A); + assert (res == A); + assert (res == expected); + + res = some_d_func(E.B); + assert (res == B); + assert (res == E.B); + + auto res2 = some_d_other_func(); + assert (res2.r == A); + assert (res2.r == expected); +} diff --git a/tests/dmd/compilable/testcomplex.i b/tests/dmd/compilable/testcomplex.i new file mode 100644 index 00000000000..3cc0021fa0e --- /dev/null +++ b/tests/dmd/compilable/testcomplex.i @@ -0,0 +1,35 @@ + +/* GCC header complex.h requires supporting `i` suffix extension + */ + +_Complex float testf() +{ + _Complex float x = 1.0if; + return x; +} + +_Complex float testf2() +{ + _Complex float x = (float _Complex)1.0i; + return x; +} + +_Complex double testd() +{ + _Complex double x = 1.0i; + return x; +} + +_Complex long double testld() +{ + _Complex long double x = 1.0iL; + return x; +} + +_Complex float testcast() +{ + _Complex double y = 1.0i; + return (_Complex float)y; +} + +_Static_assert((float _Complex)1.0i == 1.0i, "1"); diff --git a/tests/dmd/compilable/testcstuff1.c b/tests/dmd/compilable/testcstuff1.c index 2067e72b0c3..3a1f66358e1 100644 --- a/tests/dmd/compilable/testcstuff1.c +++ b/tests/dmd/compilable/testcstuff1.c @@ -201,6 +201,13 @@ _Static_assert(testexpinit() == 1 + 2 + 3, "ok"); /********************************/ +__declspec(restrict) void* testrestrictdeclspec() +{ + return 0; +} + +/********************************/ + // Character literals _Static_assert(sizeof('a') == 4, "ok"); _Static_assert(sizeof(u'a') == 4, "ok"); @@ -272,7 +279,8 @@ void test2() typedef int TI; //extern int ei; static int si; - _Thread_local int tli; + static _Thread_local int tli; + int __declspec(thread) tlj; auto int ai; register int reg; const int ci; diff --git a/tests/dmd/compilable/testcstuff2.c b/tests/dmd/compilable/testcstuff2.c index 077daf43ab4..ed4af3d84f0 100644 --- a/tests/dmd/compilable/testcstuff2.c +++ b/tests/dmd/compilable/testcstuff2.c @@ -389,6 +389,9 @@ __attribute__((static, unsigned, long, const, extern, register, typedef, short, _Thread_local, int, char, float, double, void, _Bool, _Atomic)) int test22196(); +_Atomic(_Bool) atomicbool; + + /***************************************************/ // https://issues.dlang.org/show_bug.cgi?id=22245 @@ -714,3 +717,20 @@ enum E2 { m1, m2 = m1 }; + +/************************************************************/ + +// https://issues.dlang.org/show_bug.cgi?id=23725 + +#define __fldcw(addr) asm volatile("fldcw %0" : : "m" (*(addr))) + +static __inline void +__fnldcw(unsigned short _cw, unsigned short _newcw) +{ + __fldcw(&_newcw); +} + +void test23725() +{ + __fnldcw(1, 2); +} diff --git a/tests/dmd/compilable/testnoinline.c b/tests/dmd/compilable/testnoinline.c new file mode 100644 index 00000000000..cf3928eb58b --- /dev/null +++ b/tests/dmd/compilable/testnoinline.c @@ -0,0 +1,9 @@ + +__attribute__((noinline)) int abc() { return 1; } +__declspec(noinline) int def() { return 2; } +inline int ghi() { return 3; } + +int test() +{ + return abc() + def() + ghi(); +} diff --git a/tests/dmd/compilable/traits_getFunctionAttributes.d b/tests/dmd/compilable/traits_getFunctionAttributes.d index 1f25b269053..f4defb48095 100644 --- a/tests/dmd/compilable/traits_getFunctionAttributes.d +++ b/tests/dmd/compilable/traits_getFunctionAttributes.d @@ -1,9 +1,10 @@ module traits_getFunctionAttributes; +alias tuple(T...) = T; + void test_getFunctionAttributes() { - alias tuple(T...) = T; struct S { @@ -118,3 +119,14 @@ void test_getFunctionAttributes() static assert(__traits(getFunctionAttributes, systemDel) == tuple!("pure", "nothrow", "@nogc", "@system")); static assert(__traits(getFunctionAttributes, typeof(systemDel)) == tuple!("pure", "nothrow", "@nogc", "@system")); } + +void bug19706() +{ + struct S + { + static int fImpl(Ret)() { return Ret.init; } + + // tells us: `fImpl!int` is @system + static assert(__traits(getFunctionAttributes, fImpl!int) == tuple!("pure", "nothrow", "@nogc", "@safe")); + } +} diff --git a/tests/dmd/compilable/user_defined_attributes.d b/tests/dmd/compilable/user_defined_attributes.d new file mode 100644 index 00000000000..169ca49eb0d --- /dev/null +++ b/tests/dmd/compilable/user_defined_attributes.d @@ -0,0 +1,22 @@ + +enum Test; + +@true @null @byte int x; +@(int) int y; +@"test" @`test2` @30 @'a' @__LINE__ void f(); + +@Test void h(); + +static assert( __traits(getAttributes, x)[0] == true); +static assert( __traits(getAttributes, x)[1] == null); +static assert(is(__traits(getAttributes, x)[2] == byte)); + +static assert(is(__traits(getAttributes, y)[0] == int)); + +static assert( __traits(getAttributes, f)[0] == "test"); +static assert( __traits(getAttributes, f)[1] == "test2"); +static assert( __traits(getAttributes, f)[2] == 30); +static assert( __traits(getAttributes, f)[3] == 'a'); +static assert( __traits(getAttributes, f)[4] == 6); + +static assert(is(__traits(getAttributes, h)[0] == enum)); diff --git a/tests/dmd/compilable/warn3882.d b/tests/dmd/compilable/warn3882.d index f02a87bd04b..0474315be57 100644 --- a/tests/dmd/compilable/warn3882.d +++ b/tests/dmd/compilable/warn3882.d @@ -12,7 +12,7 @@ void test3882() /******************************************/ // https://issues.dlang.org/show_bug.cgi?id=12619 -extern (C) @system nothrow pure void* memcpy(void* s1, in void* s2, size_t n); +extern (C) @system nothrow pure void* memcpy(void* s1, const void* s2, size_t n); // -> weakly pure void test12619() pure @@ -64,7 +64,7 @@ void test12909() const struct Foo13899 { - int opApply(immutable int delegate(in ref int) pure nothrow dg) pure nothrow + int opApply(immutable int delegate(const ref int) pure nothrow dg) pure nothrow { return 1; } diff --git a/tests/dmd/dub_package/avg.d b/tests/dmd/dub_package/avg.d index 4ce5776e777..b6f9f1e8898 100755 --- a/tests/dmd/dub_package/avg.d +++ b/tests/dmd/dub_package/avg.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ /* This file contains an example on how to use the transitive visitor. It implements a visitor which computes the average function length from @@ -10,7 +10,7 @@ dependency "dmd" path="../.." module examples.avg; import dmd.astbase; -import dmd.errors; +import dmd.errorsink; import dmd.parse; import dmd.target; import dmd.transitivevisitor; @@ -60,8 +60,9 @@ void main() auto id = Identifier.idPool(fname); auto m = new ASTBase.Module(&(fname.dup)[0], id, false, false); auto input = readText(fname); + input ~= '\0'; - scope p = new Parser!ASTBase(m, input, false); + scope p = new Parser!ASTBase(m, input, false, new ErrorSinkStderr(), null, false); p.nextToken(); m.members = p.parseModule(); diff --git a/tests/dmd/dub_package/frontend.d b/tests/dmd/dub_package/frontend.d index b034027deb9..184f5960d7b 100755 --- a/tests/dmd/dub_package/frontend.d +++ b/tests/dmd/dub_package/frontend.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ import std.stdio; diff --git a/tests/dmd/dub_package/frontend_file.d b/tests/dmd/dub_package/frontend_file.d index 71e62f51528..a6d662ebf64 100755 --- a/tests/dmd/dub_package/frontend_file.d +++ b/tests/dmd/dub_package/frontend_file.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ import std.stdio; import std.string : replace; diff --git a/tests/dmd/dub_package/impvisitor.d b/tests/dmd/dub_package/impvisitor.d index 85df47da226..c27a71abea0 100755 --- a/tests/dmd/dub_package/impvisitor.d +++ b/tests/dmd/dub_package/impvisitor.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ import dmd.permissivevisitor; @@ -27,7 +27,7 @@ extern(C++) class ImportVisitor2(AST) : ParseTimeTransitiveVisitor!AST printf("%s", imp.id.toChars()); - if (imp.names.dim) + if (imp.names.length) { printf(" : "); foreach (const i, const name; imp.names) @@ -82,12 +82,13 @@ void main() import dmd.id; import dmd.globals; import dmd.identifier; + import dmd.errorsink; import dmd.target; import core.memory; GC.disable(); - string path = __FILE_FULL_PATH__.dirName.buildPath("../../../phobos/std/"); + string path = __FILE_FULL_PATH__.dirName.buildPath("../../../../phobos/std/"); string regex = "*.d"; auto dFiles = dirEntries(path, regex, SpanMode.depth); @@ -106,9 +107,10 @@ void main() auto id = Identifier.idPool(fn); auto m = new ASTBase.Module(&(fn.dup)[0], id, false, false); auto input = readText(fn); + input ~= '\0'; //writeln("Started parsing..."); - scope p = new Parser!ASTBase(m, input, false); + scope p = new Parser!ASTBase(m, input, false, new ErrorSinkStderr(), null, false); p.nextToken(); m.members = p.parseModule(); //writeln("Finished parsing. Starting transitive visitor"); diff --git a/tests/dmd/dub_package/lexer.d b/tests/dmd/dub_package/lexer.d index 608f18e031b..b496d4b62c9 100755 --- a/tests/dmd/dub_package/lexer.d +++ b/tests/dmd/dub_package/lexer.d @@ -1,12 +1,13 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ void main() { import dmd.globals; import dmd.lexer; import dmd.tokens; + import dmd.errorsink; immutable expected = [ TOK.void_, @@ -18,7 +19,7 @@ void main() ]; immutable sourceCode = "void test() {} // foobar"; - scope lexer = new Lexer("test", sourceCode.ptr, 0, sourceCode.length, 0, 0); + scope lexer = new Lexer("test", sourceCode.ptr, 0, sourceCode.length, 0, 0, 0, new ErrorSinkStderr); lexer.nextToken; TOK[] result; diff --git a/tests/dmd/dub_package/parser.d b/tests/dmd/dub_package/parser.d index 2c9d7668866..9d24a77089a 100755 --- a/tests/dmd/dub_package/parser.d +++ b/tests/dmd/dub_package/parser.d @@ -1,13 +1,14 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." +/ void main() { import dmd.astbase; import dmd.globals; import dmd.parse; + import dmd.errorsink; - scope parser = new Parser!ASTBase(null, null, false); + scope parser = new Parser!ASTBase(null, null, false, new ErrorSinkStderr, null, false); assert(parser !is null); } diff --git a/tests/dmd/dub_package/retrieveScope.d b/tests/dmd/dub_package/retrieveScope.d index 36e05c365fe..bfd6d5f4614 100755 --- a/tests/dmd/dub_package/retrieveScope.d +++ b/tests/dmd/dub_package/retrieveScope.d @@ -1,6 +1,6 @@ #!/usr/bin/env dub /+dub.sdl: -dependency "dmd" path="../.." +dependency "dmd" path="../../.." versions "CallbackAPI" +/ /* @@ -20,13 +20,13 @@ import std.path : dirName; import dmd.errors; import dmd.frontend; -import dmd.mars; import dmd.console; import dmd.arraytypes; import dmd.compiler; import dmd.dmodule; import dmd.dsymbol; import dmd.dsymbolsem; +import dmd.location; import dmd.semantic2; import dmd.semantic3; import dmd.statement; @@ -77,14 +77,9 @@ int main() global.gag = 1; initDMD(diagnosticHandler); - Strings libmodules; - Module m = createModule((dirName(__FILE_FULL_PATH__) ~ "/testfiles/correct.d").ptr, - libmodules); + Module m = parseModule(__FILE_FULL_PATH__ ~ "/testfiles/correct.d").module_; m.importedFrom = m; // m.isRoot() == true - m.read(Loc.initial); - m.parse(); - CallbackHelper.cursorLoc = Loc(to!string(m.srcfile).ptr, 22, 10); Compiler.onStatementSemanticStart = &CallbackHelper.statementSem; diff --git a/tests/dmd/fail_compilation/alignedext.i b/tests/dmd/fail_compilation/alignedext.i new file mode 100644 index 00000000000..eae3137ce47 --- /dev/null +++ b/tests/dmd/fail_compilation/alignedext.i @@ -0,0 +1,14 @@ +/* TEST_OUTPUT: +--- +fail_compilation/alignedext.i(10): Error: __decspec(align(123)) must be an integer positive power of 2 and be <= 8,192 +fail_compilation/alignedext.i(11): Error: __decspec(align(16384)) must be an integer positive power of 2 and be <= 8,192 +fail_compilation/alignedext.i(13): Error: __attribute__((aligned(123))) must be an integer positive power of 2 and be <= 32,768 +fail_compilation/alignedext.i(14): Error: __attribute__((aligned(65536))) must be an integer positive power of 2 and be <= 32,768 +--- +*/ + +typedef struct __declspec(align(123)) S { int a; } S; +struct __declspec(align(16384)) T { int a; }; + +typedef struct __attribute__((aligned(123))) U { int a; } S; +struct __attribute__((aligned(65536))) V { int a; }; diff --git a/tests/dmd/fail_compilation/attributediagnostic.d b/tests/dmd/fail_compilation/attributediagnostic.d index 8360e1ac484..523a183d767 100644 --- a/tests/dmd/fail_compilation/attributediagnostic.d +++ b/tests/dmd/fail_compilation/attributediagnostic.d @@ -4,15 +4,15 @@ TEST_OUTPUT: fail_compilation/attributediagnostic.d(24): Error: `@safe` function `attributediagnostic.layer2` cannot call `@system` function `attributediagnostic.layer1` fail_compilation/attributediagnostic.d(26): which calls `attributediagnostic.layer0` fail_compilation/attributediagnostic.d(28): which calls `attributediagnostic.system` -fail_compilation/attributediagnostic.d(30): which was inferred `@system` because of: +fail_compilation/attributediagnostic.d(30): which wasn't inferred `@safe` because of: fail_compilation/attributediagnostic.d(30): `asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not fail_compilation/attributediagnostic.d(25): `attributediagnostic.layer1` is declared here fail_compilation/attributediagnostic.d(46): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system1` -fail_compilation/attributediagnostic.d(35): which was inferred `@system` because of: +fail_compilation/attributediagnostic.d(35): which wasn't inferred `@safe` because of: fail_compilation/attributediagnostic.d(35): cast from `uint` to `int*` not allowed in safe code fail_compilation/attributediagnostic.d(33): `attributediagnostic.system1` is declared here fail_compilation/attributediagnostic.d(47): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system2` -fail_compilation/attributediagnostic.d(41): which was inferred `@system` because of: +fail_compilation/attributediagnostic.d(41): which wasn't inferred `@safe` because of: fail_compilation/attributediagnostic.d(41): `@safe` function `system2` cannot call `@system` `fsys` fail_compilation/attributediagnostic.d(39): `attributediagnostic.system2` is declared here --- diff --git a/tests/dmd/fail_compilation/attributediagnostic_nogc.d b/tests/dmd/fail_compilation/attributediagnostic_nogc.d new file mode 100644 index 00000000000..e3dbee899fc --- /dev/null +++ b/tests/dmd/fail_compilation/attributediagnostic_nogc.d @@ -0,0 +1,56 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/attributediagnostic_nogc.d(21): Error: `@nogc` function `attributediagnostic_nogc.layer2` cannot call non-@nogc function `attributediagnostic_nogc.layer1` +fail_compilation/attributediagnostic_nogc.d(22): which calls `attributediagnostic_nogc.layer0` +fail_compilation/attributediagnostic_nogc.d(23): which calls `attributediagnostic_nogc.gc` +fail_compilation/attributediagnostic_nogc.d(27): which wasn't inferred `@nogc` because of: +fail_compilation/attributediagnostic_nogc.d(27): `asm` statement in function `attributediagnostic_nogc.gc` is assumed to use the GC - mark it with `@nogc` if it does not +fail_compilation/attributediagnostic_nogc.d(43): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gc1` +fail_compilation/attributediagnostic_nogc.d(32): which wasn't inferred `@nogc` because of: +fail_compilation/attributediagnostic_nogc.d(32): cannot use `new` in `@nogc` function `attributediagnostic_nogc.gc1` +fail_compilation/attributediagnostic_nogc.d(44): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gc2` +fail_compilation/attributediagnostic_nogc.d(38): which wasn't inferred `@nogc` because of: +fail_compilation/attributediagnostic_nogc.d(38): `@nogc` function `attributediagnostic_nogc.gc2` cannot call non-@nogc `fgc` +fail_compilation/attributediagnostic_nogc.d(45): Error: `@nogc` function `D main` cannot call non-@nogc function `attributediagnostic_nogc.gcClosure` +fail_compilation/attributediagnostic_nogc.d(48): which wasn't inferred `@nogc` because of: +fail_compilation/attributediagnostic_nogc.d(48): function `attributediagnostic_nogc.gcClosure` is `@nogc` yet allocates closure for `gcClosure()` with the GC +--- +*/ +#line 18 +// Issue 17374 - Improve inferred attribute error message +// https://issues.dlang.org/show_bug.cgi?id=17374 + +auto layer2() @nogc { layer1(); } +auto layer1() { layer0(); } +auto layer0() { gc(); } + +auto gc() +{ + asm {} +} + +auto gc1() +{ + int* x = new int; +} + +auto fgc = function void() {new int[10];}; +auto gc2() +{ + fgc(); +} + +void main() @nogc +{ + gc1(); + gc2(); + gcClosure(); +} + +auto gcClosure() +{ + int x; + int bar() { return x; } + return &bar; +} diff --git a/tests/dmd/fail_compilation/attributediagnostic_nothrow.d b/tests/dmd/fail_compilation/attributediagnostic_nothrow.d new file mode 100644 index 00000000000..7fea3224412 --- /dev/null +++ b/tests/dmd/fail_compilation/attributediagnostic_nothrow.d @@ -0,0 +1,45 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/attributediagnostic_nothrow.d(21): Error: function `attributediagnostic_nothrow.layer1` is not `nothrow` +fail_compilation/attributediagnostic_nothrow.d(22): which calls `attributediagnostic_nothrow.layer0` +fail_compilation/attributediagnostic_nothrow.d(23): which calls `attributediagnostic_nothrow.gc` +fail_compilation/attributediagnostic_nothrow.d(27): which wasn't inferred `nothrow` because of: +fail_compilation/attributediagnostic_nothrow.d(27): `asm` statement is assumed to throw - mark it with `nothrow` if it does not +fail_compilation/attributediagnostic_nothrow.d(21): Error: function `attributediagnostic_nothrow.layer2` may throw but is marked as `nothrow` +fail_compilation/attributediagnostic_nothrow.d(43): Error: function `attributediagnostic_nothrow.gc1` is not `nothrow` +fail_compilation/attributediagnostic_nothrow.d(32): which wasn't inferred `nothrow` because of: +fail_compilation/attributediagnostic_nothrow.d(32): `object.Exception` is thrown but not caught +fail_compilation/attributediagnostic_nothrow.d(44): Error: function `attributediagnostic_nothrow.gc2` is not `nothrow` +fail_compilation/attributediagnostic_nothrow.d(41): Error: function `D main` may throw but is marked as `nothrow` +--- +*/ + +// Issue 17374 - Improve inferred attribute error message +// https://issues.dlang.org/show_bug.cgi?id=17374 + +auto layer2() nothrow { layer1(); } +auto layer1() { layer0(); } +auto layer0() { gc(); } + +auto gc() +{ + asm {} +} + +auto gc1() +{ + throw new Exception("msg"); +} + +auto fgc = function void() {throw new Exception("msg");}; +auto gc2() +{ + fgc(); +} + +void main() nothrow +{ + gc1(); + gc2(); +} diff --git a/tests/dmd/fail_compilation/attributediagnostic_pure.d b/tests/dmd/fail_compilation/attributediagnostic_pure.d new file mode 100644 index 00000000000..a120dabf852 --- /dev/null +++ b/tests/dmd/fail_compilation/attributediagnostic_pure.d @@ -0,0 +1,21 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/attributediagnostic_pure.d(20): Error: `pure` function `D main` cannot call impure function `attributediagnostic_pure.gc` +fail_compilation/attributediagnostic_pure.d(15): which wasn't inferred `pure` because of: +fail_compilation/attributediagnostic_pure.d(15): `asm` statement is assumed to be impure - mark it with `pure` if it is not +--- +*/ + +// Issue 17374 - Improve inferred attribute error message +// https://issues.dlang.org/show_bug.cgi?id=17374 + +auto gc() +{ + asm {} +} + +void main() pure +{ + gc(); +} diff --git a/tests/dmd/fail_compilation/attrpure.i b/tests/dmd/fail_compilation/attrpure.i new file mode 100644 index 00000000000..ae2ed880b33 --- /dev/null +++ b/tests/dmd/fail_compilation/attrpure.i @@ -0,0 +1,12 @@ +/* TEST_OUTPUT: +--- +fail_compilation/attrpure.i(11): Error: `pure` function `attrpure.pureAsSnow` cannot call impure function `attrpure.impure` +--- +*/ + +void impure(); + +__attribute__((pure)) void pureAsSnow() +{ + impure(); +} diff --git a/tests/dmd/fail_compilation/bug9631.d b/tests/dmd/fail_compilation/bug9631.d index c980d76a73d..802d1c2983e 100644 --- a/tests/dmd/fail_compilation/bug9631.d +++ b/tests/dmd/fail_compilation/bug9631.d @@ -66,8 +66,8 @@ fail_compilation/bug9631.d(79): Error: function `bug9631.arg.f(int i, S s)` is n fail_compilation/bug9631.d(79): cannot pass argument `y` of type `bug9631.tem!().S` to parameter `bug9631.S s` fail_compilation/bug9631.d(80): Error: function literal `__lambda4(S s)` is not callable using argument types `(S)` fail_compilation/bug9631.d(80): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S s` -fail_compilation/bug9631.d(86): Error: constructor `bug9631.arg.A.this(S _param_0)` is not callable using argument types `(S)` -fail_compilation/bug9631.d(86): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S _param_0` +fail_compilation/bug9631.d(86): Error: constructor `bug9631.arg.A.this(S __param_0)` is not callable using argument types `(S)` +fail_compilation/bug9631.d(86): cannot pass argument `S(0)` of type `bug9631.tem!().S` to parameter `bug9631.S __param_0` --- */ void arg() @@ -89,8 +89,8 @@ void arg() /* TEST_OUTPUT: --- -fail_compilation/bug9631.d(106): Error: function `bug9631.targ.ft!().ft(S _param_0)` is not callable using argument types `(S)` -fail_compilation/bug9631.d(106): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S _param_0` +fail_compilation/bug9631.d(106): Error: function `bug9631.targ.ft!().ft(S __param_0)` is not callable using argument types `(S)` +fail_compilation/bug9631.d(106): cannot pass argument `x` of type `bug9631.S` to parameter `bug9631.tem!().S __param_0` fail_compilation/bug9631.d(107): Error: none of the overloads of template `bug9631.targ.ft` are callable using argument types `!()(S)` fail_compilation/bug9631.d(105): Candidate is: `ft()(tem!().S)` fail_compilation/bug9631.d(109): Error: none of the overloads of template `bug9631.targ.ft2` are callable using argument types `!()(S, int)` diff --git a/tests/dmd/fail_compilation/cdeprecated.i b/tests/dmd/fail_compilation/cdeprecated.i new file mode 100644 index 00000000000..75ce2acbe6e --- /dev/null +++ b/tests/dmd/fail_compilation/cdeprecated.i @@ -0,0 +1,22 @@ +/* REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/cdeprecated.i(18): Deprecation: function `cdeprecated.mars` is deprecated +fail_compilation/cdeprecated.i(19): Deprecation: function `cdeprecated.jupiter` is deprecated - jumping jupiter +fail_compilation/cdeprecated.i(20): Deprecation: function `cdeprecated.saturn` is deprecated +fail_compilation/cdeprecated.i(21): Deprecation: function `cdeprecated.neptune` is deprecated - spinning neptune +--- +*/ +__declspec(deprecated) int mars(); +__declspec(deprecated("jumping jupiter")) int jupiter(); +__attribute__((deprecated)) extern int saturn(); +__attribute__((deprecated("spinning neptune"))) extern int neptune(); + +int test() +{ + return + mars() + + jupiter() + + saturn() + + neptune(); +} diff --git a/tests/dmd/fail_compilation/ctfeblock.d b/tests/dmd/fail_compilation/ctfeblock.d index 2d8bf7a0fef..9c901033223 100644 --- a/tests/dmd/fail_compilation/ctfeblock.d +++ b/tests/dmd/fail_compilation/ctfeblock.d @@ -19,7 +19,7 @@ struct T { } { L1: new T(); - a = 3; + a = 3; } goto L1; } @@ -31,3 +31,14 @@ L1: new T(); } } + +@nogc void test3() +{ + if (!__ctfe) + { + } + else + { + int* p = new int; + } +} diff --git a/tests/dmd/fail_compilation/deprecatedinref.d b/tests/dmd/fail_compilation/deprecatedinref.d new file mode 100644 index 00000000000..20c3666bef1 --- /dev/null +++ b/tests/dmd/fail_compilation/deprecatedinref.d @@ -0,0 +1,10 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/deprecatedinref.d(9): Deprecation: using `in ref` is deprecated, use `-preview=in` and `in` instead +fail_compilation/deprecatedinref.d(10): Deprecation: using `ref in` is deprecated, use `-preview=in` and `in` instead +--- +*/ +void foo(in ref int); +void foor(ref in int); diff --git a/tests/dmd/fail_compilation/deprecations_preview_in.d b/tests/dmd/fail_compilation/deprecations_preview_in.d new file mode 100644 index 00000000000..33cc904e4be --- /dev/null +++ b/tests/dmd/fail_compilation/deprecations_preview_in.d @@ -0,0 +1,11 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/deprecations_preview_in.d(1): Deprecation: using `in` parameters with `extern(C)` functions is deprecated +fail_compilation/deprecations_preview_in.d(1): parameter `__anonymous_param` declared as `in` here +--- +*/ + +#line 1 +extern(C) void fun1(in char*); diff --git a/tests/dmd/fail_compilation/diag10319.d b/tests/dmd/fail_compilation/diag10319.d index 7b2eca79b80..efc818f30be 100644 --- a/tests/dmd/fail_compilation/diag10319.d +++ b/tests/dmd/fail_compilation/diag10319.d @@ -1,17 +1,21 @@ /* TEST_OUTPUT: --- -fail_compilation/diag10319.d(29): Error: `pure` function `D main` cannot call impure function `diag10319.foo` -fail_compilation/diag10319.d(29): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` -fail_compilation/diag10319.d(18): `diag10319.foo` is declared here -fail_compilation/diag10319.d(30): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(30): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(23): which was inferred `@system` because of: -fail_compilation/diag10319.d(23): cannot take address of local `x` in `@safe` function `bar` -fail_compilation/diag10319.d(20): `diag10319.bar!int.bar` is declared here -fail_compilation/diag10319.d(29): Error: function `diag10319.foo` is not `nothrow` -fail_compilation/diag10319.d(30): Error: function `diag10319.bar!int.bar` is not `nothrow` -fail_compilation/diag10319.d(27): Error: function `D main` may throw but is marked as `nothrow` +fail_compilation/diag10319.d(33): Error: `pure` function `D main` cannot call impure function `diag10319.foo` +fail_compilation/diag10319.d(33): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` +fail_compilation/diag10319.d(22): `diag10319.foo` is declared here +fail_compilation/diag10319.d(34): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` +fail_compilation/diag10319.d(26): which wasn't inferred `pure` because of: +fail_compilation/diag10319.d(26): `pure` function `diag10319.bar!int.bar` cannot access mutable static data `g` +fail_compilation/diag10319.d(34): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` +fail_compilation/diag10319.d(27): which wasn't inferred `@safe` because of: +fail_compilation/diag10319.d(27): cannot take address of local `x` in `@safe` function `bar` +fail_compilation/diag10319.d(24): `diag10319.bar!int.bar` is declared here +fail_compilation/diag10319.d(33): Error: function `diag10319.foo` is not `nothrow` +fail_compilation/diag10319.d(34): Error: function `diag10319.bar!int.bar` is not `nothrow` +fail_compilation/diag10319.d(28): which wasn't inferred `nothrow` because of: +fail_compilation/diag10319.d(28): `object.Exception` is thrown but not caught +fail_compilation/diag10319.d(31): Error: function `D main` may throw but is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/diag10415.d b/tests/dmd/fail_compilation/diag10415.d index 1fde171b713..207f6a4aa15 100644 --- a/tests/dmd/fail_compilation/diag10415.d +++ b/tests/dmd/fail_compilation/diag10415.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/diag10415.d(36): Error: none of the overloads of `x` are callable using argument types `(int) const` fail_compilation/diag10415.d(13): Candidates are: `diag10415.C.x()` -fail_compilation/diag10415.d(18): `diag10415.C.x(int _param_0)` +fail_compilation/diag10415.d(18): `diag10415.C.x(int __param_0)` fail_compilation/diag10415.d(39): Error: d.x is not an lvalue --- */ diff --git a/tests/dmd/fail_compilation/diag11769.d b/tests/dmd/fail_compilation/diag11769.d index 2717de4a6e7..75047f53785 100644 --- a/tests/dmd/fail_compilation/diag11769.d +++ b/tests/dmd/fail_compilation/diag11769.d @@ -2,9 +2,9 @@ TEST_OUTPUT: --- fail_compilation/diag11769.d(18): Error: `diag11769.foo!string.bar` called with argument types `(string)` matches both: -fail_compilation/diag11769.d(13): `diag11769.foo!string.bar(wstring _param_0)` +fail_compilation/diag11769.d(13): `diag11769.foo!string.bar(wstring __param_0)` and: -fail_compilation/diag11769.d(14): `diag11769.foo!string.bar(dstring _param_0)` +fail_compilation/diag11769.d(14): `diag11769.foo!string.bar(dstring __param_0)` --- */ diff --git a/tests/dmd/fail_compilation/diag14818.d b/tests/dmd/fail_compilation/diag14818.d index f9b535ab8d7..6147f32d0db 100644 --- a/tests/dmd/fail_compilation/diag14818.d +++ b/tests/dmd/fail_compilation/diag14818.d @@ -2,8 +2,8 @@ TEST_OUTPUT: --- fail_compilation/diag14818.d(40): Error: none of the overloads of `func` are callable using argument types `(string)` -fail_compilation/diag14818.d(18): Candidates are: `diag14818.foo(int _param_0)` -fail_compilation/diag14818.d(19): `diag14818.bar(double _param_0)` +fail_compilation/diag14818.d(18): Candidates are: `diag14818.foo(int __param_0)` +fail_compilation/diag14818.d(19): `diag14818.bar(double __param_0)` fail_compilation/diag14818.d(41): Error: template instance `diag14818.X!string` does not match any template declaration fail_compilation/diag14818.d(41): Candidates are: fail_compilation/diag14818.d(24): Foo(T) if (is(T == int)) diff --git a/tests/dmd/fail_compilation/diag15974.d b/tests/dmd/fail_compilation/diag15974.d index 03f63f4c21a..1967bb1e4f2 100644 --- a/tests/dmd/fail_compilation/diag15974.d +++ b/tests/dmd/fail_compilation/diag15974.d @@ -22,7 +22,7 @@ void test15974() struct S { - // CompileDeclaration + // MixinDeclaration mixin(format("%s", f)); } } diff --git a/tests/dmd/fail_compilation/diag20268.d b/tests/dmd/fail_compilation/diag20268.d new file mode 100644 index 00000000000..a314561892a --- /dev/null +++ b/tests/dmd/fail_compilation/diag20268.d @@ -0,0 +1,12 @@ +// https://issues.dlang.org/show_bug.cgi?id=20268 + +/* +TEST_OUTPUT: +--- +fail_compilation/diag20268.d(12): Error: none of the overloads of template `diag20268.__lambda4` are callable using argument types `!()(int)` +fail_compilation/diag20268.d(11): Candidate is: `__lambda4(__T1, __T2)(x, y)` +--- +*/ + +alias f = (x,y) => true; +auto x = f(1); diff --git a/tests/dmd/fail_compilation/diag8101b.d b/tests/dmd/fail_compilation/diag8101b.d index bc0ee9d2fb7..a55ef731ad2 100644 --- a/tests/dmd/fail_compilation/diag8101b.d +++ b/tests/dmd/fail_compilation/diag8101b.d @@ -2,13 +2,13 @@ TEST_OUTPUT: --- fail_compilation/diag8101b.d(28): Error: none of the overloads of `foo` are callable using argument types `(double)` -fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int _param_0)` -fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int _param_0, int _param_1)` -fail_compilation/diag8101b.d(30): Error: function `diag8101b.S.bar(int _param_0)` is not callable using argument types `(double)` -fail_compilation/diag8101b.d(30): cannot pass argument `1.0` of type `double` to parameter `int _param_0` +fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int __param_0)` +fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int __param_0, int __param_1)` +fail_compilation/diag8101b.d(30): Error: function `diag8101b.S.bar(int __param_0)` is not callable using argument types `(double)` +fail_compilation/diag8101b.d(30): cannot pass argument `1.0` of type `double` to parameter `int __param_0` fail_compilation/diag8101b.d(33): Error: none of the overloads of `foo` are callable using a `const` object -fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int _param_0)` -fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int _param_0, int _param_1)` +fail_compilation/diag8101b.d(19): Candidates are: `diag8101b.S.foo(int __param_0)` +fail_compilation/diag8101b.d(20): `diag8101b.S.foo(int __param_0, int __param_1)` fail_compilation/diag8101b.d(35): Error: mutable method `diag8101b.S.bar` is not callable using a `const` object fail_compilation/diag8101b.d(22): Consider adding `const` or `inout` here --- diff --git a/tests/dmd/fail_compilation/diag9312.d b/tests/dmd/fail_compilation/diag9312.d index 94e3d3ffd43..98308133e02 100644 --- a/tests/dmd/fail_compilation/diag9312.d +++ b/tests/dmd/fail_compilation/diag9312.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9312.d(10): Error: `with` expressions must be aggregate types or pointers to them, not `int` +fail_compilation/diag9312.d(10): Error: `with` expression types must be enums or aggregates or pointers to them, not `int` --- */ diff --git a/tests/dmd/fail_compilation/diag9620.d b/tests/dmd/fail_compilation/diag9620.d index d99290c6a39..4af87df0a2f 100644 --- a/tests/dmd/fail_compilation/diag9620.d +++ b/tests/dmd/fail_compilation/diag9620.d @@ -1,8 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9620.d(18): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo1` -fail_compilation/diag9620.d(19): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo2!().foo2` +fail_compilation/diag9620.d(20): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo1` +fail_compilation/diag9620.d(21): Error: `pure` function `diag9620.main.bar` cannot call impure function `diag9620.foo2!().foo2` +fail_compilation/diag9620.d(14): which wasn't inferred `pure` because of: +fail_compilation/diag9620.d(14): `pure` function `diag9620.foo2!().foo2` cannot access mutable static data `x` --- */ diff --git a/tests/dmd/fail_compilation/diag9831.d b/tests/dmd/fail_compilation/diag9831.d index b990ced0522..c93a06a465a 100644 --- a/tests/dmd/fail_compilation/diag9831.d +++ b/tests/dmd/fail_compilation/diag9831.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3` cannot access variable `c` in frame of function `D main` +fail_compilation/diag9831.d(13): Error: function `diag9831.main.__lambda3(__T1)(x)` cannot access variable `c` in frame of function `D main` fail_compilation/diag9831.d(11): `c` declared here --- */ diff --git a/tests/dmd/fail_compilation/dip1000_deprecation.d b/tests/dmd/fail_compilation/dip1000_deprecation.d index bf51363c07e..61174395be3 100644 --- a/tests/dmd/fail_compilation/dip1000_deprecation.d +++ b/tests/dmd/fail_compilation/dip1000_deprecation.d @@ -3,11 +3,11 @@ REQUIRED_ARGS: -de TEST_OUTPUT: --- fail_compilation/dip1000_deprecation.d(20): Deprecation: `@safe` function `main` calling `inferred` -fail_compilation/dip1000_deprecation.d(28): which would be `@system` because of: +fail_compilation/dip1000_deprecation.d(28): which wouldn't be `@safe` because of: fail_compilation/dip1000_deprecation.d(28): scope variable `x0` may not be returned fail_compilation/dip1000_deprecation.d(22): Deprecation: `@safe` function `main` calling `inferredC` fail_compilation/dip1000_deprecation.d(39): which calls `dip1000_deprecation.inferred` -fail_compilation/dip1000_deprecation.d(28): which would be `@system` because of: +fail_compilation/dip1000_deprecation.d(28): which wouldn't be `@safe` because of: fail_compilation/dip1000_deprecation.d(28): scope variable `x0` may not be returned fail_compilation/dip1000_deprecation.d(54): Deprecation: escaping reference to stack allocated value returned by `S(null)` fail_compilation/dip1000_deprecation.d(55): Deprecation: escaping reference to stack allocated value returned by `createS()` diff --git a/tests/dmd/fail_compilation/dtor_attributes.d b/tests/dmd/fail_compilation/dtor_attributes.d index ce81d6bfd35..21a12ed0253 100644 --- a/tests/dmd/fail_compilation/dtor_attributes.d +++ b/tests/dmd/fail_compilation/dtor_attributes.d @@ -8,8 +8,6 @@ fail_compilation/dtor_attributes.d(113): generated `Strict.~this` is impu fail_compilation/dtor_attributes.d(111): - HasDtor member fail_compilation/dtor_attributes.d(103): impure `HasDtor.~this` is declared here fail_compilation/dtor_attributes.d(118): Error: `@safe` function `dtor_attributes.test1` cannot call `@system` destructor `dtor_attributes.Strict.~this` -fail_compilation/dtor_attributes.d(113): which calls `dtor_attributes.Strict.~this` -fail_compilation/dtor_attributes.d(103): which calls `dtor_attributes.HasDtor.~this` fail_compilation/dtor_attributes.d(113): `dtor_attributes.Strict.~this` is declared here fail_compilation/dtor_attributes.d(113): generated `Strict.~this` is @system because of the following field's destructors: fail_compilation/dtor_attributes.d(111): - HasDtor member diff --git a/tests/dmd/fail_compilation/dtorfields_attributes.d b/tests/dmd/fail_compilation/dtorfields_attributes.d index 45b23cece4d..f6cab893bb4 100644 --- a/tests/dmd/fail_compilation/dtorfields_attributes.d +++ b/tests/dmd/fail_compilation/dtorfields_attributes.d @@ -9,7 +9,6 @@ fail_compilation/dtorfields_attributes.d(119): generated `Strict.~this` i fail_compilation/dtorfields_attributes.d(115): - HasDtor member fail_compilation/dtorfields_attributes.d(103): impure `HasDtor.~this` is declared here fail_compilation/dtorfields_attributes.d(117): Error: `@safe` constructor `dtorfields_attributes.Strict.this` cannot call `@system` destructor `dtorfields_attributes.Strict.~this` -fail_compilation/dtorfields_attributes.d(103): which calls `dtorfields_attributes.HasDtor.~this` fail_compilation/dtorfields_attributes.d(119): `dtorfields_attributes.Strict.~this` is declared here fail_compilation/dtorfields_attributes.d(119): generated `Strict.~this` is @system because of the following field's destructors: fail_compilation/dtorfields_attributes.d(115): - HasDtor member diff --git a/tests/dmd/fail_compilation/enumtype.c b/tests/dmd/fail_compilation/enumtype.c index f6aae337e22..d283595f177 100644 --- a/tests/dmd/fail_compilation/enumtype.c +++ b/tests/dmd/fail_compilation/enumtype.c @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/enumtype.c(111): Error: enum member `enumtype.E2.A2` enum member value `549755813889L` does not fit in an `int` +fail_compilation/enumtype.c(111): Error: enum member `enumtype.E2.A2` enum member value `549755813889L` does not fit in `int` --- */ @@ -11,9 +11,9 @@ enum E1 { A1 = 0, B1 = sizeof(A1), C1 = 1LL, D1 = sizeof(C1), F1 = -1U, G1 }; _Static_assert(A1 == 0, "in"); _Static_assert(B1 == 4, "in"); _Static_assert(C1 == 1, "in"); -_Static_assert(D1 == 4, "in"); -_Static_assert(F1 == -1, "in"); -_Static_assert(G1 == 0, "in"); -_Static_assert(sizeof(enum E1) == 4, "in"); +_Static_assert(D1 == 8, "in"); +_Static_assert(F1 == -1U, "in"); +_Static_assert(G1 == 0x100000000, "in"); +_Static_assert(sizeof(enum E1) == 8, "in"); -enum E2 { A2 = 0x8000000001LL }; +enum E2 : int { A2 = 0x8000000001LL }; diff --git a/tests/dmd/fail_compilation/fail10968.d b/tests/dmd/fail_compilation/fail10968.d index cfda8f4d9ad..3b6c3a0610e 100644 --- a/tests/dmd/fail_compilation/fail10968.d +++ b/tests/dmd/fail_compilation/fail10968.d @@ -8,10 +8,14 @@ fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot fail_compilation/fail10968.d(44): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(44): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arraysetassign!(SA[], SA)._d_arraysetassign` +$p:/core/internal/array/arrayassign.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(45): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(45): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.arrayassign._d_arrayassign_l!(SA[], SA)._d_arrayassign_l` +$p:/core/internal/array/arrayassign.d$-mixin-$n$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` fail_compilation/fail10968.d(48): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(48): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here @@ -19,13 +23,18 @@ fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot fail_compilation/fail10968.d(49): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(49): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arraysetctor!(SA[], SA)._d_arraysetctor` +$p:/core/internal/array/construction.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `fail10968.SA.__postblit` fail_compilation/fail10968.d(50): Error: `@safe` function `fail10968.bar` cannot call `@system` function `fail10968.SA.__postblit` fail_compilation/fail10968.d(31): `fail10968.SA.__postblit` is declared here fail_compilation/fail10968.d(50): Error: `pure` function `fail10968.bar` cannot call impure function `core.internal.array.construction._d_arrayctor!(SA[], SA)._d_arrayctor` +$p:/core/internal/array/construction.d$($n$): which calls `core.lifetime.copyEmplace!(SA, SA).copyEmplace` +$p:/core/lifetime.d$($n$): which calls `fail10968.SA.__postblit` --- */ +#line 29 struct SA { this(this) diff --git a/tests/dmd/fail_compilation/fail11375.d b/tests/dmd/fail_compilation/fail11375.d index 7592a5a1dfd..cabf87a6cae 100644 --- a/tests/dmd/fail_compilation/fail11375.d +++ b/tests/dmd/fail_compilation/fail11375.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail11375.d(17): Error: constructor `fail11375.D!().D.this` is not `nothrow` -fail_compilation/fail11375.d(15): Error: function `D main` may throw but is marked as `nothrow` +fail_compilation/fail11375.d(18): Error: constructor `fail11375.D!().D.this` is not `nothrow` + which calls `fail11375.B.this` +fail_compilation/fail11375.d(16): Error: function `D main` may throw but is marked as `nothrow` --- */ diff --git a/tests/dmd/fail_compilation/fail12236.d b/tests/dmd/fail_compilation/fail12236.d index 738864cca35..824f5e48db2 100644 --- a/tests/dmd/fail_compilation/fail12236.d +++ b/tests/dmd/fail_compilation/fail12236.d @@ -7,7 +7,7 @@ fail_compilation/fail12236.d(21): Error: forward reference to inferred return ty fail_compilation/fail12236.d(21): while evaluating `pragma(msg, f2(T)(T).mangleof)` fail_compilation/fail12236.d(27): Error: template instance `fail12236.f2!int` error instantiating fail_compilation/fail12236.d(31): Error: forward reference to inferred return type of function `__lambda1` -fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda1.mangleof)` +fail_compilation/fail12236.d(31): while evaluating `pragma(msg, __lambda1(__T1)(a).mangleof)` --- */ diff --git a/tests/dmd/fail_compilation/fail13120.d b/tests/dmd/fail_compilation/fail13120.d index f1cf340b6a0..6a1335eebd5 100644 --- a/tests/dmd/fail_compilation/fail13120.d +++ b/tests/dmd/fail_compilation/fail13120.d @@ -17,13 +17,13 @@ void g1(char[] s) pure @nogc TEST_OUTPUT: --- fail_compilation/fail13120.d(35): Error: `pure` function `fail13120.h2` cannot call impure function `fail13120.g2!().g2` +fail_compilation/fail13120.d(30): which calls `fail13120.f2` fail_compilation/fail13120.d(35): Error: `@safe` function `fail13120.h2` cannot call `@system` function `fail13120.g2!().g2` fail_compilation/fail13120.d(27): `fail13120.g2!().g2` is declared here fail_compilation/fail13120.d(35): Error: `@nogc` function `fail13120.h2` cannot call non-@nogc function `fail13120.g2!().g2` --- */ void f2() {} - void g2()(char[] s) { foreach (dchar dc; s) diff --git a/tests/dmd/fail_compilation/fail13577.d b/tests/dmd/fail_compilation/fail13577.d new file mode 100644 index 00000000000..79f9068c759 --- /dev/null +++ b/tests/dmd/fail_compilation/fail13577.d @@ -0,0 +1,28 @@ +// https://issues.dlang.org/show_bug.cgi?id=13577 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail13577.d(27): Error: cannot implicilty convert range element of type `int[]` to variable `x` of type `immutable(int[])` +--- +*/ + +struct Tuple(Types...) +{ + Types items; + alias items this; +} + +struct Range(T) +{ + T[] arr; + alias ElemType = Tuple!(int, T); + ElemType front() { return typeof(return)(0, arr[0]); } + bool empty() { return false; } + void popFront() {} +} + +void main() +{ + foreach (immutable i, immutable x; Range!(int[])()) {} // Error +} diff --git a/tests/dmd/fail_compilation/fail16600.d b/tests/dmd/fail_compilation/fail16600.d index eb341c64af5..3bd600e507b 100644 --- a/tests/dmd/fail_compilation/fail16600.d +++ b/tests/dmd/fail_compilation/fail16600.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: --- fail_compilation/fail16600.d(22): Error: `fail16600.S.__ctor` called with argument types `(string) const` matches both: -fail_compilation/fail16600.d(16): `fail16600.S.this(string _param_0)` +fail_compilation/fail16600.d(16): `fail16600.S.this(string __param_0)` and: -fail_compilation/fail16600.d(17): `fail16600.S.this(string _param_0) immutable` +fail_compilation/fail16600.d(17): `fail16600.S.this(string __param_0) immutable` --- */ diff --git a/tests/dmd/fail_compilation/fail17518.d b/tests/dmd/fail_compilation/fail17518.d index 385483c048d..cf2648db40b 100644 --- a/tests/dmd/fail_compilation/fail17518.d +++ b/tests/dmd/fail_compilation/fail17518.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail17518.d(21): Error: constructor `fail17518.S.this(inout(Correct) _param_0) inout` is not callable using argument types `(Wrong)` -fail_compilation/fail17518.d(21): cannot pass argument `Wrong()` of type `Wrong` to parameter `inout(Correct) _param_0` +fail_compilation/fail17518.d(21): Error: constructor `fail17518.S.this(inout(Correct) __param_0) inout` is not callable using argument types `(Wrong)` +fail_compilation/fail17518.d(21): cannot pass argument `Wrong()` of type `Wrong` to parameter `inout(Correct) __param_0` --- */ diff --git a/tests/dmd/fail_compilation/fail17955.d b/tests/dmd/fail_compilation/fail17955.d index 95eb5cc8c1f..08329194228 100644 --- a/tests/dmd/fail_compilation/fail17955.d +++ b/tests/dmd/fail_compilation/fail17955.d @@ -11,7 +11,7 @@ fail_compilation/fail17955.d(49): instantiated from here: `toRedis!(SysTi fail_compilation/fail17955.d(40): ... (2 instantiations, -v to show) ... fail_compilation/fail17955.d(32): instantiated from here: `indicesOf!(isRedisType, resetCodeExpireTime)` fail_compilation/fail17955.d(67): instantiated from here: `RedisStripped!(User, true)` -fail_compilation/fail17955.d(93): Error: need `this` for `fromISOExtString` of type `pure nothrow @nogc @safe immutable(SimpleTimeZone)(dstring _param_0)` +fail_compilation/fail17955.d(93): Error: need `this` for `fromISOExtString` of type `pure nothrow @nogc @safe immutable(SimpleTimeZone)(dstring __param_0)` fail_compilation/fail17955.d(95): Error: undefined identifier `DateTimeException` fail_compilation/fail17955.d(25): Error: variable `fail17955.isISOExtStringSerializable!(SysTime).isISOExtStringSerializable` - type `void` is inferred from initializer `fromISOExtString("")`, and variables cannot be of type `void` fail_compilation/fail17955.d(54): Error: function `fail17955.toRedis!(SysTime).toRedis` has no `return` statement, but is expected to return a value of type `string` diff --git a/tests/dmd/fail_compilation/fail196.d b/tests/dmd/fail_compilation/fail196.d index 2c7d93fe4e0..53505f496c2 100644 --- a/tests/dmd/fail_compilation/fail196.d +++ b/tests/dmd/fail_compilation/fail196.d @@ -6,15 +6,15 @@ fail_compilation/fail196.d(27): Error: implicit string concatenation is error-pr fail_compilation/fail196.d(27): Use the explicit syntax instead (concatenating literals is `@nogc`): "foo(xxx)" ~ ";\n assert(s == " fail_compilation/fail196.d(28): Error: semicolon needed to end declaration of `s`, instead of `foo` fail_compilation/fail196.d(27): `s` declared here -fail_compilation/fail196.d(28): Error: found `");\n\n s = q"` when expecting `;` following statement -fail_compilation/fail196.d(30): Error: found `";\n assert(s == "` when expecting `;` following statement -fail_compilation/fail196.d(31): Error: found `");\n\n s = q"` when expecting `;` following statement -fail_compilation/fail196.d(33): Error: found `{` when expecting `;` following statement -fail_compilation/fail196.d(33): Error: found `}` when expecting `;` following statement -fail_compilation/fail196.d(34): Error: found `foo` when expecting `;` following statement -fail_compilation/fail196.d(34): Error: found `}` when expecting `;` following statement -fail_compilation/fail196.d(36): Error: found `<` when expecting `;` following statement -fail_compilation/fail196.d(37): Error: found `foo` when expecting `;` following statement +fail_compilation/fail196.d(28): Error: found `");\n\n s = q"` when expecting `;` following statement `foo(xxx)` on line fail_compilation/fail196.d(28) +fail_compilation/fail196.d(30): Error: found `";\n assert(s == "` when expecting `;` following statement `[foo[xxx]]` on line fail_compilation/fail196.d(30) +fail_compilation/fail196.d(31): Error: found `");\n\n s = q"` when expecting `;` following statement `foo[xxx]` on line fail_compilation/fail196.d(31) +fail_compilation/fail196.d(33): Error: found `{` when expecting `;` following statement `foo` on line fail_compilation/fail196.d(33) +fail_compilation/fail196.d(33): Error: found `}` when expecting `;` following statement `xxx` on line fail_compilation/fail196.d(33) +fail_compilation/fail196.d(34): Error: found `foo` when expecting `;` following statement `";\n assert(s == "` on line fail_compilation/fail196.d(33) +fail_compilation/fail196.d(34): Error: found `}` when expecting `;` following statement `xxx` on line fail_compilation/fail196.d(34) +fail_compilation/fail196.d(36): Error: found `<` when expecting `;` following statement `");\n\n s = q" < foo` on line fail_compilation/fail196.d(34) +fail_compilation/fail196.d(37): Error: found `foo` when expecting `;` following statement `xxx >> ";\n assert(s == "` on line fail_compilation/fail196.d(36) fail_compilation/fail196.d(37): Error: found `<` instead of statement fail_compilation/fail196.d(43): Error: unterminated string constant starting at fail_compilation/fail196.d(43) fail_compilation/fail196.d(45): Error: found `End of File` when expecting `}` following compound statement diff --git a/tests/dmd/fail_compilation/fail19948.d b/tests/dmd/fail_compilation/fail19948.d index e8a9e777904..ae67443fea4 100644 --- a/tests/dmd/fail_compilation/fail19948.d +++ b/tests/dmd/fail_compilation/fail19948.d @@ -1,5 +1,5 @@ // https://issues.dlang.org/show_bug.cgi?id=19948 - +// DISABLED: win32 /* TEST_OUTPUT: --- diff --git a/tests/dmd/fail_compilation/fail20609.d b/tests/dmd/fail_compilation/fail20609.d index 05b7c85375a..80a5d461574 100644 --- a/tests/dmd/fail_compilation/fail20609.d +++ b/tests/dmd/fail_compilation/fail20609.d @@ -4,7 +4,7 @@ fail_compilation/fail20609.d(26): Error: none of the overloads of `this` are callable using argument types `(int)` fail_compilation/fail20609.d(23): Candidate is: `fail20609.Foo.this(string[] args)` fail_compilation/fail20609.d(27): Error: none of the overloads of `this` are callable using argument types `(int)` -fail_compilation/fail20609.d(22): Candidates are: `fail20609.Foo.this(Object _param_0)` +fail_compilation/fail20609.d(22): Candidates are: `fail20609.Foo.this(Object __param_0)` fail_compilation/fail20609.d(23): `fail20609.Foo.this(string[] args)` fail_compilation/fail20609.d(37): Error: none of the overloads of `this` are callable using argument types `(int)` fail_compilation/fail20609.d(37): All possible candidates are marked as `deprecated` or `@disable` diff --git a/tests/dmd/fail_compilation/fail22202.d b/tests/dmd/fail_compilation/fail22202.d index 167d3624879..d865fd95553 100644 --- a/tests/dmd/fail_compilation/fail22202.d +++ b/tests/dmd/fail_compilation/fail22202.d @@ -3,7 +3,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail22202.d(21): Error: function `fail22202.fun(SystemCopy _param_0)` is not callable using argument types `(SystemCopy)` +fail_compilation/fail22202.d(21): Error: function `fail22202.fun(SystemCopy __param_0)` is not callable using argument types `(SystemCopy)` fail_compilation/fail22202.d(21): `inout ref inout(SystemCopy)(ref inout(SystemCopy) other)` copy constructor cannot be called from a `pure @safe nogc` context --- */ diff --git a/tests/dmd/fail_compilation/fail22729.d b/tests/dmd/fail_compilation/fail22729.d new file mode 100644 index 00000000000..38bbfeeed2b --- /dev/null +++ b/tests/dmd/fail_compilation/fail22729.d @@ -0,0 +1,39 @@ +// https://issues.dlang.org/show_bug.cgi?id=22729 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail22729.d(12): Error: field `getChildAtPosition` not allowed in interface +--- +*/ + +interface ContainerFunctionSetI +{ + Tuple!(WidgetI) getChildAtPosition; +} + +interface WidgetI : ContainerFunctionSetI +{ +} + +class Form : WidgetI +{ +} + +template Tuple(Specs) +{ + enum areCompatibleTuples(Tup2)(Tuple tup1, Tup2 tup2) + { + tup1.field == tup2; + } + + struct Tuple + { + Specs field; + + bool opEquals(R)(R) if (areCompatibleTuples!R) + { + } + + } +} diff --git a/tests/dmd/fail_compilation/fail23773.d b/tests/dmd/fail_compilation/fail23773.d new file mode 100644 index 00000000000..e6cdc3e0354 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23773.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=23773 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23773.d(14): Error: assignment cannot be used as a condition, perhaps `==` was meant? +--- +*/ + +void main() +{ + int i; + int[] arr; + assert(arr.length = i); +} diff --git a/tests/dmd/fail_compilation/fail23822.d b/tests/dmd/fail_compilation/fail23822.d new file mode 100644 index 00000000000..5cdd1fe0503 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23822.d @@ -0,0 +1,22 @@ +// https://issues.dlang.org/show_bug.cgi?id=23822 + +// REQUIRED_ARGS: -de + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23822.d(21): Deprecation: alias `fail23822.S.value` is deprecated +--- +*/ + +alias Alias(alias A) = A; + +struct S +{ + deprecated alias value = Alias!5; +} + +void main() +{ + auto a = S.value; +} diff --git a/tests/dmd/fail_compilation/fail23826.d b/tests/dmd/fail_compilation/fail23826.d new file mode 100644 index 00000000000..3db243a3ce8 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23826.d @@ -0,0 +1,24 @@ +// https://issues.dlang.org/show_bug.cgi?id=23826 + +// REQUIRED_ARGS: -de + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23826.d(23): Deprecation: alias `fail23826.S.value` is deprecated +--- +*/ + +alias Alias(alias A) = A; + +class S +{ + deprecated alias value = Alias!5; +} + +enum identity(alias A) = A; + +void main() +{ + auto a = identity!(S.value); +} diff --git a/tests/dmd/fail_compilation/fail23861.d b/tests/dmd/fail_compilation/fail23861.d new file mode 100644 index 00000000000..23c540799e6 --- /dev/null +++ b/tests/dmd/fail_compilation/fail23861.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=23861 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail23861.d(24): Error: cannot implicitly convert expression `3` of type `int` to `Foo` +--- +*/ + +Foo global; + +struct Foo +{ + ref Foo get() + { + return global; + } + alias get this; +} + +void main() +{ + Foo g; + g = 3; +} diff --git a/tests/dmd/fail_compilation/fail332.d b/tests/dmd/fail_compilation/fail332.d index 91f80464705..77e8cd8eb00 100644 --- a/tests/dmd/fail_compilation/fail332.d +++ b/tests/dmd/fail_compilation/fail332.d @@ -1,14 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/fail332.d(22): Error: function `fail332.foo(int _param_0, ...)` is not callable using argument types `()` -fail_compilation/fail332.d(22): missing argument for parameter #1: `int _param_0` -fail_compilation/fail332.d(23): Error: function `fail332.foo(int _param_0, ...)` is not callable using argument types `(typeof(null))` -fail_compilation/fail332.d(23): cannot pass argument `null` of type `typeof(null)` to parameter `int _param_0` -fail_compilation/fail332.d(25): Error: function `fail332.baz(int[] _param_0...)` is not callable using argument types `(string)` -fail_compilation/fail332.d(25): cannot pass argument `""` of type `string` to parameter `int[] _param_0...` -fail_compilation/fail332.d(26): Error: function `fail332.baz(int[] _param_0...)` is not callable using argument types `(int, typeof(null))` -fail_compilation/fail332.d(26): cannot pass argument `null` of type `typeof(null)` to parameter `int[] _param_0...` +fail_compilation/fail332.d(22): Error: function `fail332.foo(int __param_0, ...)` is not callable using argument types `()` +fail_compilation/fail332.d(22): missing argument for parameter #1: `int __param_0` +fail_compilation/fail332.d(23): Error: function `fail332.foo(int __param_0, ...)` is not callable using argument types `(typeof(null))` +fail_compilation/fail332.d(23): cannot pass argument `null` of type `typeof(null)` to parameter `int __param_0` +fail_compilation/fail332.d(25): Error: function `fail332.baz(int[] __param_0...)` is not callable using argument types `(string)` +fail_compilation/fail332.d(25): cannot pass argument `""` of type `string` to parameter `int[] __param_0...` +fail_compilation/fail332.d(26): Error: function `fail332.baz(int[] __param_0...)` is not callable using argument types `(int, typeof(null))` +fail_compilation/fail332.d(26): cannot pass argument `null` of type `typeof(null)` to parameter `int[] __param_0...` --- */ diff --git a/tests/dmd/fail_compilation/fail4375q.d b/tests/dmd/fail_compilation/fail4375q.d index b02fbb169b7..f57e746b6ed 100644 --- a/tests/dmd/fail_compilation/fail4375q.d +++ b/tests/dmd/fail_compilation/fail4375q.d @@ -4,7 +4,7 @@ TEST_OUTPUT: --- fail_compilation/fail4375q.d(17): Warning: else is dangling, add { } after condition at fail_compilation/fail4375q.d(13) -fail_compilation/fail4375q.d(14): Error: `with` expressions must be aggregate types or pointers to them, not `int` +fail_compilation/fail4375q.d(14): Error: `with` expression types must be enums or aggregates or pointers to them, not `int` --- */ diff --git a/tests/dmd/fail_compilation/fail4559.d b/tests/dmd/fail_compilation/fail4559.d index 0101ae98bf1..657c184d787 100644 --- a/tests/dmd/fail_compilation/fail4559.d +++ b/tests/dmd/fail_compilation/fail4559.d @@ -1,10 +1,10 @@ /* -REQUIRED_ARGS: -o- -de +REQUIRED_ARGS: TEST_OUTPUT: --- -fail_compilation/fail4559.d(13): Deprecation: use `{ }` for an empty statement, not `;` -fail_compilation/fail4559.d(19): Deprecation: use `{ }` for an empty statement, not `;` -fail_compilation/fail4559.d(21): Deprecation: use `{ }` for an empty statement, not `;` +fail_compilation/fail4559.d(13): Error: use `{ }` for an empty statement, not `;` +fail_compilation/fail4559.d(19): Error: use `{ }` for an empty statement, not `;` +fail_compilation/fail4559.d(21): Error: use `{ }` for an empty statement, not `;` --- */ diff --git a/tests/dmd/fail_compilation/fail_typeof.d b/tests/dmd/fail_compilation/fail_typeof.d index 392cebd2f73..a3b4d1a1415 100644 --- a/tests/dmd/fail_compilation/fail_typeof.d +++ b/tests/dmd/fail_compilation/fail_typeof.d @@ -1,17 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/fail_typeof.d(18): Error: undefined identifier `this` -fail_compilation/fail_typeof.d(23): Error: `this` is not in a class or struct scope -fail_compilation/fail_typeof.d(23): Error: `this` is only defined in non-static member functions, not `fail_typeof` -fail_compilation/fail_typeof.d(28): Error: undefined identifier `super` -fail_compilation/fail_typeof.d(33): Error: `super` is not in a class scope -fail_compilation/fail_typeof.d(33): Error: `super` is only allowed in non-static class member functions -fail_compilation/fail_typeof.d(40): Error: undefined identifier `this`, did you mean `typeof(this)`? -fail_compilation/fail_typeof.d(50): Error: undefined identifier `super` -fail_compilation/fail_typeof.d(55): Error: `super` is not in a class scope -fail_compilation/fail_typeof.d(55): Error: `super` is only allowed in non-static class member functions -fail_compilation/fail_typeof.d(63): Error: undefined identifier `this`, did you mean `typeof(this)`? -fail_compilation/fail_typeof.d(73): Error: undefined identifier `super`, did you mean `typeof(super)`? +fail_compilation/fail_typeof.d(15): Error: undefined identifier `this` +fail_compilation/fail_typeof.d(20): Error: `this` is not in a class or struct scope +fail_compilation/fail_typeof.d(25): Error: undefined identifier `super` +fail_compilation/fail_typeof.d(30): Error: `super` is not in a class scope +fail_compilation/fail_typeof.d(37): Error: undefined identifier `this`, did you mean `typeof(this)`? +fail_compilation/fail_typeof.d(47): Error: undefined identifier `super` +fail_compilation/fail_typeof.d(52): Error: `super` is not in a class scope +fail_compilation/fail_typeof.d(60): Error: undefined identifier `this`, did you mean `typeof(this)`? +fail_compilation/fail_typeof.d(70): Error: undefined identifier `super`, did you mean `typeof(super)`? --- */ diff --git a/tests/dmd/fail_compilation/failcstuff4.i b/tests/dmd/fail_compilation/failcstuff4b.i similarity index 62% rename from tests/dmd/fail_compilation/failcstuff4.i rename to tests/dmd/fail_compilation/failcstuff4b.i index df26647a5c4..66ee7578947 100644 --- a/tests/dmd/fail_compilation/failcstuff4.i +++ b/tests/dmd/fail_compilation/failcstuff4b.i @@ -1,6 +1,6 @@ /* TEST_OUTPUT: --- -fail_compilation/failcstuff4.i(605): Error: invalid flag for line marker directive +fail_compilation/failcstuff4b.i(605): Error: invalid flag for line marker directive --- */ diff --git a/tests/dmd/fail_compilation/failcstuff6.c b/tests/dmd/fail_compilation/failcstuff6.c index af486745444..88c541ca64f 100644 --- a/tests/dmd/fail_compilation/failcstuff6.c +++ b/tests/dmd/fail_compilation/failcstuff6.c @@ -2,11 +2,6 @@ /* TEST_OUTPUT: --- fail_compilation/failcstuff6.c(56): Error: enum member `failcstuff6.test_overflow.boom` initialization with `2147483647+1` causes overflow for type `int` -fail_compilation/failcstuff6.c(105): Error: enum member `failcstuff6.test_enum_fits.firstMinError` enum member value `-2147483649L` does not fit in an `int` -fail_compilation/failcstuff6.c(106): Error: enum member `failcstuff6.test_enum_fits.firstMaxError` enum member value `4294967296L` does not fit in an `int` -fail_compilation/failcstuff6.c(107): Error: enum member `failcstuff6.test_enum_fits.lastMaxError` enum member value `18446744071562067967LU` does not fit in an `int` -fail_compilation/failcstuff6.c(108): Error: enum member `failcstuff6.test_enum_fits.firstBlindSpot` enum member value `18446744071562067968LU` does not fit in an `int` -fail_compilation/failcstuff6.c(109): Error: enum member `failcstuff6.test_enum_fits.lastBlindSpot` enum member value `18446744073709551615LU` does not fit in an `int` --- */ diff --git a/tests/dmd/fail_compilation/gccasm1.c b/tests/dmd/fail_compilation/gccasm1.c new file mode 100644 index 00000000000..3ba12bba057 --- /dev/null +++ b/tests/dmd/fail_compilation/gccasm1.c @@ -0,0 +1,18 @@ +/* TEST_OUTPUT: +--- +fail_compilation/gccasm1.c(12): Error: string literal expected for Assembler Template, not `%` +--- + */ + +#define __fldcw(addr) asm volatile(%0 : : "m" (*(addr))) + +static __inline void +__fnldcw(unsigned short _cw, unsigned short _newcw) +{ + __fldcw(&_newcw); +} + +void main() +{ + __fnldcw(1, 2); +} diff --git a/tests/dmd/fail_compilation/ice10651.d b/tests/dmd/fail_compilation/ice10651.d index 1f87955b959..8b6c7200bb7 100644 --- a/tests/dmd/fail_compilation/ice10651.d +++ b/tests/dmd/fail_compilation/ice10651.d @@ -1,7 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10651.d(11): Error: can only throw class objects derived from `Throwable`, not type `int*` +fail_compilation/ice10651.d(13): Error: can only throw class objects derived from `Throwable`, not type `int*` +fail_compilation/ice10651.d(19): Deprecation: cannot throw object of qualified type `immutable(Exception)` +fail_compilation/ice10651.d(20): Deprecation: cannot throw object of qualified type `const(Dummy)` --- */ @@ -10,3 +12,20 @@ void main() alias T = int; throw new T(); // ICE } + +void f() +{ + immutable c = new Exception(""); + if (c) throw c; + throw new const Dummy([]); +} + +class Dummy: Exception +{ + int[] data; + @safe pure nothrow this(immutable int[] data) immutable + { + super("Dummy"); + this.data = data; + } +} diff --git a/tests/dmd/fail_compilation/ice11626.d b/tests/dmd/fail_compilation/ice11626.d index 5dc5d5c1e66..6d347bcdd0e 100644 --- a/tests/dmd/fail_compilation/ice11626.d +++ b/tests/dmd/fail_compilation/ice11626.d @@ -5,4 +5,4 @@ fail_compilation/ice11626.d(8): Error: undefined identifier `Bar` --- */ -void foo(in ref Bar) {} +void foo(const ref Bar) {} diff --git a/tests/dmd/fail_compilation/ice11982.d b/tests/dmd/fail_compilation/ice11982.d index ff5fae491c6..0f2ce413c40 100644 --- a/tests/dmd/fail_compilation/ice11982.d +++ b/tests/dmd/fail_compilation/ice11982.d @@ -1,16 +1,19 @@ /* TEST_OUTPUT: --- -fail_compilation/ice11982.d(16): Error: basic type expected, not `scope` -fail_compilation/ice11982.d(16): Error: found `scope` when expecting `;` following statement -fail_compilation/ice11982.d(16): Error: basic type expected, not `}` -fail_compilation/ice11982.d(16): Error: missing `{ ... }` for function literal -fail_compilation/ice11982.d(16): Error: C style cast illegal, use `cast(funk)function _error_() +fail_compilation/ice11982.d(19): Error: basic type expected, not `scope` +fail_compilation/ice11982.d(19): Error: found `scope` when expecting `;` following statement `new _error_` on line fail_compilation/ice11982.d(19) +fail_compilation/ice11982.d(19): Error: basic type expected, not `}` +fail_compilation/ice11982.d(19): Error: missing `{ ... }` for function literal +fail_compilation/ice11982.d(19): Error: C style cast illegal, use `cast(funk)function _error_() { } ` -fail_compilation/ice11982.d(16): Error: found `}` when expecting `;` following statement -fail_compilation/ice11982.d(17): Error: found `End of File` when expecting `}` following compound statement +fail_compilation/ice11982.d(19): Error: found `}` when expecting `;` following statement `cast(funk)function _error_() +{ +} +` on line fail_compilation/ice11982.d(19) +fail_compilation/ice11982.d(20): Error: found `End of File` when expecting `}` following compound statement --- */ void main() { new scope ( funk ) function } diff --git a/tests/dmd/fail_compilation/ice13225.d b/tests/dmd/fail_compilation/ice13225.d index 6988cd7c18d..abc30eaf4f2 100644 --- a/tests/dmd/fail_compilation/ice13225.d +++ b/tests/dmd/fail_compilation/ice13225.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice13225.d(12): Error: mixin `ice13225.S.M!(function (S _param_0) pure nothrow @nogc @safe => 0)` does not match template declaration `M(T)` +fail_compilation/ice13225.d(12): Error: mixin `ice13225.S.M!(function (S __param_0) pure nothrow @nogc @safe => 0)` does not match template declaration `M(T)` fail_compilation/ice13225.d(16): Error: undefined identifier `undefined` --- */ diff --git a/tests/dmd/fail_compilation/ice23097.d b/tests/dmd/fail_compilation/ice23097.d index 4fd1f61f828..90cf03f1b48 100644 --- a/tests/dmd/fail_compilation/ice23097.d +++ b/tests/dmd/fail_compilation/ice23097.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/ice23097.d(12): Error: undefined identifier `ICE` fail_compilation/ice23097.d(27): Error: template instance `ice23097.ice23097!(S23097)` error instantiating -fail_compilation/ice23097.d(27): Error: function `ice23097.ice23097!(S23097).ice23097(S23097 _param_0)` is not callable using argument types `(S23097)` +fail_compilation/ice23097.d(27): Error: function `ice23097.ice23097!(S23097).ice23097(S23097 __param_0)` is not callable using argument types `(S23097)` fail_compilation/ice23097.d(27): generating a copy constructor for `struct S23097` failed, therefore instances of it are uncopyable --- */ diff --git a/tests/dmd/fail_compilation/ice9540.d b/tests/dmd/fail_compilation/ice9540.d index 5276e83c722..456d3e12f23 100644 --- a/tests/dmd/fail_compilation/ice9540.d +++ b/tests/dmd/fail_compilation/ice9540.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice9540.d(35): Error: function `ice9540.A.test.AddFront!(this, f).AddFront.dg(int _param_0)` is not callable using argument types `()` +fail_compilation/ice9540.d(35): Error: function `ice9540.A.test.AddFront!(this, f).AddFront.dg(int __param_0)` is not callable using argument types `()` fail_compilation/ice9540.d(35): too few arguments, expected 1, got 0 fail_compilation/ice9540.d(26): Error: template instance `ice9540.A.test.AddFront!(this, f)` error instantiating --- diff --git a/tests/dmd/fail_compilation/imports/import23873.d b/tests/dmd/fail_compilation/imports/import23873.d new file mode 100644 index 00000000000..39334cf62ef --- /dev/null +++ b/tests/dmd/fail_compilation/imports/import23873.d @@ -0,0 +1,2 @@ +static if ; +else auto x diff --git a/tests/dmd/fail_compilation/misc_parser_err_cov1.d b/tests/dmd/fail_compilation/misc_parser_err_cov1.d index 11fddf069d6..d1361440920 100644 --- a/tests/dmd/fail_compilation/misc_parser_err_cov1.d +++ b/tests/dmd/fail_compilation/misc_parser_err_cov1.d @@ -23,7 +23,7 @@ fail_compilation/misc_parser_err_cov1.d(40): Error: semicolon expected following fail_compilation/misc_parser_err_cov1.d(40): Error: identifier or `new` expected following `.`, not `+` fail_compilation/misc_parser_err_cov1.d(41): Error: identifier or new keyword expected following `(...)`. fail_compilation/misc_parser_err_cov1.d(41): Error: expression expected, not `;` -fail_compilation/misc_parser_err_cov1.d(42): Error: found `}` when expecting `;` following statement +fail_compilation/misc_parser_err_cov1.d(42): Error: found `}` when expecting `;` following statement `(__error) + 0` on line fail_compilation/misc_parser_err_cov1.d(41) fail_compilation/misc_parser_err_cov1.d(43): Error: found `End of File` when expecting `}` following compound statement --- */ diff --git a/tests/dmd/fail_compilation/named_arguments_parse.d b/tests/dmd/fail_compilation/named_arguments_parse.d index 19e230ee519..096c499790e 100644 --- a/tests/dmd/fail_compilation/named_arguments_parse.d +++ b/tests/dmd/fail_compilation/named_arguments_parse.d @@ -1,13 +1,13 @@ /** TEST_OUTPUT: --- -fail_compilation/named_arguments_parse.d(10): Error: named arguments not allowed here fail_compilation/named_arguments_parse.d(13): Error: named arguments not allowed here fail_compilation/named_arguments_parse.d(14): Error: named arguments not allowed here --- */ -@(attribute: 3) + +// @(attribute: 3) Currently gives an ugly parse error, will be better when named template arguments are implemented void main() { mixin(thecode: "{}"); diff --git a/tests/dmd/fail_compilation/parseStc.d b/tests/dmd/fail_compilation/parseStc.d index c9c42882438..d13006db0ab 100644 --- a/tests/dmd/fail_compilation/parseStc.d +++ b/tests/dmd/fail_compilation/parseStc.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/parseStc.d(12): Error: missing closing `)` after `if (x` fail_compilation/parseStc.d(12): Error: use `{ }` for an empty statement, not `;` -fail_compilation/parseStc.d(12): Error: found `)` when expecting `;` following statement +fail_compilation/parseStc.d(12): Error: found `)` when expecting `;` following statement `1` on line fail_compilation/parseStc.d(12) fail_compilation/parseStc.d(13): Error: redundant attribute `const` --- */ diff --git a/tests/dmd/fail_compilation/previewin.d b/tests/dmd/fail_compilation/previewin.d index ca540930129..d0e97c8bcd3 100644 --- a/tests/dmd/fail_compilation/previewin.d +++ b/tests/dmd/fail_compilation/previewin.d @@ -4,10 +4,10 @@ TEST_OUTPUT: --- fail_compilation/previewin.d(4): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(real x) pure nothrow @nogc @safe)` fail_compilation/previewin.d(4): cannot pass argument `__lambda1` of type `void function(real x) pure nothrow @nogc @safe` to parameter `void function(in real) f` -fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(const(real) x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` -fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref const(real) x) pure nothrow @nogc @safe)` -fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(5): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(scope const(real) x) pure nothrow @nogc @safe)` +fail_compilation/previewin.d(5): cannot pass argument `__lambda2` of type `void function(scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` +fail_compilation/previewin.d(6): Error: function `previewin.takeFunction(void function(in real) f)` is not callable using argument types `(void function(ref scope const(real) x) pure nothrow @nogc @safe)` +fail_compilation/previewin.d(6): cannot pass argument `__lambda3` of type `void function(ref scope const(real) x) pure nothrow @nogc @safe` to parameter `void function(in real) f` fail_compilation/previewin.d(15): Error: scope variable `arg` assigned to global variable `myGlobal` fail_compilation/previewin.d(16): Error: scope variable `arg` assigned to global variable `myGlobal` fail_compilation/previewin.d(17): Error: scope parameter `arg` may not be returned diff --git a/tests/dmd/fail_compilation/retscope2.d b/tests/dmd/fail_compilation/retscope2.d index 829fb6a1970..2e7940f70fe 100644 --- a/tests/dmd/fail_compilation/retscope2.d +++ b/tests/dmd/fail_compilation/retscope2.d @@ -86,8 +86,8 @@ fail_compilation/retscope2.d(504): Error: scope variable `c` may not be returned /* TEST_OUTPUT: --- -fail_compilation/retscope2.d(604): Error: scope variable `_param_0` assigned to non-scope anonymous parameter calling `foo600` -fail_compilation/retscope2.d(604): Error: scope variable `_param_1` assigned to non-scope anonymous parameter calling `foo600` +fail_compilation/retscope2.d(604): Error: scope variable `__param_0` assigned to non-scope anonymous parameter calling `foo600` +fail_compilation/retscope2.d(604): Error: scope variable `__param_1` assigned to non-scope anonymous parameter calling `foo600` fail_compilation/retscope2.d(614): Error: template instance `retscope2.test600!(int*, int*)` error instantiating --- */ diff --git a/tests/dmd/fail_compilation/retscope6.d b/tests/dmd/fail_compilation/retscope6.d index 5c581d1db71..ddeae81bc23 100644 --- a/tests/dmd/fail_compilation/retscope6.d +++ b/tests/dmd/fail_compilation/retscope6.d @@ -25,7 +25,7 @@ int* test() @safe --- fail_compilation/retscope6.d(7034): Error: address of variable `i` assigned to `s` with longer lifetime fail_compilation/retscope6.d(7035): Error: address of variable `i` assigned to `s` with longer lifetime -fail_compilation/retscope6.d(7025): Error: scope variable `_param_2` assigned to `ref` variable `t` with longer lifetime +fail_compilation/retscope6.d(7025): Error: scope variable `__param_2` assigned to `ref` variable `t` with longer lifetime fail_compilation/retscope6.d(7037): Error: template instance `retscope6.S.emplace4!(int*)` error instantiating fail_compilation/retscope6.d(7037): Error: address of variable `i` assigned to `s` with longer lifetime --- diff --git a/tests/dmd/fail_compilation/systemvariables_deprecation.d b/tests/dmd/fail_compilation/systemvariables_deprecation.d index 75dbe2dc1a0..b5115351efe 100644 --- a/tests/dmd/fail_compilation/systemvariables_deprecation.d +++ b/tests/dmd/fail_compilation/systemvariables_deprecation.d @@ -4,7 +4,7 @@ TEST_OUTPUT: --- fail_compilation/systemvariables_deprecation.d(16): Deprecation: `@safe` function `main` calling `middle` fail_compilation/systemvariables_deprecation.d(21): which calls `systemvariables_deprecation.inferred` -fail_compilation/systemvariables_deprecation.d(27): which would be `@system` because of: +fail_compilation/systemvariables_deprecation.d(27): which wouldn't be `@safe` because of: fail_compilation/systemvariables_deprecation.d(27): cannot access `@system` variable `x0` in @safe code --- */ diff --git a/tests/dmd/fail_compilation/test21025.d b/tests/dmd/fail_compilation/test21025.d new file mode 100644 index 00000000000..40b3a96823d --- /dev/null +++ b/tests/dmd/fail_compilation/test21025.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=21025 +// REQUIRED_ARGS: -preview=dip1021 + +/* +TEST_OUTPUT: +--- +fail_compilation/test21025.d(15): Error: variable `r` cannot be read at compile time +fail_compilation/test21025.d(15): called from here: `binaryFun(r, r)` +fail_compilation/test21025.d(24): Error: none of the overloads of template `test21025.uniq` are callable using argument types `!()(void[])` +fail_compilation/test21025.d(14): Candidate is: `uniq()(int[] r)` +--- +*/ + +void uniq()(int[] r) +if (binaryFun(r, r)) {} + +bool binaryFun(T, U)(T, U) +{ + return true; +} + +void generateStatements() +{ + uniq([]); +} diff --git a/tests/dmd/fail_compilation/test23279.d b/tests/dmd/fail_compilation/test23279.d new file mode 100644 index 00000000000..43f2d44a071 --- /dev/null +++ b/tests/dmd/fail_compilation/test23279.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=23279 + +/* +TEST_OUTPUT: +--- +fail_compilation/test23279.d(13): Error: undefined identifier `Sth` +--- +*/ + +class Tester +{ + enum a = __traits(hasMember, Tester, "setIt"); + void setIt(Sth sth){} +} diff --git a/tests/dmd/fail_compilation/test23715.i b/tests/dmd/fail_compilation/test23715.i new file mode 100644 index 00000000000..5a1a8047ea3 --- /dev/null +++ b/tests/dmd/fail_compilation/test23715.i @@ -0,0 +1,12 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test23715.i(11): Error: `_Thread_local` in block scope must be accompanied with `static` or `extern` +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=23715 + +void test2() +{ + _Thread_local int tli; +} diff --git a/tests/dmd/fail_compilation/test23789.c b/tests/dmd/fail_compilation/test23789.c new file mode 100644 index 00000000000..44edc0d98ba --- /dev/null +++ b/tests/dmd/fail_compilation/test23789.c @@ -0,0 +1,14 @@ +/* TEST_OUTPUT: +--- +fail_compilation/test23789.c(101): Error: __decspec(align(3)) must be an integer positive power of 2 and be <= 8,192 +fail_compilation/test23789.c(103): Error: alignment value expected, not `"a"` +--- + */ + +// https://issues.dlang.org/show_bug.cgi?id=23789 + +#line 100 + +struct __declspec(align(3)) S { int a; }; + +struct __declspec(align("a")) T { int a; }; diff --git a/tests/dmd/fail_compilation/test23873.d b/tests/dmd/fail_compilation/test23873.d new file mode 100644 index 00000000000..bb6a71dcc24 --- /dev/null +++ b/tests/dmd/fail_compilation/test23873.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=23873 + +/* +TEST_OUTPUT: +--- +fail_compilation/imports/import23873.d(1): Error: (expression) expected following `static if` +fail_compilation/imports/import23873.d(1): Error: declaration expected following attribute, not `;` +fail_compilation/imports/import23873.d(3): Error: no identifier for declarator `x` +--- +*/ +struct Foo +{ + import imports.import23873; +} diff --git a/tests/dmd/fail_compilation/test23882.d b/tests/dmd/fail_compilation/test23882.d new file mode 100644 index 00000000000..f6b57c4ea04 --- /dev/null +++ b/tests/dmd/fail_compilation/test23882.d @@ -0,0 +1,37 @@ +// https://issues.dlang.org/show_bug.cgi?id=23882 + +/* +TEST_OUTPUT: +--- +fail_compilation/test23882.d(26): Error: `typeof((*YC).S).init` is used as a type +--- +*/ + +struct G(H) +{ + Tuple!(R) S; +} + +struct BB(H) +{ + H* YC; + alias YC this; +} + +struct R +{ + BB!(G!float) CB; + alias CB this; + + this(typeof(CB.S).init); +} + +struct Tuple(Specs) +{ + Specs expand; + + this(Specs values) + { + expand = values; + } +} diff --git a/tests/dmd/fail_compilation/test23905.d b/tests/dmd/fail_compilation/test23905.d new file mode 100644 index 00000000000..5b30fa855e2 --- /dev/null +++ b/tests/dmd/fail_compilation/test23905.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=23905 + +/* +TEST_OUTPUT: +--- +fail_compilation/test23905.d(24): Error: enum `test23905.Foo` is opaque and has no default initializer +--- +*/ + +struct SumType(T) +{ + T storage; + + bool opEquals(Rhs)(Rhs rhs) + if (is(typeof(Rhs.init))) + { + } + +} + +enum Foo; + +void main(){ + SumType!Foo data = Foo.init; +} diff --git a/tests/dmd/fail_compilation/test23982.d b/tests/dmd/fail_compilation/test23982.d new file mode 100644 index 00000000000..f8eee238ed8 --- /dev/null +++ b/tests/dmd/fail_compilation/test23982.d @@ -0,0 +1,36 @@ +/* +REQUIRED_ARGS: -preview=dip1000 +TEST_OUTPUT: +--- +fail_compilation/test23982.d(35): Error: scope variable `a` assigned to non-scope parameter `a` calling `foo2` +fail_compilation/test23982.d(26): which is not `scope` because of `b = a` +--- +*/ +// https://issues.dlang.org/show_bug.cgi?id=23982 +// Issue 23982 - segfault when printing scope inference failure +@safe: + +struct B() +{ + this(int* a) + { + this.a = a; + } + int* a; +} + +class C() +{ + int* foo2(int* a) + { + auto b = B!()(a); + return b.a; + } +} + +void main() +{ + scope int* a; + C!() c; + c.foo2(a); +} diff --git a/tests/dmd/fail_compilation/testInference.d b/tests/dmd/fail_compilation/testInference.d index c0d5a05d05c..145fc9e8b9d 100644 --- a/tests/dmd/fail_compilation/testInference.d +++ b/tests/dmd/fail_compilation/testInference.d @@ -138,8 +138,13 @@ immutable(void)* g10063(inout int* p) pure TEST_OUTPUT: --- fail_compilation/testInference.d(154): Error: `pure` function `testInference.bar14049` cannot call impure function `testInference.foo14049!int.foo14049` +fail_compilation/testInference.d(149): which calls `testInference.foo14049!int.foo14049.__lambda2` +fail_compilation/testInference.d(148): which calls `testInference.impure14049` +fail_compilation/testInference.d(143): which wasn't inferred `pure` because of: +fail_compilation/testInference.d(143): `pure` function `testInference.impure14049` cannot access mutable static data `i` --- */ +#line 143 auto impure14049() { static int i = 1; return i; } void foo14049(T)(T val) @@ -170,8 +175,10 @@ int* f14160() pure TEST_OUTPUT: --- fail_compilation/testInference.d(180): Error: `pure` function `testInference.test12422` cannot call impure function `testInference.test12422.bar12422!().bar12422` +fail_compilation/testInference.d(179): which calls `testInference.foo12422` --- */ +#line 175 int g12422; void foo12422() { ++g12422; } void test12422() pure @@ -184,9 +191,15 @@ void test12422() pure TEST_OUTPUT: --- fail_compilation/testInference.d(198): Error: `pure` function `testInference.test13729a` cannot call impure function `testInference.test13729a.foo` +fail_compilation/testInference.d(196): which wasn't inferred `pure` because of: +fail_compilation/testInference.d(196): `pure` function `testInference.test13729a.foo` cannot access mutable static data `g13729` fail_compilation/testInference.d(206): Error: `pure` function `testInference.test13729b` cannot call impure function `testInference.test13729b.foo!().foo` +fail_compilation/testInference.d(204): which wasn't inferred `pure` because of: +fail_compilation/testInference.d(204): `pure` function `testInference.test13729b.foo!().foo` cannot access mutable static data `g13729` --- */ + +#line 190 int g13729; void test13729a() pure @@ -229,8 +242,10 @@ void test17086_call () TEST_OUTPUT: --- fail_compilation/testInference.d(238): Error: `pure` function `testInference.test20047_pure_function` cannot call impure function `testInference.test20047_pure_function.bug` +fail_compilation/testInference.d(237): which calls `testInference.test20047_impure_function` --- */ +#line 234 void test20047_impure_function() {} void test20047_pure_function() pure { diff --git a/tests/dmd/fail_compilation/testnothrow.c b/tests/dmd/fail_compilation/testnothrow.c new file mode 100644 index 00000000000..538e2c174bd --- /dev/null +++ b/tests/dmd/fail_compilation/testnothrow.c @@ -0,0 +1,31 @@ +/* TEST_OUTPUT: +--- +fail_compilation/testnothrow.c(105): Error: function `testnothrow.throwing` is not `nothrow` +fail_compilation/testnothrow.c(104): Error: function `testnothrow.mul` may throw but is marked as `nothrow` +fail_compilation/testnothrow.c(111): Error: function `testnothrow.throwing` is not `nothrow` +fail_compilation/testnothrow.c(110): Error: function `testnothrow.add` may throw but is marked as `nothrow` +--- +*/ + +// https://issues.dlang.org/show_bug?id=21938 + +#line 100 + +void throwing() { } + +__attribute__((nothrow)) int mul(int x) +{ + throwing(); + return x * x; +} + +__declspec(nothrow) int add(int x) +{ + throwing(); + return x + x; +} + +int doSquare(int x) +{ + return mul(x) + add(x); +} diff --git a/tests/dmd/fail_compilation/testrvaluecpctor.d b/tests/dmd/fail_compilation/testrvaluecpctor.d index 96511f511e4..50cebabfda4 100644 --- a/tests/dmd/fail_compilation/testrvaluecpctor.d +++ b/tests/dmd/fail_compilation/testrvaluecpctor.d @@ -6,7 +6,7 @@ TEST_OUTPUT: fail_compilation/testrvaluecpctor.d(16): Error: cannot define both an rvalue constructor and a copy constructor for `struct Foo` fail_compilation/testrvaluecpctor.d(24): Template instance `testrvaluecpctor.Foo!int.Foo.__ctor!(immutable(Foo!int), immutable(Foo!int))` creates an rvalue constructor for `struct Foo` fail_compilation/testrvaluecpctor.d(24): Error: none of the overloads of `__ctor` are callable using a `immutable` object -fail_compilation/testrvaluecpctor.d(18): Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref Foo!int rhs)` +fail_compilation/testrvaluecpctor.d(18): Candidates are: `testrvaluecpctor.Foo!int.Foo.this(ref scope Foo!int rhs)` fail_compilation/testrvaluecpctor.d(16): `__ctor(Rhs, this This)(scope Rhs rhs)` --- */ diff --git a/tests/dmd/fail_compilation/var_func_attr.d b/tests/dmd/fail_compilation/var_func_attr.d new file mode 100644 index 00000000000..8609d29b58c --- /dev/null +++ b/tests/dmd/fail_compilation/var_func_attr.d @@ -0,0 +1,35 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/var_func_attr.d(19): Error: cannot implicitly convert expression `__lambda8` of type `void function() nothrow @nogc @safe` to `void function() pure` +--- +*/ + +// Test the effect of function attributes on variables +// See: +// https://issues.dlang.org/show_bug.cgi?id=7432 +// https://github.com/dlang/dmd/pull/14199 +// Usually it's a no-op, but the attribute can apply to the function/delegate type of the variable +// The current behavior is weird, so this is a test of the current behavior, not necessarily the desired behavior + +// No-op +pure int x; + +// Applies to function type (existing code in dmd and Phobos relies on this) +pure void function() pf = () { + static int g; + g++; +}; + +// Function attributes currently don't apply to inferred types (somewhat surprisingly) +nothrow nf = () { + throw new Exception(""); +}; + +// Neither do they apply to indirections +alias F = void function(); + +pure F pf2 = () { + static int g; + g++; +}; diff --git a/tests/dmd/runnable/betterc.d b/tests/dmd/runnable/betterc.d index d84bf3e3a92..05f0b950bca 100644 --- a/tests/dmd/runnable/betterc.d +++ b/tests/dmd/runnable/betterc.d @@ -48,6 +48,7 @@ extern (C) void main() test18472(); testRuntimeLowerings(); test18457(); + test20737(); } /*******************************************/ @@ -205,3 +206,13 @@ void test18457() } assert(dtor == 1); } + +/**********************************************/ +// https://issues.dlang.org/show_bug.cgi?id=20737 +int tlsVar; + +int test20737() +{ + tlsVar = 123; + return 0; +} diff --git a/tests/dmd/runnable/class_destructors.d b/tests/dmd/runnable/class_destructors.d index b01c4d9e54e..ce126bfc85d 100644 --- a/tests/dmd/runnable/class_destructors.d +++ b/tests/dmd/runnable/class_destructors.d @@ -154,7 +154,13 @@ void testDeleteWithoutCpp() class ThrowingChildD : ChildD { - static immutable ex = new Exception("STOP"); + static Exception ex; + + static this() + { + ex = new Exception("STOP"); + } + ~this() { throw ex; diff --git a/tests/dmd/runnable/cstuff3.i b/tests/dmd/runnable/cstuff3.i index 04a8a95b54a..fd1beb1ae6c 100644 --- a/tests/dmd/runnable/cstuff3.i +++ b/tests/dmd/runnable/cstuff3.i @@ -1,3 +1,5 @@ +// REQUIRED_ARGS: -defaultlib= + # 1 "runnable/extra-files/cstuff3.c" # 1 "" # 1 "" diff --git a/tests/dmd/runnable/debug_info.d b/tests/dmd/runnable/debug_info.d index 60b9c5f90f4..470b408c75e 100644 --- a/tests/dmd/runnable/debug_info.d +++ b/tests/dmd/runnable/debug_info.d @@ -36,8 +36,8 @@ else extern (C) { MachHeader* _dyld_get_image_header(uint image_index); - const(section)* getsectbynamefromheader(in mach_header* mhp, in char* segname, in char* sectname); - const(section_64)* getsectbynamefromheader_64(in mach_header_64* mhp, in char* segname, in char* sectname); + const(section)* getsectbynamefromheader(scope const mach_header* mhp, scope const char* segname, scope const char* sectname); + const(section_64)* getsectbynamefromheader_64(scope const mach_header_64* mhp, scope const char* segname, scope const char* sectname); } const(Section)* getSectByNameFromHeader(MachHeader* mhp, in char* segname, in char* sectname) diff --git a/tests/dmd/runnable/eh2.d b/tests/dmd/runnable/eh2.d index dc285a5dfab..2b469d2f803 100644 --- a/tests/dmd/runnable/eh2.d +++ b/tests/dmd/runnable/eh2.d @@ -24,7 +24,7 @@ class Abc : Throwable { printf("foo 1\n"); x |= 4; - throw this; + throw cast() this; printf("foo 2\n"); x |= 8; } diff --git a/tests/dmd/runnable/extra-files/hello-profile-postscript.sh b/tests/dmd/runnable/extra-files/hello-profile-postscript.sh index 47c18c08955..cb986e0a4fe 100755 --- a/tests/dmd/runnable/extra-files/hello-profile-postscript.sh +++ b/tests/dmd/runnable/extra-files/hello-profile-postscript.sh @@ -3,7 +3,9 @@ source tools/common_funcs.sh # strip out Dmain since it's symbol differs between windows and non-windows -grep -v Dmain ${OUTPUT_BASE}.d.trace.def > ${OUTPUT_BASE}.d.trace.def2 +# strip out _d_arraycatnTX and _d_arraysetlengthT since they are part of the +# lowering of the array concatenation operator +grep -v 'Dmain\|_d_arraycatnTX\|_d_arraysetlengthT' ${OUTPUT_BASE}.d.trace.def > ${OUTPUT_BASE}.d.trace.def2 diff -up --strip-trailing-cr ${EXTRA_FILES}/${TEST_NAME}.d.trace.def ${OUTPUT_BASE}.d.trace.def2 diff --git a/tests/dmd/runnable/imports/imp23014.i b/tests/dmd/runnable/imports/imp23014.i new file mode 100644 index 00000000000..ea51a781765 --- /dev/null +++ b/tests/dmd/runnable/imports/imp23014.i @@ -0,0 +1 @@ +static _Thread_local int tmp; diff --git a/tests/dmd/runnable/imports/imp23402a.c b/tests/dmd/runnable/imports/imp23402a.c new file mode 100644 index 00000000000..53c5fdf1799 --- /dev/null +++ b/tests/dmd/runnable/imports/imp23402a.c @@ -0,0 +1 @@ +#include diff --git a/tests/dmd/runnable/imports/imp23402b.c b/tests/dmd/runnable/imports/imp23402b.c new file mode 100644 index 00000000000..53c5fdf1799 --- /dev/null +++ b/tests/dmd/runnable/imports/imp23402b.c @@ -0,0 +1 @@ +#include diff --git a/tests/dmd/runnable/imports/link11069z.d b/tests/dmd/runnable/imports/link11069z.d index 5987cb4be7b..02301b9d1d2 100644 --- a/tests/dmd/runnable/imports/link11069z.d +++ b/tests/dmd/runnable/imports/link11069z.d @@ -1,7 +1,7 @@ module imports.link11069z; struct Matrix(T, uint _M) { - int opCmp()(auto ref in Matrix b) const + int opCmp()(const auto ref Matrix b) const { return 0; } diff --git a/tests/dmd/runnable/imports/link13415a.d b/tests/dmd/runnable/imports/link13415a.d index de3bbe2ac9b..077671b01c4 100644 --- a/tests/dmd/runnable/imports/link13415a.d +++ b/tests/dmd/runnable/imports/link13415a.d @@ -7,7 +7,7 @@ struct S(alias func) } } -extern(C) int printf(in char*, ...); +extern(C) int printf(const char*, ...); void f(int i = 77) { diff --git a/tests/dmd/runnable/imports/mainx23837.c b/tests/dmd/runnable/imports/mainx23837.c new file mode 100644 index 00000000000..0c446ab6ad1 --- /dev/null +++ b/tests/dmd/runnable/imports/mainx23837.c @@ -0,0 +1,10 @@ +// https://issues.dlang.org/show_bug?id=23837 + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; +}; diff --git a/tests/dmd/runnable/mangle.d b/tests/dmd/runnable/mangle.d index 7599e0e03c3..6e8f2b28987 100644 --- a/tests/dmd/runnable/mangle.d +++ b/tests/dmd/runnable/mangle.d @@ -571,12 +571,6 @@ void test12231() /***************************************************/ -int test2a(scope int a) { return a; } - -static assert(test2a.mangleof == "_D6mangle6test2aFiZi"); - -/***************************************************/ - class CC { int* p; diff --git a/tests/dmd/runnable/objc_call.d b/tests/dmd/runnable/objc_call.d index 22428a96746..481ee75d841 100644 --- a/tests/dmd/runnable/objc_call.d +++ b/tests/dmd/runnable/objc_call.d @@ -13,12 +13,12 @@ extern class Class extern (Objective-C) extern class NSObject { - NSObject initWithUTF8String(in char* str) @selector("initWithUTF8String:"); + NSObject initWithUTF8String(scope const char* str) @selector("initWithUTF8String:"); void release() @selector("release"); } extern (C) void NSLog(NSObject, ...); -extern (C) Class objc_lookUpClass(in char* name); +extern (C) Class objc_lookUpClass(scope const char* name); void main() { diff --git a/tests/dmd/runnable/objc_objc_msgSend.d b/tests/dmd/runnable/objc_objc_msgSend.d index 587ef79efbe..ef8e5b09dbe 100644 --- a/tests/dmd/runnable/objc_objc_msgSend.d +++ b/tests/dmd/runnable/objc_objc_msgSend.d @@ -4,7 +4,7 @@ import core.attribute : selector; -extern (C) Class objc_lookUpClass(in char* name); +extern (C) Class objc_lookUpClass(scope const char* name); struct Struct { diff --git a/tests/dmd/runnable/objc_protocol_sections.d b/tests/dmd/runnable/objc_protocol_sections.d index 97a655935a4..1382bf2569f 100644 --- a/tests/dmd/runnable/objc_protocol_sections.d +++ b/tests/dmd/runnable/objc_protocol_sections.d @@ -32,8 +32,8 @@ struct objc_method_description } } -SEL sel_registerName(in char* str); -Protocol* objc_getProtocol(in char* name); +SEL sel_registerName(scope const char* str); +Protocol* objc_getProtocol(scope const char* name); objc_method_description protocol_getMethodDescription( Protocol* proto, SEL aSel, bool isRequiredMethod, bool isInstanceMethod ); diff --git a/tests/dmd/runnable/profilegc_stdout.d b/tests/dmd/runnable/profilegc_stdout.d new file mode 100644 index 00000000000..4cf94ed725f --- /dev/null +++ b/tests/dmd/runnable/profilegc_stdout.d @@ -0,0 +1,19 @@ +/* +REQUIRED_ARGS: -profile=gc +DISABLED: LDC // -profile=gc not supported +RUN_OUTPUT: +--- +bytes allocated, allocations, type, function, file:line + 96 1 ubyte[] D main runnable/profilegc_stdout.d:17 +--- +*/ + +import core.runtime; + +void main() +{ + // test that stdout output works + profilegc_setlogfilename(""); + + ubyte[] arr = new ubyte[64]; +} diff --git a/tests/dmd/runnable/test22070_2.c b/tests/dmd/runnable/test22070_2.c index 7263202c5a3..f26c70cc6d6 100644 --- a/tests/dmd/runnable/test22070_2.c +++ b/tests/dmd/runnable/test22070_2.c @@ -51,5 +51,23 @@ int main() return 1; if (test4() != '5') return 1; - return 0; + return test23055(); +} + +// https://issues.dlang.org/show_bug?id=23055 + +int *px = (int[1]){0}; + +int fn() +{ + int *p = (int[1]){0}; + *p = 7; + return *p; +} + +_Static_assert(fn() == 7, ""); + +int test23055() +{ + return (fn() != 7); } diff --git a/tests/dmd/runnable/test23014.i b/tests/dmd/runnable/test23014.i new file mode 100644 index 00000000000..a890f0b42f3 --- /dev/null +++ b/tests/dmd/runnable/test23014.i @@ -0,0 +1,7 @@ +/* EXTRA_SOURCES: imports/imp23014.i + * REQUIRED_ARGS: -defaultlib= + */ + +static _Thread_local int tmp; + +int main() { return tmp; } diff --git a/tests/dmd/runnable/test23402.d b/tests/dmd/runnable/test23402.d new file mode 100644 index 00000000000..22fd21a7170 --- /dev/null +++ b/tests/dmd/runnable/test23402.d @@ -0,0 +1,10 @@ +// https://issues.dlang.org/show_bug.cgi?id=23402 + +import imports.imp23402a; +import imports.imp23402b; + +int main() +{ + printf("hello world\n"); + return 0; +} diff --git a/tests/dmd/runnable/test23786.c b/tests/dmd/runnable/test23786.c new file mode 100644 index 00000000000..3cf62063232 --- /dev/null +++ b/tests/dmd/runnable/test23786.c @@ -0,0 +1,66 @@ +// https://issues.dlang.org/show_bug.cgi?id=23768 + +typedef struct { + union { + struct { + int o; + } f; + }; +} T; + +void f(void) { + T data = (T) { + {.f = {.o = 0}} + }; +} + +/***************/ + +typedef struct { + union { + struct { + struct { double o; } f; + }; + }; +} S; + +_Static_assert(sizeof(S) == 8, "1"); + +void test23768() +{ + S data = (S) { + {{.f = {.o = 3}}} + }; + __check(data.f.o == 3); + S s; + s.f.o = 4; + __check(s.f.o == 4); +} + +/**************************/ +// https://issues.dlang.org/show_bug.cgi?id=24026 + +struct A +{ + int type; +}; + +struct E +{ + struct A action; +}; + +void test24026() +{ + struct E entry = {{ .type = 1 }}; + __check(entry.action.type == 1); +} + +/**************************/ + +int main() +{ + test23768(); + test24026(); + return 0; +} diff --git a/tests/dmd/runnable/test23837.d b/tests/dmd/runnable/test23837.d new file mode 100644 index 00000000000..c0c86683ee7 --- /dev/null +++ b/tests/dmd/runnable/test23837.d @@ -0,0 +1,14 @@ +// https://issues.dlang.org/show_bug.cgi?id=23837 + +import imports.mainx23837; + +struct TexturePacker +{ + stbrp_context _context; +} + +int main() +{ + auto res = TexturePacker(); + return 0; +} diff --git a/tests/dmd/runnable/test23959.d b/tests/dmd/runnable/test23959.d new file mode 100644 index 00000000000..32883736d44 --- /dev/null +++ b/tests/dmd/runnable/test23959.d @@ -0,0 +1,30 @@ +// https://issues.dlang.org/show_bug.cgi?id=23959; + +struct ST() +{ + int i; + this(this) {} +} + +alias S = ST!(); + +void poison() +{ + static S g; + auto s = g; +} + +S[1] sa; + +void fun(S[] values...) +{ + sa[] = values; +} + +int main() +{ + fun(S(1)); + assert(sa[0].i); + + return 0; +} diff --git a/tests/dmd/runnable/test42.d b/tests/dmd/runnable/test42.d index 74188e9913f..7b281e17a9a 100644 --- a/tests/dmd/runnable/test42.d +++ b/tests/dmd/runnable/test42.d @@ -2105,7 +2105,7 @@ void test12725() struct Matrix12728(T, uint m, uint n = m, ubyte f = 0) { - void foo(uint r)(auto ref in Matrix12728!(T, n, r) b) + void foo(uint r)(const auto ref Matrix12728!(T, n, r) b) { } } diff --git a/tests/dmd/runnable/testpdb.d b/tests/dmd/runnable/testpdb.d index 578ae68f203..99b4d9c9980 100644 --- a/tests/dmd/runnable/testpdb.d +++ b/tests/dmd/runnable/testpdb.d @@ -10,7 +10,7 @@ import ldc.attributes; void main(string[] args) { // https://issues.dlang.org/show_bug.cgi?id=4014 - // -gf should drag in full definitions of Object, TickDuration and ClockType + // -gf should drag in full definitions of Object, Duration and ClockType version (LDC) { // `Object` has no explicit fields; DMD emits debuginfos about methods which LDC doesn't. @@ -21,7 +21,7 @@ void main(string[] args) { Object o = new Object; } - TickDuration duration; // struct + Duration duration; // struct ClockType ct; // enumerator version (CRuntime_Microsoft) @@ -50,8 +50,8 @@ void main(string[] args) objsym.Release(); } - IDiaSymbol ticksym = searchSymbol(globals, "core.time.TickDuration"); - testSymbolHasChildren(ticksym, "core.time.TickDuration"); + IDiaSymbol ticksym = searchSymbol(globals, "core.time.Duration"); + testSymbolHasChildren(ticksym, "core.time.Duration"); ticksym.Release(); IDiaSymbol ctsym = searchSymbol(globals, "core.time.ClockType"); diff --git a/tests/dmd/runnable/xtest46.d b/tests/dmd/runnable/xtest46.d index e57f52f6578..5017a767693 100644 --- a/tests/dmd/runnable/xtest46.d +++ b/tests/dmd/runnable/xtest46.d @@ -5016,18 +5016,18 @@ void test6763() { int n; - f6763(0); //With D2: Error: function main.f ((ref const const(int) _param_0)) is not callable using argument types (int) + f6763(0); //With D2: Error: function main.f ((ref const const(int) __param_0)) is not callable using argument types (int) c6763(0); r6763(n); static assert(__traits(compiles, r6763(0))); i6763(0); o6763(n); static assert(!__traits(compiles, o6763(0))); // https://issues.dlang.org/show_bug.cgi?id=6755 - static assert(typeof(f6763).stringof == "void(int _param_0)"); - static assert(typeof(c6763).stringof == "void(const(int) _param_0)"); - static assert(typeof(r6763).stringof == "void(ref int _param_0)"); - static assert(typeof(i6763).stringof == "void(in int _param_0)"); - static assert(typeof(o6763).stringof == "void(out int _param_0)"); + static assert(typeof(f6763).stringof == "void(int __param_0)"); + static assert(typeof(c6763).stringof == "void(const(int) __param_0)"); + static assert(typeof(r6763).stringof == "void(ref int __param_0)"); + static assert(typeof(i6763).stringof == "void(in int __param_0)"); + static assert(typeof(o6763).stringof == "void(out int __param_0)"); } /***************************************************/ @@ -5997,7 +5997,7 @@ void test7618(const int x = 1) { int func(ref int x) { return 1; } static assert(!__traits(compiles, func(x))); - // Error: function test.foo.func (ref int _param_0) is not callable using argument types (const(int)) + // Error: function test.foo.func (ref int __param_0) is not callable using argument types (const(int)) int delegate(ref int) dg = (ref int x) => 1; static assert(!__traits(compiles, dg(x))); @@ -6170,14 +6170,6 @@ static assert(!__traits(compiles, foo8220(typeof(0)))); // fail /***************************************************/ -void func8105(in ref int x) { } - -void test8105() -{ -} - -/***************************************************/ - template ParameterTypeTuple159(alias foo) { static if (is(typeof(foo) P == __parameters)) @@ -8300,7 +8292,6 @@ int main() test12503(); test8004(); test8064(); - test8105(); test159(); test12824(); test8283(); diff --git a/tests/dmd/unit/deinitialization.d b/tests/dmd/unit/deinitialization.d index b4cb73a945f..d3458802af9 100644 --- a/tests/dmd/unit/deinitialization.d +++ b/tests/dmd/unit/deinitialization.d @@ -33,7 +33,7 @@ unittest @("Type.deinitialize") unittest { - import dmd.mars : addDefaultVersionIdentifiers; + import dmd.target : addDefaultVersionIdentifiers; import dmd.mtype : Type; import dmd.globals : global; diff --git a/tests/dmd/unit/lexer/diagnostic_reporter.d b/tests/dmd/unit/lexer/diagnostic_reporter.d index e707e99bb76..00ac59af6e1 100644 --- a/tests/dmd/unit/lexer/diagnostic_reporter.d +++ b/tests/dmd/unit/lexer/diagnostic_reporter.d @@ -43,7 +43,7 @@ private void lexUntilEndOfFile(string code) if (!global.errorSink) global.errorSink = new ErrorSinkCompiler; - scope lexer = new Lexer("test", code.ptr, 0, code.length, 0, 0, global.errorSink); + scope lexer = new Lexer("test", code.ptr, 0, code.length, 0, 0, global.errorSink, null); lexer.nextToken; while (lexer.nextToken != TOK.endOfFile) {} diff --git a/tests/dmd/unit/lexer/lexer_dmdlib.d b/tests/dmd/unit/lexer/lexer_dmdlib.d index 4b53bafc9ac..ea7ebb4ab82 100644 --- a/tests/dmd/unit/lexer/lexer_dmdlib.d +++ b/tests/dmd/unit/lexer/lexer_dmdlib.d @@ -170,7 +170,7 @@ unittest TOK.rightCurly, ]; - Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr); + Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr, null); lexer.nextToken; TOK[] result; @@ -191,7 +191,7 @@ unittest TOK.comment, ]; - Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, true, new ErrorSinkStderr); + Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, true, new ErrorSinkStderr, null); lexer.nextToken; TOK[] result; @@ -217,7 +217,7 @@ unittest TOK.reserved, ]; - Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr); + Lexer lexer = new Lexer(null, code.ptr, 0, code.length, false, false, new ErrorSinkStderr, null); TOK[] result; @@ -266,7 +266,7 @@ unittest foreach (codeNum, code; codes) { auto fileName = text("file", codeNum, '\0'); - Lexer lexer = new Lexer(fileName.ptr, code.ptr, 0, code.length, false, false, new ErrorSinkCompiler); + Lexer lexer = new Lexer(fileName.ptr, code.ptr, 0, code.length, false, false, new ErrorSinkCompiler, null); // Generate the errors foreach(unused; lexer){} } diff --git a/tests/dmd/unit/lexer/location_offset.d b/tests/dmd/unit/lexer/location_offset.d index c52de839dbe..4a950e866f2 100644 --- a/tests/dmd/unit/lexer/location_offset.d +++ b/tests/dmd/unit/lexer/location_offset.d @@ -17,7 +17,7 @@ unittest { enum code = "token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -29,7 +29,7 @@ unittest { enum code = "ignored_token token"; - scope lexer = new Lexer("test.d", code.ptr, 13, code.length - 14, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 13, code.length - 14, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -41,7 +41,7 @@ unittest { enum code = "token1 token2 3"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -55,7 +55,7 @@ unittest { enum code = "token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -68,7 +68,7 @@ unittest { enum code = "/* comment */"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr, null); lexer.nextToken; @@ -81,7 +81,7 @@ unittest { enum code = "// comment"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr, null); lexer.nextToken; @@ -94,7 +94,7 @@ unittest { enum code = "/+ comment +/"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, true, new ErrorSinkStderr, null); lexer.nextToken; @@ -107,7 +107,7 @@ unittest { enum code = "/* comment */ token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -119,7 +119,7 @@ unittest { enum code = "// comment\ntoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -131,7 +131,7 @@ unittest { enum code = "/+ comment +/ token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -143,7 +143,7 @@ unittest { enum code = "line\ntoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -156,7 +156,7 @@ unittest { enum code = "line\r\ntoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -169,7 +169,7 @@ unittest { enum code = "line\rtoken"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -182,7 +182,7 @@ unittest { enum code = "'🍺'"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -194,7 +194,7 @@ unittest { enum code = `"🍺🍺"`; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; @@ -206,7 +206,7 @@ unittest { enum code = "'🍺' token"; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -219,7 +219,7 @@ unittest { enum code = `"🍺🍺" token`; - scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", code.ptr, 0, code.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; @@ -542,6 +542,7 @@ enum ignoreTokens __cdecl, __declspec, __stdcall, + __thread, __pragma, __int128, __attribute__, @@ -556,9 +557,9 @@ static foreach (tok; __traits(allMembers, TOK)) @(tests[tok].description) unittest { - const newCode = "first_token " ~ tests[tok].code; + const newCode = "first_token " ~ tests[tok].code ~ '\0'; - scope lexer = new Lexer("test.d", newCode.ptr, 0, newCode.length, 0, 0, new ErrorSinkStderr); + scope lexer = new Lexer("test.d", newCode.ptr, 0, newCode.length, 0, 0, new ErrorSinkStderr, null); lexer.nextToken; lexer.nextToken; diff --git a/tests/semantic/dcompute.d b/tests/semantic/dcompute.d index 44247e71f67..1bde310b170 100644 --- a/tests/semantic/dcompute.d +++ b/tests/semantic/dcompute.d @@ -29,7 +29,7 @@ void func() //CHECK: dcompute.d([[@LINE+1]]): Error: {{.*}} interfaces and classes not allowed in `@compute` code C cc; int[] quux; - //CHECK: dcompute.d([[@LINE+1]]): Error: can only call functions from other `@compute` modules in `@compute` code + //CHECK: dcompute.d([[@LINE+1]]): Error: setting `length` in `@compute` code not allowed quux.length = 1; //CHECK: dcompute.d([[@LINE+1]]): Error: can only call functions from other `@compute` modules in `@compute` code quux ~= 42;